OnchainEvm

Rust NIFs for Elixir: local EVM simulation via revm, Solidity ABI parsing via Alloy, debug/trace APIs, and contract codegen. Built on onchain.

Installation

Requires a Rust toolchain for NIF compilation.

def deps do
[
{:onchain, "~> 0.10"},
{:onchain_evm, "~> 0.3"}
]
end

Modules

ModulePurpose
Onchain.EVMLocal EVM execution — fork mainnet state, simulate calls/transactions/batches
Onchain.SolidityAlloy-powered Solidity ABI parser (JSON ABI, .sol source, import resolution)
Onchain.TraceDebug/trace APIs — trace_transaction, trace_call, storage_at
Onchain.Contract.Generator.sol file → typed Elixir module at compile time

EVM Simulation

EVM simulation forks state from an RPC endpoint passed per-call via :rpc_url:

# Simulate a read (eth_call semantics)
Onchain.EVM.simulate_call(usdc_address, calldata, rpc_url: "https://eth-mainnet.example.com")
# Full execution result (gas_used, success, return data)
Onchain.EVM.simulate_transaction(address, calldata, rpc_url: url)
# Batch many calls against one shared fork
Onchain.EVM.simulate_batch(calls, rpc_url: url)

Options:

OptionMeaning
:rpc_urlRPC endpoint to fork from (required; validated — empty/non-HTTP(S)/hostless rejected)
:blockBlock to fork at — integer, "0x…" hex, or a tag ("latest", "finalized", "safe", "pending", "earliest")
:timeout_msPer-RPC-request timeout (positive integer; default 30s, 5s connect). Surfaces as {:error, {:timeout, msg}}
:value, :gas_limit, :state_overridesValidated and threaded into the fork (invalid input fails fast, not silently dropped)

Every public function has a bang (!) variant that raises on error.

Contract Codegen

Generate a typed module from a .sol file or JSON ABI at compile time:

defmodule USDC do
use Onchain.Contract.Generator, abi_file: "priv/abis/erc20.json"
end
USDC.balance_of(contract, holder, rpc_url: url) # => {:ok, [balance]}

Generator inputs (in precedence order): :abi_json, :abi_file, :sol, :sol_file. Solidity sources support :remappings (Foundry-style) and :root_contract for import resolution.

Discovery

All modules use descripex for self-describing APIs:

OnchainEvm.describe() # Module overview
OnchainEvm.describe(:evm) # Function listings
OnchainEvm.describe(:evm, :simulate_call) # Full function details

Testing

mix test.json --quiet # Offline unit tests
mix test.json --quiet --include integration # + integration (requires an RPC node)

Integration tests read ETHEREUM_API_URL (or ETH_RPC_URL).

License

MIT