Repository navigation
Conversation
Port the sfm-colab/anvil-reth prototype onto upstream reth v2.7.0 as a workspace crate. The node runs on a temporary MDBX datadir and serves the anvil_* namespace for impersonation, mining control, time control, and block environment overrides. State writes, dumpState/loadState, forking, and snapshot/revert are not ported yet. foundry-core is pinned to the commit that drops the time crate pin, because reth-node-core needs vergen and therefore time 0.3.51 or newer.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
…lock executor Add anvil_setBalance, addBalance, setNonce, setCode, and setStorageAt. A write lands in a read overlay served by a provider wrapper on latest and pending state, so RPC reads and pool validation see it at once. The block executor applies every queued write at the start of the next block, so the write enters the block's state changes and state root, and the overlay entry drops once that block is canonical. The provider wrapper needs a launcher that installs it, so the engine launcher is mirrored with only the provider construction changed.
Add anvil_snapshot, anvil_revert, anvil_rollback, and evm_mine. The node now drives block production itself instead of through reth's local miner: mining requests return the mined header, so anvil_mine no longer polls the head, and the engine never finalizes blocks, so a revert can rewind to any block. A rewind drops the in-memory blocks above the target, removes the persisted blocks above it, and emits a reorg notification so the RPC caches and the pool follow; the pool rejects the dropped transactions so the reorg does not mine them again. The safe and finalized tags follow anvil: one and two epochs behind the head.
Add the `reth-anvil` binary with anvil's flags, `NodeConfig` with the `with_*` builders and `spawn`/`try_spawn`, and an in-process `EthApi` that calls the merged RPC module without a socket. The dev accounts come from anvil's mnemonic generator and sign through the node's `eth_sendTransaction`. Add anvil_reorg, anvil_reset, anvil_setMinGasPrice, anvil_setLoggingEnabled, anvil_getLastBlockWallTime, the blob getters, the ERC20 deal and allowance helpers, the hardhat aliases, and the genesis timestamp and chain id options. Pin reth to the rev string the tempo crates use so Cargo resolves one copy. Impersonated transactions record their sender in reth's sender recovery cache so receipt lookups find them once paradigmxyz/reth#27757 lands.
figtracer
marked this pull request as ready for review
October 6, 2026 19:25
figtracer
requested review from
0xrusowsky,
DaniPopes,
grandizzy,
mablr,
mattsse and
stevencartavia
as code owners
October 6, 2026 19:25
figtracer
marked this pull request as draft
October 6, 2026 19:26
Add `--fork-url` and the other fork flags. The local database starts at the fork block: its header is the remote header, so the next local block links to the remote chain, and the dev accounts keep their remote nonce and code. Blocks, receipts, and transactions below the fork block come from the remote endpoint on demand, with remote transactions in their own transaction number space so `eth_getLogs` spans both sides. State reads check whether the local chain wrote the account or slot, through reth's history index and the in-memory blocks, and fall back to foundry's cached fork database at the fork block otherwise. The engine validates blocks against the local database directly, so the remote reads the block builder made are copied into the local database before the engine executes the block, with the fork block as their history entry. Reads block on the remote endpoint with `block_in_place` on a multi-threaded runtime and with a plain wait elsewhere, and the fork backends get their own providers so a blocked runtime cannot stall them. `anvil_metadata` and `anvil_nodeInfo` report the fork, `anvil_setRpcUrl` replaces the endpoint, and `anvil_reset` resets to the fork block.
Add anvil_dumpState and anvil_loadState, and the --state, --dump-state, --load-state, and --state-interval flags. A dump walks the persisted plain state, applies the in-memory blocks and the pending anvil state writes on top, and records the head block environment and number. A node started from a dump puts the accounts in genesis and starts at the dumped block number with its timestamp, gas limit, and base fee, so the chain continues where the dump left off. Loading at runtime applies the accounts as anvil state writes, with nonces taking the higher value. Also add a regression test for the EIP-8037 gas estimate from #17428: reth executes a transfer at 21,000 gas before accepting that estimate, so a transfer that creates an account on Amsterdam reports the state gas.
… methods Add anvil_impersonateSignature, eth_sendUnsignedTransaction, and personal_sign. A signature override attributes every raw transaction that carries the signature to the chosen sender: the pool validator rebuilds the transaction with that sender so the block builder executes it from the right account, and execution and lookups use the recorded sender. eth_sendUnsignedTransaction impersonates the `from` account for one send. Serve every reth RPC namespace over HTTP and WebSocket, as anvil does, which also provides erigon_getHeaderByNumber through the otterscan module.
Print the RPC methods served and the mined blocks and transactions to stdout as anvil does, through an RPC middleware and a canonical state listener. `--silent` turns the output off at startup and anvil_setLoggingEnabled toggles it at runtime. Serve the IPC endpoint when `--ipc` is set, and add `--order fifo` through a pool ordering that ranks transactions by arrival, which anvil_nodeInfo reports. Add UPSTREAM.md: the list of copies and workarounds in this crate, the reth hook that would replace each, and the gaps that remain.
Introduce the `AnvilNetwork` trait: a network names its reth node type, its components and RPC add-ons with the anvil wrappers installed, and its payload attributes builder. The node launch, the provider, the launcher, the miner, the EVM wrapper, the pool validator, and the `anvil_*` namespace are now generic over the network's primitives, chain spec, payload, and RPC types, and the fork converts remote responses through a `ForkNetwork` implementation. Ethereum is the first network. `NodeConfig` carries the network selection with `with_networks` and the `test_tempo` and `test_monad` constructors.
Apply the code size limit, the memory limit, EIP-3607, the block gas limit check, and the EIP-7825 cap to every EVM environment, so block execution, the pool, and RPC calls agree with anvil. Pass the memory limit to reth's RPC call path, which sets its own. Pin the payload builder's gas limit to the configured one, so blocks no longer drift towards reth's default, and set it to `u64::MAX` when the block gas limit is disabled. Let the pool skip the balance check and the minimum priority fee, and report an unlimited balance when the check is off, so a transaction funded in the same block is mined. Replace `eth_sendTransaction` with a version that treats a missing `to` as a contract creation, which reth rejects. Use the anvil dev mnemonic by default.
Run Monad on reth's Ethereum node types with a `ConfigureEvm` built on `monad-revm`. The EVM factory looks up the senders and EIP-7702 authorities of the parent and grandparent blocks for every EVM it creates, and the block executor adds each transaction to the chain context before it runs, so reserve balances behave as on Monad. The environment carries Monad's code size, initcode size, and transaction gas limits, so the pool enforces them too. The pool rejects blob transactions and checks only the fee at the effective gas price, as Monad does. `anvil_nodeInfo` reports the network and the Monad hardfork, and the chain spec activates the Ethereum hardfork the Monad hardfork is based on.
The in-process `instance_id` kept the id the node started with, while `anvil_reset` rotates it. Workspace tests compare the two across a reset.
Run the RPC server in front of the reth node instead of inside it. Every method forwards to the module of the current node, so the node can be replaced while the endpoint, the open connections, and the in-process API keep working. `anvil_reset` to another fork endpoint or block, and the new `anvil_setChainId`, stop the node and launch one from the updated config; `setChainId` carries the state and the height over through a state dump. A failed launch falls back to the previous config. Set the chain id of the dev wallets with `with_chain_id`, so a node with a custom chain id signs transactions.
Add `eth_sendTransactionSync`, `eth_resend`, `eth_sendRawTransactionConditional`, `eth_requestAccounts`, and `eth_networkId`, the `hardhat_*` state dump aliases, and the `tenderly_*` balance aliases. Register `eth_config`, which reth adds only to its transport modules. A test checks that every method anvil serves is registered, except the Tempo helpers and the opcode gas traces. Add the network flags to the CLI with anvil's chain-id inference and hardfork normalization, and honour `--allow-origin`, `--no-cors`, and `--no-request-size-limit` in the RPC server.
Resolve the transaction on the fork endpoint, fork at the block before it, and replay the transactions up to and including it into the first local block before the node serves requests. The replayed block keeps the remote block's timestamp, fee recipient, gas limit, base fee, and randomness, and the pool takes the transactions in arrival order while it builds, so the block matches the remote prefix.
Keep the writes a block applied after it is canonical, so a replay of that block applies them again, and serve them in the state at the block they were written at, as anvil writes into the head state. Tracing a transaction that calls code set with `anvil_setCode`, and tools that fork at the parent block to replay a transaction, see the same state as with anvil. Default to the latest hardfork for a chain id without a schedule, as anvil does, instead of mainnet's schedule. Drop the in-process block lookup's numeric block parameter in favour of a block number or tag, and accept anvil's request type in `send_transaction`, so the workspace tests compile against this crate unchanged. `UPSTREAM.md` lists the differences that running cast's tests against this node showed.
Reth gives calls and estimates without a gas limit 50M gas. Anvil gives them the block gas limit, and contracts that check the gas they got see the difference.
Tempo runs on Tempo's node types, executor, and RPC, with the anvil executor wrapper, a pool built over the wrapped EVM config, and a sequential dev block builder. The genesis seeds Tempo's precompiles, fee tokens, dev balances, keychain keys, and fee token liquidity, as anvil does, and a fork keeps the remote Tempo state. The anvil namespace gains the Tempo methods: dealTIP20, setFeeToken, setValidatorFeeToken, and setFeeAmmLiquidity. eth_signRawTransaction and raw transactions with the sponsorship placeholder use the node's fee payer. Anvil's Tempo tests move to reth-anvil; 103 of 167 pass.
Call batches keep no create target when sent or simulated, balances and fork accounts skip Tempo's native balance placeholder, the pool validates against anvil's clock through a pool-only EVM config and sees anvil state writes before the next block, the next base fee follows the chain spec's rule, and eth_config lists Tempo's precompiles. Tempo-only requests fail on other networks, and Tempo fee token shortfalls report as anvil reports them. 136 of 156 Tempo tests pass.
Tempo now passes anvil's Tempo tests: the txpool views key nonce lanes as anvil does, calls and estimates run with the request's nonce, lane nonces resolve from storage, signing fills Tempo fees and lane nonces, sponsored simulations keep their signed gas, impersonated senders are known to block execution, the block builder follows gas limit overrides, and the base fee and coinbase follow anvil across resets. Six tests stay ignored for gaps in Tempo's crates, which UPSTREAM.md lists with the hook that would close each. docs/networks.md explains how a network team adds its network, with the Optimism and Base specifics.
The tests move from anvil, ignored until the Optimism and Base networks run, so the teams that add them start from anvil's behavior. The base feature gains anvil's activation admin option.
The anvil binary now builds from reth-anvil, and the old anvil crates are deleted. reth-anvil gains anvil's build features (jemalloc, mimalloc, tracy-allocator, asm-keccak, js-tracer, cli), runs Tempo by default, and the release feature lists include tempo. Optimism and Base do not run yet; their teams add them with crates/reth-anvil/docs/networks.md, from anvil's tests, which reth-anvil keeps as ignored specs. A fork probes anvil_nodeInfo for up to 2s, once per launch, since a busy reth-anvil endpoint answers more slowly than anvil did.
Revert messages decode the revert data with foundry's RevertDecoder, as anvil's do: Tempo's API named precompile errors by a selector that several precompiles share, and reth printed custom errors without their selector. CI names the cast package with its version, since Tempo's node brings another crate named cast.
The interval mining task requested a block every interval without waiting for it. When a block took longer than the interval to build, the requests queued in the miner, and a switch to manual mining still mined them. The task now waits for each block, so no request queues.
Tempo rejected a state dump with a block environment. A dump block now keeps its header as a FoundryHeader, so a Tempo header keeps its Tempo fields. A new ForkNetwork::dump_header hook converts it to the node's header type, and the block keeps its dumped hash. Tempo then loads a dump as Ethereum does: alone, or above a fork block. An Ethereum dump on Tempo shows Tempo headers with the Ethereum hashes, as anvil does. anvil_loadState with blocks now keeps the accounts the dump does not name, as anvil loads a dump over its state. This also vendors TempoEthApi as AnvilTempoEthApi. It builds pending blocks, reports native balances, and gives each simulated expiring-nonce call its own hash. This removes the eth_getBalance override and enables four Tempo tests that were ignored.
anvil_impersonateSignature did not apply to EIP-7702 authorities, on any network, or to ecrecover on Tempo. Two tests were ignored for this. alloy-evm recovers authorities when it builds the transaction environment, through alloy's crypto functions. When a node first sets a signature override, it now installs an alloy CryptoProvider that recovers an overridden signature to its address. The override then applies to block execution, calls, traces, and replays. The provider is process-wide: in one process, the overrides of every node apply. For a network whose EVM factory anvil cannot wrap, AnvilEvmConfig now installs the ecrecover override on the EVMs it creates, so Tempo gets it. Monad recovers ancestor authorities through the same functions.
Pending block. Reth reuses a pending block for a second and keys it on the parent alone. The node now drops it after a change to the pool, the state, or the next block's environment. Calls, estimates, and access lists at pending run with the pending block's state changes as state overrides, because reth runs them on the latest state. eth_estimateGas without a block estimates on the pending block, as anvil does. Otterscan. ots_getInternalOperations now includes the top-level operation, and ots_getBlockTransactions pages from the first transaction. ots_searchTransactionsBefore and ots_searchTransactionsAfter, which reth does not implement, now work. Fees. A blob call without a blob fee cap runs at a zero blob base fee in eth_call and trace_call, as in geth. On Arbitrum chains, the pool and the EVM accept a priority fee above the fee cap. This enables nine tests that were ignored.
The pool validator accepts some transactions itself: an impersonated sender, a signature override, a sender with code, and, on Arbitrum, a tip above the fee cap. For a blob transaction this dropped the sidecar from the blob store, so the block builder never included it. A blob transaction from a dev account with code on a mainnet fork never got mined. The validator now keeps the sidecar. --prune-history had no effect, because reth keeps the full history. An RPC middleware now answers a state read at a block below the retention window with anvil's BlockOutOfRangeError. This enables four tests that were ignored.
A blob call without a blob fee cap now gets a zero cap, so it builds as a blob transaction. Without validation, a zero cap runs at the block's blob base fee, and the response shows the zero cap. A request with a sidecar gets its blob hashes also when it sends an empty hash list. The blob gas of a simulated block is checked against the hardfork's per-block limit. A cap below the blob base fee is reported with anvil's message. This enables four tests that were ignored. Two stay ignored, because anvil charges no blob fee without validation. One stays ignored, because reth-anvil schedules no hardfork after the configured one.
reth-anvil now runs Ethereum and Tempo. Networks other than Ethereum belong in extensions that their teams own, on the AnvilNetwork API. The reth-anvil side (the Monad network, its tests, the monad feature, the pool's gas-only balance rule and blob rejection) landed with the previous commit by mistake. This commit removes the anvil/monad feature from cast, forge, script, and the EVM, and the cast tests that spawned a Monad anvil node. cast, forge, and the EVM keep their own Monad support.
crates/anvil stays deleted. Ported from master's anvil changes: the configurable transaction coalescing window (#17448), the fork URL redaction in anvil_nodeInfo (#17421), and the start of the priced-call rule (#17419): free calls still drop their fee fields, priced calls no longer do. The state gas estimate (#17432) needs no port. Master's new Base tests are left out, because Base is out of scope. Not done yet: reth runs every call with the base fee check off, so a priced call below the base fee still succeeds; reth-anvil needs an explicit check in eth_call, eth_estimateGas, trace_call, and trace_callMany. Two more tests fail after the merge: gas::test_estimate_gas_transfers_across_hardforks and eip4844::estimate_gas_reserves_blob_fee.
Reject priced calls, estimates, and traces below the execution block base fee. Use the pending environment for pending calls and retain free calls and pre-London behavior. Refine gas estimates below 21,000 after EIP-2780, enforce the requested gas allowance, and populate blob hashes from sidecars. Cover block selection and fee boundaries across all four RPC methods.
Use the configured endpoint for the fee history comparison. Node info exposes a redacted URL that must not be used to connect to the upstream node.
Try one gas below a small estimate before refining the full error range. Exact transfer estimates need one probe, while Amsterdam contract calls still get the exact gas limit. Remove extra probes that delayed batch transaction submission past the coalescing window. Keep the requested gas allowance check after refinement.
Move the Tempo network, native request adapters, RPC methods, and tests into reth-anvil-tempo. Put the anvil binary in reth-anvil-cli, which selects the installed Ethereum or Tempo network. Expose the network components and marker-based adapter traits so external crates can implement the SDK for upstream types. Register typed network RPC methods through the network hook and retain them after reset. Keep Tempo RPC generic over node components so the compiler does not expand the full concrete node type in every handler.
Return a JSON-RPC parse error for unmatched IPC delimiters and resume decoding later requests on the same connection. Keep reth's dispatch, subscription limits, and connection lifecycle in a local adapter until its IPC server accepts a configurable codec. Cover malformed and split frames, leading whitespace, and connection recovery. Document the adapter and the upstream hook that replaces it.
Restore the remaining Ethereum regression cases across blob calls, fork replay, pending blocks, and traces after chain-id changes. Keep Arbitrum execution numbers and historical execution rules in state dumps, and apply BPO blob schedules without upgrading the configured EVM. Permit sidecarless blob transactions only for impersonated senders, while retaining normal pool validation and native payload accounting. Give each node an owned runtime and close it on shutdown or failed startup so retained handles cannot leave its executor running. Select omitted nonces from a stable chain head under the sender lock to avoid stale nonces when automining updates the pool between reads. Enable the affected integration tests, add regression coverage, and record the remaining upstream workarounds.
Merge origin/master at 71a072d. Retain the shared CIP-64 transaction primitives and keep the legacy Anvil crates deleted. Leave Celo execution to network extensions and describe the shared encoding support accurately. Preserve the no-default-features request builder and the Ethereum reorg request types when resolving the merge.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replace
crates/anvilwith a reth SDK node that retains anvil's development controls, RPC methods, CLI, and fork support.reth-anvilprovides the SDK and Ethereum implementation;reth-anvil-tempoimplements the public extension API, andreth-anvil-cliowns the binary. Other networks belong in extensions; Optimism and Base remain ignored specifications.UPSTREAM.mdrecords the remaining workarounds, including sender caching and receipt retries, which stay until the pinned dependencies contain the fixes. Refs paradigmxyz/reth#27816 (reopening #27757) and #9970.