Helper to download and deploy diode_contract (https://github.com/diodechain/diode_contract) on a local Anvil chain for testing.
Requires Foundry (forge) on PATH for contract deployment. Set ANVIL_CONTRACT_REPO_PATH
to use an existing clone; otherwise the repo is cloned to a temporary directory.
One-shot test env (for downstream test_helper.exs)
# In your app's test/test_helper.exs:
case DiodeClient.Anvil.Helper.ensure_test_env(wallet: "test_anvil", deploy_contracts: false) do
:ok ->
:ok
{:error, :anvil_not_reachable} ->
ExUnit.configure(exclude: [anvil: true])
{:error, _reason} ->
ExUnit.configure(exclude: [anvil: true])
end
ExUnit.start()Example (manual steps)
# With Anvil running (e.g. anvil) and diode_contract deployed:
{:ok, list, addresses} = DiodeClient.Anvil.Helper.deploy_contracts()
DiodeClient.Contracts.Factory.set_anvil_contracts(list)
Summary
Functions
Returns whether the Anvil RPC endpoint is reachable (e.g. anvil is running).
Runs forge build in the given repo path. Returns :ok or {:error, reason}.
Deploys contracts from diode_contract to the Anvil RPC URL (default from ANVIL_RPC_URL).
Ensures the diode_contract repo is available: returns path from ANVIL_CONTRACT_REPO_PATH
or clones to a temporary directory. Returns {:ok, path} or {:error, reason}.
One-shot setup for using the Anvil shell in tests.
Returns the path to the diode_contract repo. Uses ANVIL_CONTRACT_REPO_PATH if set; otherwise returns nil (caller must clone or set path).
Spawns Anvil in the background and waits until the RPC endpoint is reachable.
Stops the Anvil process (if running) and clears the stored port.
Functions
Returns whether the Anvil RPC endpoint is reachable (e.g. anvil is running).
Use in test_helper.exs to exclude :anvil tests when Anvil is not running:
if not DiodeClient.Anvil.Helper.anvil_reachable?() do
ExUnit.configure(exclude: [anvil: true])
endOptional rpc_url defaults to ANVIL_RPC_URL or http://127.0.0.1:8545.
Runs forge build in the given repo path. Returns :ok or {:error, reason}.
Deploys contracts from diode_contract to the Anvil RPC URL (default from ANVIL_RPC_URL).
Option A: If a deploy script exists (e.g. script/DeployAnvil.s.sol), run it and parse
broadcast output. Option B: Read out/ artifacts and send deployment transactions.
Returns {:ok, %List{}, addresses} where addresses is a map of contract name => deployed
address (<<_::160>>). Calls Factory.set_anvil_contracts(list) so
Factory.contracts(DiodeClient.Shell.Anvil) works. Returns {:error, reason} on failure.
Ensures the diode_contract repo is available: returns path from ANVIL_CONTRACT_REPO_PATH
or clones to a temporary directory. Returns {:ok, path} or {:error, reason}.
One-shot setup for using the Anvil shell in tests.
Call from your library's test/test_helper.exs before ExUnit.start().
Optionally sets a wallet, checks Anvil reachability, and deploys diode_contract
so DiodeClient.Contracts.Factory.contracts(DiodeClient.Shell.Anvil) works.
Options
:wallet– Path or callback for setting the wallet. If set, ensures a wallet is set (e.g."test_anvil"creates/uses that file). Skip if your tests do not send transactions. Default: no change.:deploy_contracts– Iftrue, ensures diode_contract repo is available, runsforge build, deploys contracts to Anvil, and callsFactory.set_anvil_contracts/1. Requires Anvil reachable and Foundry on PATH. Default:false.:rpc_url– Anvil RPC URL. Default:ANVIL_RPC_URLorhttp://127.0.0.1:8545.
Returns
:ok– Wallet (if requested) and optional deployment succeeded.{:error, :anvil_not_reachable}– RPC endpoint not reachable; exclude:anviltests or start Anvil.{:error, reason}– Deployment or wallet error (e.g.{:clone_failed, _}).
Example (minimal: Anvil only, no contracts)
DiodeClient.Anvil.Helper.ensure_test_env(wallet: "test_anvil")
ExUnit.start()Example (Anvil + contracts, skip anvil tests when unreachable)
case DiodeClient.Anvil.Helper.ensure_test_env(wallet: "test_anvil", deploy_contracts: true) do
:ok -> :ok
{:error, :anvil_not_reachable} -> ExUnit.configure(exclude: [anvil: true])
{:error, _} -> ExUnit.configure(exclude: [anvil: true])
end
ExUnit.start()
Returns the path to the diode_contract repo. Uses ANVIL_CONTRACT_REPO_PATH if set; otherwise returns nil (caller must clone or set path).
Spawns Anvil in the background and waits until the RPC endpoint is reachable.
Use in test_helper.exs so mix test works without manually starting Anvil:
case DiodeClient.Anvil.Helper.start_anvil() do
{:ok, _port} -> :ok
{:error, _} -> ExUnit.configure(exclude: [anvil: true])
end
DiodeClient.Anvil.Helper.ensure_test_env(wallet: "test_anvil")
ExUnit.start()The process is kept alive for the test run (port stored in process); when the test runner exits, Anvil is terminated.
Options
:rpc_url– URL to poll (default:ANVIL_RPC_URLorhttp://127.0.0.1:8545).:timeout– Max ms to wait for Anvil to become reachable (default:15_000).:port– Port for Anvil (default: parsed from:rpc_urlor8545).:args– Extra args foranvil(default:[]).
Returns
{:ok, port}– Anvil is running and reachable.{:error, :executable_not_found}–anvilnot on PATH (install Foundry).{:error, :timeout}– Anvil did not become reachable within:timeout.{:error, {:spawn_failed, reason}}– Failed to start the process.
Stops the Anvil process (if running) and clears the stored port.
Returns :ok. Safe to call when Anvil was not started by this helper.