perf(evm): discover non-Anvil fork endpoints once - #17280
Merged
Merged
Conversation
Fork resolution snapshots the endpoint identity (`eth_chainId` plus the `anvil_nodeInfo` probe) around every remote read so that an Anvil reset cannot slip between them. An endpoint whose discovery found no Anvil identity has nothing to revalidate, but each step still repeated the snapshot: `cast run` sent 9 `eth_chainId` and 9 `anvil_nodeInfo` requests before replaying anything. Reuse the cached discovery for such endpoints. Discovery itself is unchanged, endpoints identified as Anvil keep their strict revalidation, and `discover_fork_endpoint` still always inspects the endpoint. Cold `cast run` of a mainnet swap against a remote RPC drops from 9+9 to 2+2 probe requests and from 1.66s to 1.34s (median of 20 interleaved runs).
mattsse
marked this pull request as ready for review
October 2, 2026 11:40
mattsse
requested review from
0xrusowsky,
DaniPopes,
figtracer,
grandizzy,
mablr and
stevencartavia
as code owners
October 2, 2026 11:40
Discovery read a second snapshot to confirm the first. Without Anvil node info the identity depends on the chain ID alone, so the second `eth_chainId` and `anvil_nodeInfo` requests confirmed nothing a remote node can change. Return the first snapshot instead. An Anvil endpoint whose first probe fails is now discovered as non-Anvil. Resolution without a cached discovery still reads both snapshots and identifies it. Cold `cast run` of a mainnet swap against a remote RPC now sends 1+1 probe requests (9+9 on master): 1.69s -> 1.29s (median of 20 interleaved runs).
mablr
reviewed
Oct 2, 2026
stevencartavia
previously approved these changes
Oct 2, 2026
stevencartavia
approved these changes
Oct 3, 2026
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.
Fork resolution snapshots the endpoint identity (
eth_chainIdplus theanvil_nodeInfoprobe) before and after every remote read, so an Anvil reset or profile change cannot slip in between. An endpoint without Anvil node info has nothing to revalidate, since its identity is derived from the chain ID alone, yet each step repeated the snapshot:cast rundiscovered the endpoint inrunand again inTracingExecutor::get_fork, took three snapshots around the block and gas price reads inenv_resolved, and two more around the block read increate_fork. That is 9eth_chainIdand 9anvil_nodeInforequests, sent sequentially before the replay starts.cast call --traceandforge test --fork-urlgo through the same sequence.Such endpoints are now probed once: discovery returns the first snapshot without a confirming one when it finds no Anvil node info, and later snapshots reuse the cached discovery. Any probe failure still means "no Anvil" until node info is returned, endpoints identified as Anvil keep their strict revalidation around every read, and
discover_fork_endpointstill always inspects the endpoint. The remote trace paths (--debug-trace-transaction,--debug-trace-call) use the same reuse for their before/after identity check. The one behavior difference is an Anvil endpoint whose first discovery probe fails: it is now treated as non-Anvil for the rest of the invocation instead of being picked up by the confirming snapshot. Resolution without a cached discovery still reads both snapshots and identifies it.Results
Cold
cast run 0x29582b47311ef26dc1d0677448ff520d646ac1d0489ca310d70294343991130b --rpc-url https://ethereum.reth.rs/rpc(fork cache for the block removed before every run), release builds:eth_chainIdrequestsanvil_nodeInforequestsProbe requests (
eth_chainId+anvil_nodeInfo) on the other paths against the same endpoint:cast run --quickcast call --tracecast run --debug-trace-transactioncast call --debug-trace-callforge test --fork-url