Skip to main content

evm_fork_cache/
lib.rs

1//! Forked EVM **simulation engine** for EVM search, MEV, and backtesting.
2//!
3//! `evm-fork-cache` simulates EVM transactions against recent on-chain state
4//! without re-deriving that state on every call. It builds on [`revm`],
5//! [`alloy`], and [`foundry-fork-db`] to provide a lazy-loading state cache,
6//! immutable snapshots shareable across threads, per-simulation overlays, a
7//! freshness control plane, and the state-manipulation helpers a search loop
8//! needs (balance overrides, batched multicalls, Foundry-style bytecode etching,
9//! CREATE3 address derivation, and an extensible revert decoder).
10//!
11//! [`revm`]: https://github.com/bluealloy/revm
12//! [`alloy`]: https://github.com/alloy-rs/alloy
13//! [`foundry-fork-db`]: https://github.com/foundry-rs/foundry-fork-db
14//!
15//! # The state stack
16//!
17//! Reads flow up; the fork DB lazily fetches misses from RPC. Writes and purges
18//! are applied directly to the cache (no RPC on the hot path).
19//!
20//! ```text
21//! EvmOverlay × N      isolated, Send simulations (cheap Arc clones)
22//!      ▲ clone × N
23//! EvmSnapshot         immutable, point-in-time, Send + Sync
24//!      ▲ create_snapshot()
25//! EvmCache            lazy RPC fetch + local state cache + targeted writes/purge
26//!      ▲ lazy fetch
27//! RPC provider
28//! ```
29//!
30//! The entry point is [`cache::EvmCache`]: construct one over an RPC backend
31//! (see [`cache::EvmCacheBuilder`]), then snapshot it with
32//! [`cache::EvmCache::create_snapshot`] to fan out parallel simulations, each
33//! driving its own [`cache::EvmOverlay`]. `EvmCache` is `!Send` (it owns the
34//! mutable fork and blocks on RPC internally); `EvmSnapshot` is `Send + Sync`
35//! and `EvmOverlay` is `Send`, so the fan-out parallelizes safely.
36//!
37//! # Modules
38//!
39//! - [`cache`] — the fork cache, snapshots, overlays, and on-disk persistence.
40//! - [`access_list`] / [`access_set`] — EIP-2930 access-list construction and
41//!   EIP-2929 warm-slot tracking for gas estimation.
42//! - [`errors`] — structured simulation errors ([`errors::SimError`]) and an
43//!   extensible revert-reason decoder you can teach your own custom Solidity
44//!   error selectors.
45//! - [`freshness`] — the four-layer freshness model (classification, observation,
46//!   policy, mechanism) and the optimistic verify-and-rerun execution loop with
47//!   deferred validation.
48//! - [`state_update`] — the generic state-mutation vocabulary (`StateUpdate` /
49//!   `AccountPatch` / `PurgeScope`, plus relative `SlotDelta` read-modify-write and
50//!   masked `SlotMasked` writes) applied by `EvmCache::apply_update` /
51//!   `apply_updates` / `modify_slot`, with a structured `StateDiff` output
52//!   (Pillar B.1).
53//! - [`events`] — the event → state pipeline (Pillar B.2): `EventDecoder` /
54//!   `StateView` / `DecoderRegistry` decode an on-chain `Log` into `StateUpdate`s,
55//!   and `EventPipeline` ingests, reorg-purges, and reconciles a block's logs.
56//!   Ships an ERC-20 `Transfer` decoder plus traits for external decoders.
57//! - `reactive` — default-enabled provider-neutral handler runtime for logs,
58//!   blocks, and pending transaction signals. Pure handlers emit `StateUpdate`s,
59//!   invalidations, resync requests, speculative signals, and hooks; the runtime
60//!   validates and applies canonical cache mutations, journals canonical block
61//!   effects for depth-bounded reorg recovery, and includes a live
62//!   `AlloySubscriber` (WebSocket) transport.
63//! - `cold_start` — default-enabled (reactive-gated) declarative warming of a
64//!   working set of accounts/slots into the cache in one batched pass
65//!   (`EvmCache::run_cold_start` + `ColdStartPlanner`), returning a structured
66//!   `ColdStartRunReport`.
67//! - [`bundle`] — multi-transaction bundle execution over cumulative block state
68//!   ([`EvmOverlay::simulate_bundle`](cache::EvmOverlay::simulate_bundle)): ordered
69//!   txs, an `Atomic`/`AllowReverts` revert policy, and coinbase/miner-payment
70//!   accounting.
71//! - [`inspector`] — an [`Inspector`](revm::Inspector) that captures ERC20
72//!   `Transfer` events to reconstruct balance deltas from a simulation.
73//! - [`tracing`] — a call-frame [`Inspector`](revm::Inspector)
74//!   ([`CallTracer`] building a [`CallTrace`] tree) plus [`InspectorStack`] for
75//!   composing several inspectors over one pass, driven through
76//!   [`EvmOverlay::call_raw_with_inspector`](cache::EvmOverlay::call_raw_with_inspector).
77//! - [`multicall`] — batched read-only calls through Multicall3.
78//! - [`deploy`] / [`create3`] — contract deployment and CREATE3 address math.
79//! - [`prefetch_registry`] — two-stage storage-slot pre-warming.
80//!
81//! # Requirements
82//!
83//! Any constructor or method that may touch RPC fetches missing state through a
84//! synchronous façade over an async provider
85//! ([`tokio::task::block_in_place`]), so it must run on a **multi-thread** tokio
86//! runtime:
87//!
88//! ```ignore
89//! #[tokio::main(flavor = "multi_thread")]
90//! async fn main() { /* ... */ }
91//!
92//! #[tokio::test(flavor = "multi_thread")]
93//! async fn my_test() { /* ... */ }
94//! ```
95//!
96//! Running on a current-thread runtime panics when a fetch is attempted. The
97//! offline examples and integration tests build the cache over a mocked provider
98//! and never reach the network, so they are exempt.
99//!
100//! # Error handling
101//!
102//! Simulation entry points that distinguish failure modes return
103//! [`errors::SimulationResult`] (`Result<T, SimError>`), where
104//! [`SimError`](errors::SimError) separates a decoded [`Revert`](errors::SimError::Revert),
105//! an EVM [`Halt`](errors::SimError::Halt), and an unexpected host-side
106//! [`Other`](errors::SimError::Other) error (RPC, database, ABI encoding). The
107//! freshness loop never silently trusts stale data: a transient RPC failure
108//! surfaces as [`freshness::Validation::Unverified`] so callers can retry rather
109//! than act on unverified results.
110//!
111//! # Maturity & stability
112//!
113//! This crate is **pre-1.0** and developed against a phased roadmap (see
114//! `docs/ROADMAP.md`). Until 1.0, breaking changes may land in minor releases;
115//! each is recorded in the crate `CHANGELOG.md`. MSRV is Rust 1.88 (edition 2024).
116//!
117//! The `examples/` directory has runnable, documented walkthroughs of each
118//! module — offline ones that need no network, plus a few that fork real chain
119//! state over RPC. See the crate README for the full list.
120pub mod access_list;
121pub mod access_set;
122pub mod bundle;
123pub mod cache;
124#[cfg(feature = "reactive")]
125pub mod cold_start;
126pub mod create3;
127pub mod deploy;
128pub mod errors;
129pub mod events;
130pub mod freshness;
131pub mod inspector;
132pub mod multicall;
133pub mod prefetch_registry;
134#[cfg(feature = "reactive")]
135pub mod reactive;
136pub mod state_update;
137pub mod tracing;
138
139pub use access_set::StorageAccessList;
140// Phase 6 Track A+B: bundle simulation + coinbase accounting public vocabulary.
141pub use bundle::{BundleOptions, BundleResult, BundleTx, RevertPolicy, TxOutcome};
142// Primary entry points, hoisted to the crate root for discoverability. The
143// fully-qualified module paths (`cache::EvmCache`, `reactive::ReactiveRuntime`,
144// …) remain valid, so this is purely additive.
145pub use cache::{
146    CallSimulationResult, EvmCache, EvmCacheBuilder, EvmOverlay, EvmSnapshot, TxConfig,
147};
148#[cfg(feature = "reactive")]
149pub use cold_start::{
150    ColdStartCall, ColdStartCallResult, ColdStartConfig, ColdStartError, ColdStartPin,
151    ColdStartPlan, ColdStartPlanner, ColdStartResults, ColdStartRoundSummary, ColdStartRunReport,
152    ColdStartStep, RoundOutcome,
153};
154pub use events::erc20::Erc20TransferDecoder;
155pub use events::{
156    BlockDigest, DecoderRegistry, EventDecoder, EventPipeline, ReconcileReport, ReorgConfig,
157    StateView,
158};
159pub use freshness::{
160    AlwaysVerify, BlockClock, FreshnessClock, FreshnessController, FreshnessParams,
161    FreshnessPolicy, FreshnessRegistry, NeverVerify, ObservationDriven, SimRequest, SlotChange,
162    SlotFetch, SlotOutcome, SpeculativeSim, Validation, Validity, WallClock,
163};
164#[cfg(feature = "reactive")]
165pub use reactive::{ReactiveConfig, ReactiveHandler, ReactiveRuntime};
166pub use state_update::{
167    AccountChange, AccountPatch, PurgeRecord, PurgeScope, SkippedAccountPatch, SkippedBalanceDelta,
168    SkippedDelta, SkippedMask, SlotDelta, StateDiff, StateUpdate,
169};
170pub use tracing::{CallKind, CallStatus, CallTrace, CallTracer, InspectorStack};