Skip to main content

Crate evm_fork_cache

Crate evm_fork_cache 

Source
Expand description

Forked EVM simulation engine for EVM search, MEV, and backtesting.

evm-fork-cache simulates EVM transactions against recent on-chain state without re-deriving that state on every call. It builds on revm, alloy, and foundry-fork-db to provide a lazy-loading state cache, immutable snapshots shareable across threads, per-simulation overlays, a freshness control plane, and the state-manipulation helpers a search loop needs (balance overrides, batched multicalls, verified code seeding + Foundry-style bytecode etching, CREATE3 address derivation, and an extensible revert decoder).

§The state stack

Reads flow up; the fork DB lazily fetches misses from RPC. Writes and purges are applied directly to the cache (no RPC on the hot path).

EvmOverlay × N      isolated, Send simulations (cheap Arc clones)
     ▲ clone × N
EvmSnapshot         immutable, point-in-time, Send + Sync
     ▲ snapshot()
EvmCache            lazy RPC fetch + local state cache + targeted writes/purge
     ▲ lazy fetch
RPC provider

The entry point is cache::EvmCache: construct one over an RPC backend (see cache::EvmCacheBuilder), then snapshot it with cache::EvmCache::snapshot to fan out parallel simulations, each driving its own cache::EvmOverlay. EvmCache is !Send (it owns the mutable fork and blocks on RPC internally); EvmSnapshot is Send + Sync and EvmOverlay is Send, so the fan-out parallelizes safely.

§Modules

  • cache — the fork cache, snapshots, overlays, and on-disk persistence.
  • access_list / access_set — EIP-2930 access-list construction and EIP-2929 warm-slot tracking for gas estimation.
  • errors — structured simulation errors (errors::SimError) and an extensible revert-reason decoder you can teach your own custom Solidity error selectors.
  • freshness — the four-layer freshness model (classification, observation, policy, mechanism) and the optimistic verify-and-rerun execution loop with deferred validation.
  • state_update — the generic state-mutation vocabulary (StateUpdate / AccountPatch / PurgeScope, plus relative SlotDelta read-modify-write and masked SlotMasked writes) applied by EvmCache::apply_update / apply_updates / modify_slot, with a structured StateDiff output (Pillar B.1).
  • events — the event → state pipeline (Pillar B.2): EventDecoder / StateView / DecoderRegistry decode an on-chain Log into StateUpdates, and EventPipeline ingests, reorg-purges, and reconciles a block’s logs. Ships an ERC-20 Transfer decoder plus traits for external decoders.
  • reactive — default-enabled provider-neutral handler runtime for logs, blocks, and pending transaction signals. Pure handlers emit StateUpdates, invalidations, resync requests, speculative signals, and hooks; the runtime validates and applies canonical cache mutations, journals canonical block effects for depth-bounded reorg recovery, and includes a live AlloySubscriber (WebSocket) transport.
  • cold_start — default-enabled (reactive-gated) declarative warming of a working set of accounts/slots into the cache in one batched pass (EvmCache::run_cold_start + ColdStartPlanner), returning a structured ColdStartRunReport. Each round verifies any pending code seeds first (the verify_code phase), so sims never run over unverified claims.
  • bundle — multi-transaction bundle execution over cumulative block state (EvmOverlay::simulate_bundle): ordered txs, an Atomic/AllowReverts revert policy, and coinbase/miner-payment accounting.
  • inspector — an Inspector that captures ERC20 Transfer events to reconstruct balance deltas from a simulation.
  • tracing — a call-frame Inspector (CallTracer building a CallTrace tree) plus InspectorStack for composing several inspectors over one pass, driven through EvmOverlay::call_raw_with_inspector.
  • bulk_storage — bulk storage extraction over eth_call state overrides (the default batch storage fetcher): thousands of slots — across many contracts — per call, plus custom storage programs and account-fields/block-context extractors. See docs/bulk-storage-extraction.md for measured economics.
  • multicall — batched read-only calls through Multicall3.
  • deploy / create3 — contract deployment and CREATE3 address math.
  • prefetch_registry — two-stage storage-slot pre-warming (complemented by the declarative cache::EvmCache::prewarm_slots).
  • mapping_probe — trace-based discovery of hash-derived storage slots: derive a mapping’s base slot and byte-order layout (Solidity / Vyper / Solady / nested) from one simulation, via EvmCache::trace_hashed_slots / discover_erc20_balance_slot / track_erc20_balances.

§Requirements

Any constructor or method that may touch RPC fetches missing state through a synchronous façade over an async provider (tokio::task::block_in_place), so it must run on a multi-thread tokio runtime:

#[tokio::main(flavor = "multi_thread")]
async fn main() { /* ... */ }

#[tokio::test(flavor = "multi_thread")]
async fn my_test() { /* ... */ }

On a current-thread runtime, fetch paths do not panic: the synchronous bridge checks the runtime flavor up front and returns a typed RuntimeError (CurrentThreadRuntime) instead, so a misconfigured runtime surfaces as a handled error rather than a panic deep in a callback. The offline examples and integration tests build the cache over a mocked provider and never reach the network, so they are exempt.

§Error handling

Simulation entry points that distinguish failure modes return errors::SimulationResult (Result<T, SimError>), where SimError separates a decoded Revert, an EVM Halt, and an unexpected host-side Other SimHostError (RPC, database, ABI encoding). Other fallible modules expose domain errors such as CacheError, FreshnessError, and StorageFetchError rather than erasing failures into a dynamic catch-all error. The freshness loop never silently trusts stale data: a transient RPC failure surfaces as freshness::Validation::Unverified so callers can retry rather than act on unverified results.

§Maturity & stability

This crate is pre-1.0 and developed against a phased roadmap (see docs/ROADMAP.md). Until 1.0, breaking changes may land in minor releases; each is recorded in the crate CHANGELOG.md. MSRV is Rust 1.88 (edition 2024).

The examples/ directory has runnable, documented walkthroughs of each module — offline ones that need no network, plus a few that fork real chain state over RPC. See the crate README for the full list.

Re-exports§

pub use access_set::StorageAccessList;
pub use bulk_storage::AccountFieldsSample;
pub use bulk_storage::BlockContextSample;
pub use bulk_storage::BulkCallConfig;
pub use bulk_storage::BulkFetcherStatus;
pub use bulk_storage::CallDispatch;
pub use bulk_storage::StorageProgram;
pub use bulk_storage::bulk_call_storage_fetcher;
pub use bulk_storage::bulk_call_storage_fetcher_with_fallback;
pub use bulk_storage::bulk_call_storage_fetcher_with_status;
pub use bulk_storage::fetch_account_fields_bulk;
pub use bulk_storage::fetch_block_context;
pub use bulk_storage::fetch_slots_bulk;
pub use bulk_storage::planned_call_count;
pub use bulk_storage::run_storage_program;
pub use bulk_storage::run_storage_programs;
pub use bundle::BundleOptions;
pub use bundle::BundleResult;
pub use bundle::BundleTx;
pub use bundle::RevertPolicy;
pub use bundle::TxOutcome;
pub use cache::AccountFieldsFetchFn;
pub use cache::AccountProof;
pub use cache::AccountProofFetchFn;
pub use cache::BlockContextRequirements;
pub use cache::BlockStateAccountDiff;
pub use cache::BlockStateDiff;
pub use cache::BlockStateDiffFetchFn;
pub use cache::BlockStateStorageDiff;
pub use cache::CacheSpeedMode;
pub use cache::CallSimulationResult;
pub use cache::CodeMismatch;
pub use cache::CodeSeedState;
pub use cache::CodeVerifyReport;
pub use cache::EvmCache;
pub use cache::EvmCacheBuilder;
pub use cache::EvmOverlay;
pub use cache::EvmSnapshot;
pub use cache::PrewarmReport;
pub use cache::StorageBatchConfig;
pub use cache::StorageFetchStrategy;
pub use cache::TxConfig;
pub use cache::point_read_storage_fetcher;
pub use cold_start::ColdStartCall;
pub use cold_start::ColdStartCallResult;
pub use cold_start::ColdStartConfig;
pub use cold_start::ColdStartError;
pub use cold_start::ColdStartPin;
pub use cold_start::ColdStartPlan;
pub use cold_start::ColdStartPlanner;
pub use cold_start::ColdStartResults;
pub use cold_start::ColdStartRoundSummary;
pub use cold_start::ColdStartRunReport;
pub use cold_start::ColdStartStep;
pub use cold_start::RootBaseline;
pub use cold_start::RootBaselinePlanner;
pub use cold_start::RootProbeOutcome;
pub use cold_start::RoundOutcome;
pub use errors::AccessListError;
pub use errors::AccessListResult;
pub use errors::BlockContextError;
pub use errors::CacheError;
pub use errors::CacheResult;
pub use errors::DeployError;
pub use errors::DeployResult;
pub use errors::FreshnessError;
pub use errors::FreshnessResult;
pub use errors::MulticallError;
pub use errors::MulticallResult;
pub use errors::OverlayError;
pub use errors::OverlayResult;
pub use errors::PersistenceError;
pub use errors::RpcError;
pub use errors::RuntimeError;
pub use errors::SimError;
pub use errors::SimHostError;
pub use errors::SimulationError;
pub use errors::SimulationResult;
pub use errors::StorageFetchError;
pub use errors::StorageFetchResult;
pub use events::erc20::Erc20TransferDecoder;
pub use events::BlockDigest;
pub use events::DecoderRegistry;
pub use events::EventDecoder;
pub use events::EventPipeline;
pub use events::ReconcileReport;
pub use events::ReorgConfig;
pub use events::StateView;
pub use freshness::AlwaysVerify;
pub use freshness::BlockClock;
pub use freshness::FreshnessClock;
pub use freshness::FreshnessController;
pub use freshness::FreshnessParams;
pub use freshness::FreshnessPolicy;
pub use freshness::FreshnessRegistry;
pub use freshness::NeverVerify;
pub use freshness::ObservationDriven;
pub use freshness::SimRequest;
pub use freshness::SlotChange;
pub use freshness::SlotFetch;
pub use freshness::SlotOutcome;
pub use freshness::SpeculativeSim;
pub use freshness::Validation;
pub use freshness::Validity;
pub use freshness::WallClock;
pub use mapping_probe::Confidence;
pub use mapping_probe::HashSlotAccess;
pub use mapping_probe::HashStorageProbe;
pub use mapping_probe::SlotLayout;
pub use mapping_probe::TrackedBalances;
pub use mapping_probe::TrackedMapping;
pub use reactive::InterestOwnerSubscriber;
pub use reactive::ReactiveConfig;
pub use reactive::ReactiveEngine;
pub use reactive::ReactiveEngineError;
pub use reactive::ReactiveEngineRegisterError;
pub use reactive::ReactiveHandler;
pub use reactive::ReactiveRuntime;
pub use state_update::AccountChange;
pub use state_update::AccountPatch;
pub use state_update::PurgeRecord;
pub use state_update::PurgeScope;
pub use state_update::SkippedAccountPatch;
pub use state_update::SkippedBalanceDelta;
pub use state_update::SkippedDelta;
pub use state_update::SkippedMask;
pub use state_update::SlotDelta;
pub use state_update::StateDiff;
pub use state_update::StateUpdate;
pub use tracing::CallKind;
pub use tracing::CallStatus;
pub use tracing::CallTrace;
pub use tracing::CallTracer;
pub use tracing::InspectorStack;

Modules§

access_list
EIP-2930 access list builder with L2 profitability accounting.
access_set
Compact account/storage touch sets captured from EVM execution.
bulk_storage
Bulk storage extraction over eth_call state overrides.
bundle
Multi-transaction bundle simulation over cumulative block state, plus coinbase / miner-payment accounting (Phase 6 Track A+B).
cache
The forked-EVM state cache: lazy RPC loading, a layered write funnel, and cheap copy-on-write snapshots.
cold_start
Protocol-neutral cold-start sync for EvmCache.
create3
CREATE3 deployment-address derivation.
deploy
Helpers for deploying Foundry artifacts into an EvmCache.
errors
Simulation error types and revert-reason decoding.
events
Event → state pipeline (Pillar B.2 — the reader half of the event pipeline).
freshness
Freshness control plane and the optimistic verify-and-rerun execution loop.
inspector
ERC20 Transfer-event capture for reconstructing balance deltas.
mapping_probe
Trace-based discovery of hash-derived storage slots.
multicall
Multicall3 batching support for EvmCache.
prefetch_registry
Generalized storage prefetch registry for EVM cache pre-warming.
reactive
Protocol-neutral reactive runtime for cache state effects.
state_update
Targeted state-mutation vocabulary and the structured diff it produces (Pillar B.1 — the writer half of the event → state pipeline).
tracing
Call-frame tracing and a composing inspector seam.