Skip to content

Instantly share code, notes, and snippets.

@mablr
Created April 8, 2026 21:25
Show Gist options
  • Select an option

  • Save mablr/1f23845a3224a8fb0671e65cfb44603a to your computer and use it in GitHub Desktop.

Select an option

Save mablr/1f23845a3224a8fb0671e65cfb44603a to your computer and use it in GitHub Desktop.

EVM & Cheatcode Instrumentation: Network-Generic Architecture

One of the most significant internal refactors in Foundry 2.0 is the introduction of FoundryEvmNetwork, a supertrait that makes Foundry's entire EVM execution stack generic.

This refactor was originally driven by the need to bring Tempo support upstream into Foundry itself. But the architecture that emerged is intentionally general: any custom chain can plug into Foundry's full execution and testing infrastructure, cheatcodes, forking, fuzzing, coverage, gas snapshots, as long as it implements FoundryEvmNetwork, Alloy's Network, and FoundryEvmFactory. The pattern is deliberately similar to Reth's SDK model, where the node itself becomes an extension point rather than a monolith. In Foundry 2.0, the same philosophy applies to its evm crate: it is an SDK, not a fixed implementation.

The Problem

Foundry's executor, database backend, and cheatcode inspector were historically written against Ethereum-specific types. As the ecosystem expanded to Layer-2 networks with their own transaction formats, block environments, and EVM variants, this created a recurring problem: every network integration required patching through multiple layers of hardcoded types, accumulating tech debt and making new network support error-prone.

FoundryEvmNetwork: One Trait to Rule Them All

The solution is FoundryEvmNetwork, a single marker trait that pairs a network with its EVM factory:

pub trait FoundryEvmNetwork: Copy + Debug + Default + 'static {
    type Network: Network<TxEnvelope: ..., TransactionRequest: ..., ReceiptResponse: ...>;
    type EvmFactory: FoundryEvmFactory<Tx: FromRecoveredTx<...>>;
}

Concrete implementations are zero-cost marker types:

impl FoundryEvmNetwork for EthEvmNetwork {
    type Network = Ethereum;
    type EvmFactory = EthEvmFactory;
}

impl FoundryEvmNetwork for TempoEvmNetwork {
    type Network = TempoNetwork;
    type EvmFactory = TempoEvmFactory;
}

A companion set of type aliases: TxEnvFor<FEN>, BlockEnvFor<FEN>, EvmEnvFor<FEN>, TransactionRequestFor<FEN>, lets generic code reference evm and network-specific types without spelling out long trait projections everywhere.

A Generic Execution Stack

Every major component in the execution pipeline is now generic over FEN: FoundryEvmNetwork:

  • Executor<FEN>: the top-level test executor, previously fully hardcoded on Ethereum
  • Backend<FEN>: the in-memory and forked state database
  • DatabaseExt<F: FoundryEvmFactory>: the trait governing state snapshots and fork management, parameterized over the factory's Spec and BlockEnv
  • InspectorStack<FEN> and Cheatcodes<FEN>: the full cheatcode and inspection infrastructure, including broadcastable transactions and block overrides

The result: code written generically over FEN automatically supports any network without modification. Adding a new network means implementing two traits, FoundryEvmNetwork and FoundryEvmFactory, and the rest of the stack compiles in.

Cheatcodes Across Chains

Cheatcodes were the hardest part to generalize. Cheatcodes<FEN> is now fully generic: EVM context manipulation flows through revm's FoundryContextExt extension, transaction decoding, and broadcasting are handled Network-agnostic way, and nested EVM execution is dispatched through the network's own EVM factory:

fn with_nested_evm(&mut self, cheats: &mut Cheatcodes<FEN>, ecx: &mut FoundryContextFor<'_, FEN>, ...) {
    let mut evm = FEN::EvmFactory::default()
        .create_foundry_evm_with_inspector(db, evm_env, cheats);
    // ...
}

What This Means for Developers

For most users, this refactor is invisible, existing tests and scripts continue to work unchanged. For toolchain builders and chain teams, the benefit is concrete: Foundry's execution engine is now an extension point, not a patchwork. New EVM variants slot in without forking or monkey-patching the core.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment