Skip to content

Latest commit

 

History

137 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OnchainEvm

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

Installation

Requires no Rust toolchain on hosts with a matching precompiled artifact (macOS and Linux; see below). Windows, unmatched targets, and RUSTLER_PRECOMPILED_FORCE_BUILD_ALL=1 still compile from source and need Rust 1.95+.

def deps do
  [
    {:onchain, "~> 0.12"},
    {:onchain_evm, "~> 0.6"}
  ]
end

Matching hosts download a NIF from the GitHub Release for this version and verify it against checksum-Elixir.Onchain.EVM.exs / checksum-Elixir.Onchain.Solidity.exs. A checksum mismatch always fails the load. A missing checksum file fails the load for a Hex-installed package — files: ships those files, so absence is a packaging bug — and source-builds in this repo's own checkout, which is what keeps mix ci green before the first v0.6.0 artifact release.

To force a source build on any host:

RUSTLER_PRECOMPILED_FORCE_BUILD_ALL=1 mix compile

That env var is rustler_precompiled's own ("1" or "true"). The package-specific equivalent is ONCHAIN_EVM_BUILD=1, or config :rustler_precompiled, :force_build, onchain_evm: true. Source builds need {:rustler, "~> 0.38"} in the consumer's deps — it is an optional dependency of this package.

Windows is not a shipped target. Those consumers source-build.

Precompiled release runbook

Order matters: the checksum step downloads what was uploaded, so artifacts must be live first. There is no CI. Cross-compilation is a local cargo-zigbuild ritual on an Apple Silicon Mac.

  1. Cross-build every shipped target for both crates: scripts/build-precompiled.sh
  2. Tag and push: git tag vX.Y.Z && git push origin vX.Y.Z
  3. Publish the tarballs: gh release create vX.Y.Z artifacts/precompiled/vX.Y.Z/*.tar.gz (or gh release upload onto an existing tag)
  4. Fetch them back and write checksums: mix rustler_precompiled.download Onchain.EVM --all --print mix rustler_precompiled.download Onchain.Solidity --all --print
  5. Commit checksum-Elixir.Onchain.EVM.exs and checksum-Elixir.Onchain.Solidity.exs (both are in the Hex files: glob; omitting them drops checksum verification for every consumer)
  6. mix hex.publish

Shipped targets, all reachable with cargo-zigbuild from one Mac: aarch64-apple-darwin, x86_64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl. Linux GNU builds pin glibc 2.28 via a Zig target suffix (x86_64-unknown-linux-gnu.2.28); the script asserts the artifact's GLIBC requirement rather than trusting the suffix. Adding riscv64 / armv7 is a one-line append to TARGETS in the script — they are not shipped.

The release is a manual local ritual with no build provenance or attestation, and it is reproducible only on a machine with the Zig toolchain installed. That is the accepted cost of not running CI.

Modules

Module Purpose
Onchain.EVM Local EVM execution — fork mainnet state, simulate calls/transactions/batches
Onchain.Solidity Alloy-powered Solidity ABI parser (JSON ABI, .sol source, import resolution)
Onchain.Trace Debug/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:

Option Meaning
:rpc_url RPC endpoint to fork from (required; empty/non-HTTP(S)/hostless rejected)
:block Block to fork at — integer, "0x…" hex, or a tag ("latest", "finalized", "safe", "pending", "earliest"). Also selects the EVM revision that was active at that block on Ethereum mainnet; other chain ids are rejected
:from Sender address (0x hex or 20-byte binary)
:timeout_ms Per-RPC-request timeout (positive integer; default 30s, 5s connect). Surfaces as {:error, {:timeout, msg}}
:value 0x-prefixed U256 hex quantity
:gas_limit Positive integer ≤ u64
:state_overrides %{address => %{balance/nonce/code/storage}} applied on top of the forked account

Malformed options fail at the Elixir boundary with a tagged {:error, {atom, term}}; they never reach the NIF.

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. Each generated module also emits a nested Multicall with typed call builders and result decoders for Onchain.Multicall.aggregate3/2.

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

About

EVM simulation, Solidity parsing, debug/trace APIs, and contract codegen for Elixir via Rust NIFs

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages