Skip to main content

foundry_evm_core/backend/
mod.rs

1//! Foundry's main executor backend abstraction and implementation.
2
3use crate::{
4    FoundryBlock, FoundryChain, FoundryInspectorExt, FoundryTransaction, FromAnyRpcTransaction,
5    constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS},
6    evm::{
7        BlockEnvFor, ChainFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
8        FoundryEvmNetwork, HaltReasonFor, SpecFor, TxEnvFor,
9    },
10    fork::{CreateFork, ForkId, ForkResult, MultiFork},
11    state_snapshot::StateSnapshots,
12    utils::{
13        apply_chain_and_block_specific_env_changes_for_chain,
14        apply_chain_specific_tx_replay_env_changes_for_chain,
15    },
16};
17use alloy_chains::Chain;
18use alloy_consensus::{BlockHeader, Typed2718};
19use alloy_eips::BlockNumHash;
20use alloy_evm::{Evm, EvmEnv, EvmFactory, precompiles::PrecompilesMap};
21use alloy_genesis::GenesisAccount;
22use alloy_network::{
23    AnyNetwork, AnyRpcBlock, AnyRpcTransaction, BlockResponse, Network, TransactionResponse,
24};
25use alloy_primitives::{Address, B256, ChainId, TxKind, U256, keccak256, map::AddressSet, uint};
26use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions};
27use eyre::Context;
28use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender};
29use foundry_evm_networks::{NetworkConfigs, apply_bsc_p256_precompile};
30pub use foundry_fork_db::{
31    AccountFetchPolicy, BlockchainDb, ForkBlock, ForkBlockEnv, SharedBackend,
32    cache::BlockchainDbMeta,
33};
34use revm::{
35    Database, DatabaseCommit, JournalEntry,
36    bytecode::Bytecode,
37    context::{Block, BlockEnv, CfgEnv, ContextTr, JournalInner, Transaction},
38    context_interface::{journaled_state::account::JournaledAccountTr, result::ResultAndState},
39    database::{AccountState, CacheDB, DatabaseRef, EmptyDB},
40    database_interface::bal::BalState,
41    primitives::{AddressMap, HashMap as Map, KECCAK_EMPTY, Log, hardfork::SpecId},
42    state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
43};
44use std::{
45    collections::{BTreeMap, HashMap},
46    fmt::Debug,
47    time::Instant,
48};
49
50#[cfg(feature = "monad")]
51use crate::evm::monad::BlockContext;
52
53mod diagnostic;
54pub use diagnostic::RevertDiagnostic;
55
56mod bal;
57
58mod error;
59pub use error::{BackendError, BackendResult, DatabaseError, DatabaseResult};
60
61mod cow;
62pub use cow::CowBackend;
63
64mod in_memory_db;
65pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb};
66
67mod snapshot;
68pub use snapshot::{BackendStateSnapshot, RevertStateSnapshotAction, StateSnapshot};
69
70// A `revm::Database` that is used in forking mode
71type ForkDB<N, B> = CacheDB<SharedBackend<N, B>>;
72
73/// Represents a numeric `ForkId` valid only for the existence of the `Backend`.
74///
75/// The difference between `ForkId` and `LocalForkId` is that `ForkId` tracks pairs of `endpoint +
76/// block` which can be reused by multiple tests, whereas the `LocalForkId` is unique within a test
77pub type LocalForkId = U256;
78
79/// Transaction-context update required after a fork operation.
80#[cfg(feature = "monad")]
81pub enum ContextUpdate<C> {
82    /// The operation did not change the active chain cursor or outer journal state.
83    Unchanged,
84    /// The active fork cursor changed and provides replacement chain context.
85    Replace(C),
86    /// The outer journal changed while the active chain context remained unchanged.
87    Rebase,
88}
89
90/// Transaction-context update required after a fork operation, for a given [`FoundryEvmFactory`].
91///
92/// Only Monad's family-owned chain context needs to observe fork operations; every other network
93/// has no use for this signal, so it collapses to `()` without the `monad` feature.
94#[cfg(feature = "monad")]
95pub type ContextUpdateFor<F> = ContextUpdate<<F as FoundryEvmFactory>::Chain>;
96#[cfg(not(feature = "monad"))]
97pub type ContextUpdateFor<F> = std::marker::PhantomData<F>;
98
99/// Represents the index of a fork in the created forks vector
100/// This is used for fast lookup
101type ForkLookupIndex = usize;
102
103/// Inputs that define one transaction execution.
104struct TransactionInputs<FEN: FoundryEvmNetwork> {
105    evm_env: EvmEnvFor<FEN>,
106    tx_env: TxEnvFor<FEN>,
107    chain_context: ChainFor<FEN>,
108    rpc_block_number: u64,
109}
110
111/// Environment and network configuration used while replaying transactions.
112struct ReplayInputs<FEN: FoundryEvmNetwork> {
113    fork_id: ForkId,
114    forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
115    evm_env: EvmEnvFor<FEN>,
116    networks: NetworkConfigs,
117}
118
119/// Block data required to execute or position a fork at a transaction.
120struct TransactionForkTarget {
121    fork_block: BlockNumHash,
122    transaction: AnyRpcTransaction,
123    block: AnyRpcBlock,
124    mined: bool,
125    position: Option<TransactionPosition>,
126}
127
128/// Position of a transaction in its canonical block.
129#[derive(Clone, Copy)]
130struct TransactionPosition {
131    index: usize,
132    #[cfg(feature = "monad")]
133    count: usize,
134}
135
136/// A fork roll prepared for atomic publication.
137#[cfg(feature = "monad")]
138struct StagedForkRoll<FEN: FoundryEvmNetwork> {
139    local_id: LocalForkId,
140    fork_id: ForkId,
141    fork_index: ForkLookupIndex,
142    fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
143}
144
145/// Canonical chain position used to reconstruct network-specific transaction context.
146#[derive(Clone, Copy, Debug, PartialEq, Eq)]
147enum ForkPosition {
148    /// The database contains all transactions through the fork's current block.
149    AfterBlock { block: BlockNumHash },
150    /// The database contains the transactions before `transaction_index` in `block`.
151    BeforeTransaction { block: BlockNumHash, transaction_index: usize },
152}
153
154impl ForkPosition {
155    /// Returns the canonical position after committing `transaction_index`, if it immediately
156    /// follows this position.
157    fn after_transaction(
158        self,
159        block: BlockNumHash,
160        parent_hash: B256,
161        transaction_index: usize,
162        transaction_count: usize,
163    ) -> Option<Self> {
164        let is_next = match self {
165            Self::AfterBlock { block: previous } => {
166                transaction_index == 0
167                    && previous.number.checked_add(1) == Some(block.number)
168                    && previous.hash == parent_hash
169            }
170            Self::BeforeTransaction { block: current, transaction_index: current_index } => {
171                current == block && current_index == transaction_index
172            }
173        };
174        if !is_next {
175            return None;
176        }
177        let next_index = transaction_index.checked_add(1)?;
178        if next_index > transaction_count {
179            return None;
180        }
181
182        Some(if next_index == transaction_count {
183            Self::AfterBlock { block }
184        } else {
185            Self::BeforeTransaction { block, transaction_index: next_index }
186        })
187    }
188}
189
190/// All accounts that will have persistent storage across fork swaps.
191const DEFAULT_PERSISTENT_ACCOUNTS: [Address; 3] =
192    [CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, CALLER];
193
194/// `bytes32("failed")`, as a storage slot key into [`CHEATCODE_ADDRESS`].
195///
196/// Used by all `forge-std` test contracts and newer `DSTest` test contracts as a global marker for
197/// a failed test.
198pub const GLOBAL_FAIL_SLOT: U256 =
199    uint!(0x6661696c65640000000000000000000000000000000000000000000000000000_U256);
200
201pub type JournaledState = JournalInner<JournalEntry>;
202
203/// Account field changed by an out-of-band fork RPC mutation.
204#[derive(Clone, Copy, Debug)]
205pub enum ForkAccountField {
206    Balance,
207    Nonce,
208    Code,
209}
210
211impl ForkAccountField {
212    fn update(self, target: &mut AccountInfo, refreshed: &AccountInfo) {
213        match self {
214            Self::Balance => target.balance = refreshed.balance,
215            Self::Nonce => target.nonce = refreshed.nonce,
216            Self::Code => {
217                target.code_hash = refreshed.code_hash;
218                target.code = refreshed.code.clone();
219            }
220        }
221    }
222}
223
224/// An extension trait that allows us to easily extend the `revm::Inspector` capabilities
225#[auto_impl::auto_impl(&mut)]
226pub trait DatabaseExt<F: FoundryEvmFactory>:
227    Database<Error = DatabaseError> + DatabaseCommit + Debug
228{
229    /// Creates a new state snapshot at the current point of execution.
230    ///
231    /// A state snapshot is associated with a new unique id that's created for the snapshot.
232    /// State snapshots can be reverted: [DatabaseExt::revert_state], however, depending on the
233    /// [RevertStateSnapshotAction], it will keep the snapshot alive or delete it.
234    fn snapshot_state(
235        &mut self,
236        journaled_state: &JournaledState,
237        evm_env: &EvmEnv<F::Spec, F::BlockEnv>,
238    ) -> U256;
239
240    /// Reverts the snapshot if it exists
241    ///
242    /// Returns `true` if the snapshot was successfully reverted, `false` if no snapshot for that id
243    /// exists.
244    ///
245    /// **N.B.** While this reverts the state of the evm to the snapshot, it keeps new logs made
246    /// since the snapshots was created. This way we can show logs that were emitted between
247    /// snapshot and its revert.
248    /// This will also revert any changes in the `EvmEnv` and `TxEnv` and replace them with the
249    /// captured values from `Self::snapshot_state`.
250    ///
251    /// Depending on [RevertStateSnapshotAction] it will keep the snapshot alive or delete it.
252    fn revert_state(
253        &mut self,
254        id: U256,
255        journaled_state: &JournaledState,
256        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
257        caller: Address,
258        action: RevertStateSnapshotAction,
259    ) -> Option<JournaledState>;
260
261    /// Deletes the state snapshot with the given `id`
262    ///
263    /// Returns `true` if the snapshot was successfully deleted, `false` if no snapshot for that id
264    /// exists.
265    fn delete_state_snapshot(&mut self, id: U256) -> bool;
266
267    /// Deletes all state snapshots.
268    fn delete_state_snapshots(&mut self);
269
270    /// Creates and also selects a new fork
271    ///
272    /// This is basically `create_fork` + `select_fork`
273    fn create_select_fork(
274        &mut self,
275        fork: CreateFork,
276        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
277        tx_env: &mut F::Tx,
278        journaled_state: &mut JournaledState,
279    ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
280        let id = self.create_fork(fork)?;
281        let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
282        Ok((id, context))
283    }
284
285    /// Creates and also selects a new fork
286    ///
287    /// This is basically `create_fork` + `select_fork`
288    fn create_select_fork_at_transaction(
289        &mut self,
290        fork: CreateFork,
291        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
292        tx_env: &mut F::Tx,
293        journaled_state: &mut JournaledState,
294        transaction: B256,
295    ) -> eyre::Result<(LocalForkId, ContextUpdateFor<F>)> {
296        let id = self.create_fork_at_transaction(fork, transaction)?;
297        let context = self.select_fork(id, evm_env, tx_env, journaled_state)?;
298        Ok((id, context))
299    }
300
301    /// Creates a new fork but does _not_ select it
302    fn create_fork(&mut self, fork: CreateFork) -> eyre::Result<LocalForkId>;
303
304    /// Creates a new fork but does _not_ select it
305    fn create_fork_at_transaction(
306        &mut self,
307        fork: CreateFork,
308        transaction: B256,
309    ) -> eyre::Result<LocalForkId>;
310
311    /// Selects the fork's state
312    ///
313    /// This will also modify the current `EvmEnv` and `TxEnv`.
314    ///
315    /// **Note**: this does not change the local state, but swaps the remote state
316    ///
317    /// # Errors
318    ///
319    /// Returns an error if no fork with the given `id` exists
320    fn select_fork(
321        &mut self,
322        id: LocalForkId,
323        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
324        tx_env: &mut F::Tx,
325        journaled_state: &mut JournaledState,
326    ) -> eyre::Result<ContextUpdateFor<F>>;
327
328    /// Updates the fork to given block number.
329    ///
330    /// This will essentially create a new fork at the given block height.
331    ///
332    /// # Errors
333    ///
334    /// Returns an error if not matching fork was found.
335    fn roll_fork(
336        &mut self,
337        id: Option<LocalForkId>,
338        block_number: u64,
339        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
340        tx_env: &F::Tx,
341        journaled_state: &mut JournaledState,
342    ) -> eyre::Result<ContextUpdateFor<F>>;
343
344    /// Updates the fork to given transaction hash
345    ///
346    /// This will essentially create a new fork at the block this transaction was mined and replays
347    /// all transactions up until the given transaction.
348    ///
349    /// # Errors
350    ///
351    /// Returns an error if not matching fork was found.
352    fn roll_fork_to_transaction(
353        &mut self,
354        id: Option<LocalForkId>,
355        transaction: B256,
356        evm_env: &mut EvmEnv<F::Spec, F::BlockEnv>,
357        tx_env: &F::Tx,
358        journaled_state: &mut JournaledState,
359    ) -> eyre::Result<ContextUpdateFor<F>>;
360
361    /// Fetches the given transaction for the fork and executes it, committing the state in the DB
362    fn transact(
363        &mut self,
364        id: Option<LocalForkId>,
365        transaction: B256,
366        evm_env: EvmEnv<F::Spec, F::BlockEnv>,
367        outer_tx_env: &F::Tx,
368        journaled_state: &mut JournaledState,
369        inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
370    ) -> eyre::Result<ContextUpdateFor<F>>;
371
372    /// Executes a given TransactionRequest, commits the new state to the DB
373    fn transact_from_tx(
374        &mut self,
375        tx_env: F::Tx,
376        evm_env: EvmEnv<F::Spec, F::BlockEnv>,
377        journaled_state: &mut JournaledState,
378        inspector: &mut dyn for<'db> FoundryInspectorExt<F::FoundryContext<'db>>,
379    ) -> eyre::Result<()>;
380
381    /// Returns transaction-position context for a synthetic transaction on the active database.
382    fn chain_context_for_synthetic_transaction(&self, tx: &F::Tx) -> eyre::Result<F::Chain> {
383        Ok(F::Chain::for_transaction(tx))
384    }
385
386    /// Returns the `ForkId` that's currently used in the database, if fork mode is on
387    fn active_fork_id(&self) -> Option<LocalForkId>;
388
389    /// Returns the Fork url that's currently used in the database, if fork mode is on
390    fn active_fork_url(&self) -> Option<String>;
391
392    /// Returns the source chain ID of the active fork, independent of execution overrides.
393    fn active_fork_source_chain_id(&self) -> Option<u64>;
394
395    /// Returns the active fork's current fork block number, if any.
396    fn active_fork_block_number(&self) -> Option<u64> {
397        None
398    }
399
400    /// Whether the database is currently in forked mode.
401    fn is_forked_mode(&self) -> bool {
402        self.active_fork_id().is_some()
403    }
404
405    /// Ensures that an appropriate fork exists
406    ///
407    /// If `id` contains a requested `Fork` this will ensure it exists.
408    /// Otherwise, this returns the currently active fork.
409    ///
410    /// # Errors
411    ///
412    /// Returns an error if the given `id` does not match any forks
413    ///
414    /// Returns an error if no fork exists
415    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId>;
416
417    /// Ensures that a corresponding `ForkId` exists for the given local `id`
418    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId>;
419
420    /// Handling multiple accounts/new contracts in a multifork environment can be challenging since
421    /// every fork has its own standalone storage section. So this can be a common error to run
422    /// into:
423    ///
424    /// ```solidity
425    /// function testCanDeploy() public {
426    ///    vm.selectFork(mainnetFork);
427    ///    // contract created while on `mainnetFork`
428    ///    DummyContract dummy = new DummyContract();
429    ///    // this will succeed
430    ///    dummy.hello();
431    ///
432    ///    vm.selectFork(optimismFork);
433    ///
434    ///    vm.expectRevert();
435    ///    // this will revert since `dummy` contract only exists on `mainnetFork`
436    ///    dummy.hello();
437    /// }
438    /// ```
439    ///
440    /// If this happens (`dummy.hello()`), or more general, a call on an address that's not a
441    /// contract, revm will revert without useful context. This call will check in this context if
442    /// `address(dummy)` belongs to an existing contract and if not will check all other forks if
443    /// the contract is deployed there.
444    ///
445    /// Returns a more useful error message if that's the case
446    fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic>;
447
448    /// Loads the account allocs from the given `allocs` map into the passed [JournaledState].
449    ///
450    /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise.
451    fn load_allocs(
452        &mut self,
453        allocs: &BTreeMap<Address, GenesisAccount>,
454        journaled_state: &mut JournaledState,
455    ) -> Result<(), BackendError>;
456
457    /// Copies bytecode, storage, nonce and balance from the given genesis account to the target
458    /// address.
459    ///
460    /// Returns [Ok] if data was successfully inserted into the journal, [Err] otherwise.
461    fn clone_account(
462        &mut self,
463        source: &GenesisAccount,
464        target: &Address,
465        journaled_state: &mut JournaledState,
466    ) -> Result<(), BackendError>;
467
468    /// Returns true if the given account is currently marked as persistent.
469    fn is_persistent(&self, acc: &Address) -> bool;
470
471    /// Refreshes an account field changed out-of-band on the active fork in any already-loaded
472    /// cache and journal entries.
473    fn refresh_fork_account(
474        &mut self,
475        address: Address,
476        field: ForkAccountField,
477        journaled_state: &mut JournaledState,
478    ) -> Result<(), BackendError>;
479
480    /// Like [`refresh_fork_account`](Self::refresh_fork_account), but for a single storage slot.
481    fn refresh_fork_storage(
482        &mut self,
483        address: Address,
484        slot: U256,
485        journaled_state: &mut JournaledState,
486    ) -> Result<(), BackendError>;
487
488    /// Revokes persistent status from the given account.
489    fn remove_persistent_account(&mut self, account: &Address) -> bool;
490
491    /// Marks the given account as persistent.
492    fn add_persistent_account(&mut self, account: Address) -> bool;
493
494    /// Removes persistent status from all given accounts.
495    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
496    fn remove_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
497    where
498        Self: Sized,
499    {
500        for acc in accounts {
501            self.remove_persistent_account(&acc);
502        }
503    }
504
505    /// Extends the persistent accounts with the accounts the iterator yields.
506    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
507    fn extend_persistent_accounts(&mut self, accounts: impl IntoIterator<Item = Address>)
508    where
509        Self: Sized,
510    {
511        for acc in accounts {
512            self.add_persistent_account(acc);
513        }
514    }
515
516    /// Grants cheatcode access for the given `account`
517    ///
518    /// Returns true if the `account` already has access
519    fn allow_cheatcode_access(&mut self, account: Address) -> bool;
520
521    /// Revokes cheatcode access for the given account
522    ///
523    /// Returns true if the `account` was previously allowed cheatcode access
524    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool;
525
526    /// Returns `true` if the given account is allowed to execute cheatcodes
527    fn has_cheatcode_access(&self, account: &Address) -> bool;
528
529    /// Ensures that `account` is allowed to execute cheatcodes
530    ///
531    /// Returns an error if [`Self::has_cheatcode_access`] returns `false`
532    fn ensure_cheatcode_access(&self, account: &Address) -> Result<(), BackendError> {
533        if !self.has_cheatcode_access(account) {
534            return Err(BackendError::NoCheats(*account));
535        }
536        Ok(())
537    }
538
539    /// Same as [`Self::ensure_cheatcode_access()`] but only enforces it if the backend is currently
540    /// in forking mode
541    fn ensure_cheatcode_access_forking_mode(&self, account: &Address) -> Result<(), BackendError> {
542        if self.is_forked_mode() {
543            return self.ensure_cheatcode_access(account);
544        }
545        Ok(())
546    }
547
548    /// Set the blockhash for a given block number.
549    ///
550    /// # Arguments
551    ///
552    /// * `number` - The block number to set the blockhash for
553    /// * `hash` - The blockhash to set
554    ///
555    /// # Note
556    ///
557    /// This function mimics the EVM limits of the `blockhash` operation:
558    /// - It sets the blockhash for blocks where `block.number - 256 <= number < block.number`
559    /// - Setting a blockhash for the current block (number == block.number) has no effect
560    /// - Setting a blockhash for future blocks (number > block.number) has no effect
561    /// - Setting a blockhash for blocks older than `block.number - 256` has no effect
562    fn set_blockhash(&mut self, block_number: U256, block_hash: B256);
563}
564
565/// Provides the underlying `revm::Database` implementation.
566///
567/// A `Backend` can be initialised in two forms:
568///
569/// # 1. Empty in-memory Database
570/// This is the default variant: an empty `revm::Database`
571///
572/// # 2. Forked Database
573/// A `revm::Database` that forks off a remote client
574///
575///
576/// In addition to that we support forking manually on the fly.
577/// Additional forks can be created. Each unique fork is identified by its unique `ForkId`. We treat
578/// forks as unique if they have the same `(endpoint, block number)` pair.
579///
580/// When it comes to testing, it's intended that each contract will use its own `Backend`
581/// (`Backend::clone`). This way each contract uses its own encapsulated evm state. For in-memory
582/// testing, the database is just an owned `revm::InMemoryDB`.
583///
584/// Each `Fork`, identified by a unique id, uses completely separate storage, write operations are
585/// performed only in the fork's own database, `ForkDB`.
586///
587/// A `ForkDB` consists of 2 halves:
588///   - everything fetched from the remote is readonly
589///   - all local changes (instructed by the contract) are written to the backend's `db` and don't
590///     alter the state of the remote client.
591///
592/// # Fork swapping
593///
594/// Multiple "forks" can be created `Backend::create_fork()`, however only 1 can be used by the
595/// `db`. However, their state can be hot-swapped by swapping the read half of `db` from one fork to
596/// another.
597/// When swapping forks (`Backend::select_fork()`) we also update the current `EvmEnv` of the `EVM`
598/// accordingly, so that all `block.*` config values match
599///
600/// When another for is selected [`DatabaseExt::select_fork()`] the entire storage, including
601/// `JournaledState` is swapped, but the storage of the caller's and the test contract account is
602/// _always_ cloned. This way a fork has entirely separate storage but data can still be shared
603/// across fork boundaries via stack and contract variables.
604///
605/// # Snapshotting
606///
607/// A snapshot of the current overall state can be taken at any point in time. A snapshot is
608/// identified by a unique id that's returned when a snapshot is created. A snapshot can only be
609/// reverted _once_. After a successful revert, the same snapshot id cannot be used again. Reverting
610/// a snapshot replaces the current active state with the snapshot state, the snapshot is deleted
611/// afterwards, as well as any snapshots taken after the reverted snapshot, (e.g.: reverting to id
612/// 0x1 will delete snapshots with ids 0x1, 0x2, etc.)
613///
614/// **Note:** State snapshots work across fork-swaps, e.g. if fork `A` is currently active, then a
615/// snapshot is created before fork `B` is selected, then fork `A` will be the active fork again
616/// after reverting the snapshot.
617#[must_use]
618pub struct Backend<FEN: FoundryEvmNetwork = EthEvmNetwork> {
619    /// Active network configuration.
620    // TODO(monad-fen-lifecycle): Remove this after complete select/roll/transact slices move Monad
621    // fork-position interpretation to a concrete owner. Do not replace it with another generic
622    // policy or runtime family flag.
623    networks: NetworkConfigs,
624    /// The access point for managing forks
625    forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
626    // The default in memory db
627    mem_db: FoundryEvmInMemoryDB,
628    /// The journaled_state to use to initialize new forks with
629    ///
630    /// The way [`JournaledState`] works is, that it holds the "hot" accounts loaded from the
631    /// underlying `Database` that feeds the Account and State data to the journaled_state so it
632    /// can apply changes to the state while the EVM executes.
633    ///
634    /// In a way the `JournaledState` is something like a cache that
635    /// 1. check if account is already loaded (hot)
636    /// 2. if not load from the `Database` (this will then retrieve the account via RPC in forking
637    ///    mode)
638    ///
639    /// To properly initialize we store the `JournaledState` before the first fork is selected
640    /// ([`DatabaseExt::select_fork`]).
641    ///
642    /// This will be an empty `JournaledState`, which will be populated with persistent accounts,
643    /// See [`Self::update_fork_db()`].
644    fork_init_journaled_state: JournaledState,
645    /// The currently active fork database
646    ///
647    /// If this is set, then the Backend is currently in forking mode
648    active_fork_ids: Option<(LocalForkId, ForkLookupIndex)>,
649    /// RPC block number exposed while executing a historical transaction in a temporary backend.
650    fork_block_number_override: Option<u64>,
651    /// Optional BAL position for a single transaction against unchanged parent state.
652    bal: Option<BalState>,
653    /// holds additional Backend data
654    inner: BackendInner<FEN>,
655}
656
657impl<FEN: FoundryEvmNetwork> Clone for Backend<FEN> {
658    fn clone(&self) -> Self {
659        Self {
660            networks: self.networks,
661            forks: self.forks.clone(),
662            mem_db: self.mem_db.clone(),
663            fork_init_journaled_state: self.fork_init_journaled_state.clone(),
664            active_fork_ids: self.active_fork_ids,
665            fork_block_number_override: self.fork_block_number_override,
666            bal: self.bal.clone(),
667            inner: self.inner.clone(),
668        }
669    }
670}
671
672impl<FEN: FoundryEvmNetwork> Debug for Backend<FEN> {
673    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674        f.debug_struct("Backend")
675            .field("networks", &self.networks)
676            .field("forks", &self.forks)
677            .field("mem_db", &self.mem_db)
678            .field("fork_init_journaled_state", &self.fork_init_journaled_state)
679            .field("active_fork_ids", &self.active_fork_ids)
680            .field("bal", &self.bal)
681            .field("inner", &self.inner)
682            .finish()
683    }
684}
685
686impl<FEN: FoundryEvmNetwork> Backend<FEN> {
687    /// Creates a new Backend with a spawned multi fork thread.
688    ///
689    /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory
690    /// database.
691    pub fn spawn(fork: Option<CreateFork>) -> eyre::Result<Self> {
692        Self::new(MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(), fork)
693    }
694
695    /// Creates a new instance of `Backend`
696    ///
697    /// If `fork` is `Some` this will use a `fork` database, otherwise with an in-memory
698    /// database.
699    ///
700    /// Prefer using [`spawn`](Self::spawn) instead.
701    pub fn new(
702        forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
703        fork: Option<CreateFork>,
704    ) -> eyre::Result<Self> {
705        trace!(target: "backend", forking_mode=?fork.is_some(), "creating executor backend");
706        // Note: this will take of registering the `fork`
707        let persistent_accounts = AddressSet::from_iter(DEFAULT_PERSISTENT_ACCOUNTS);
708        let inner = BackendInner { persistent_accounts, ..Default::default() };
709
710        let mut backend = Self {
711            networks: NetworkConfigs::default(),
712            forks,
713            mem_db: CacheDB::new(Default::default()),
714            fork_init_journaled_state: inner.new_journaled_state(),
715            active_fork_ids: None,
716            fork_block_number_override: None,
717            bal: None,
718            inner,
719        };
720
721        if let Some(fork) = fork {
722            let ForkResult { id: fork_id, backend: fork, resolved, .. } =
723                backend.forks.create_fork(fork)?;
724            let context = resolved.context();
725            let block = resolved.block();
726            let fork_db = ForkDB::new(fork);
727            let fork_ids = backend.inner.insert_new_fork(
728                fork_id.clone(),
729                block,
730                context.source_chain_id,
731                fork_db,
732                backend.inner.new_journaled_state(),
733            );
734            backend.inner.launched_with_fork = Some((fork_id, fork_ids.0, fork_ids.1));
735            backend.active_fork_ids = Some(fork_ids);
736        }
737
738        trace!(target: "backend", forking_mode=? backend.active_fork_ids.is_some(), "created executor backend");
739
740        Ok(backend)
741    }
742
743    /// Creates a new instance of `Backend` with fork added to the fork database and sets the fork
744    /// as active
745    pub(crate) fn new_with_fork(
746        id: &ForkId,
747        fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
748        journaled_state: JournaledState,
749        networks: NetworkConfigs,
750    ) -> eyre::Result<Self> {
751        Self::new_with_fork_manager(
752            MultiFork::<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>::spawn(),
753            id,
754            fork,
755            journaled_state,
756            networks,
757        )
758    }
759
760    fn new_with_fork_manager(
761        forks: MultiFork<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
762        id: &ForkId,
763        mut fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
764        journaled_state: JournaledState,
765        networks: NetworkConfigs,
766    ) -> eyre::Result<Self> {
767        let mut backend = Self::new(forks, None)?;
768        backend.networks = networks;
769        fork.journaled_state = journaled_state;
770        let fork_ids = backend.inner.insert_fork(id.clone(), fork);
771        backend.inner.launched_with_fork = Some((id.clone(), fork_ids.0, fork_ids.1));
772        backend.active_fork_ids = Some(fork_ids);
773        Ok(backend)
774    }
775
776    /// Creates a new instance with a `BackendDatabase::InMemory` cache layer for the `CacheDB`
777    pub fn clone_empty(&self) -> Self {
778        Self {
779            networks: self.networks,
780            forks: self.forks.clone(),
781            mem_db: CacheDB::new(Default::default()),
782            fork_init_journaled_state: self.inner.new_journaled_state(),
783            active_fork_ids: None,
784            fork_block_number_override: None,
785            bal: None,
786            inner: Default::default(),
787        }
788    }
789
790    /// Returns the active network configuration.
791    pub const fn networks(&self) -> NetworkConfigs {
792        self.networks
793    }
794
795    /// Sets the active network configuration.
796    pub const fn set_networks(&mut self, networks: NetworkConfigs) {
797        self.networks = networks;
798    }
799
800    pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) {
801        if let Some(db) = self.active_fork_db_mut() {
802            db.insert_account_info(address, account)
803        } else {
804            self.mem_db.insert_account_info(address, account)
805        }
806    }
807
808    /// Inserts a value on an account's storage without overriding account info
809    pub fn insert_account_storage(
810        &mut self,
811        address: Address,
812        slot: U256,
813        value: U256,
814    ) -> Result<(), DatabaseError> {
815        if let Some(db) = self.active_fork_db_mut() {
816            db.insert_account_storage(address, slot, value)
817        } else {
818            self.mem_db.insert_account_storage(address, slot, value)
819        }
820    }
821
822    /// Completely replace an account's storage without overriding account info.
823    ///
824    /// When forking, this causes the backend to assume a `0` value for all
825    /// unset storage slots instead of trying to fetch it.
826    pub fn replace_account_storage(
827        &mut self,
828        address: Address,
829        storage: Map<U256, U256>,
830    ) -> Result<(), DatabaseError> {
831        if let Some(db) = self.active_fork_db_mut() {
832            db.replace_account_storage(address, storage.into_iter().collect())
833        } else {
834            self.mem_db.replace_account_storage(address, storage.into_iter().collect())
835        }
836    }
837
838    /// Returns all snapshots created in this backend
839    #[allow(clippy::type_complexity)]
840    pub const fn state_snapshots(
841        &self,
842    ) -> &StateSnapshots<
843        BackendStateSnapshot<
844            BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
845            SpecFor<FEN>,
846            BlockEnvFor<FEN>,
847        >,
848    > {
849        &self.inner.state_snapshots
850    }
851
852    /// Sets the address of the `DSTest` contract that is being executed
853    ///
854    /// This will also mark the caller as persistent and remove the persistent status from the
855    /// previous test contract address
856    ///
857    /// This will also grant cheatcode access to the test account
858    pub fn set_test_contract(&mut self, acc: Address) -> &mut Self {
859        trace!(?acc, "setting test account");
860        self.inner.persistent_accounts.insert(acc);
861        self.inner.cheatcode_access_accounts.insert(acc);
862        self
863    }
864
865    /// Sets the caller address
866    pub fn set_caller(&mut self, acc: Address) -> &mut Self {
867        trace!(?acc, "setting caller account");
868        self.inner.caller = Some(acc);
869        self.inner.cheatcode_access_accounts.insert(acc);
870        self
871    }
872
873    /// Sets the current spec id
874    pub fn set_spec_id(&mut self, spec_id: impl Into<SpecFor<FEN>>) -> &mut Self {
875        self.inner.spec_id = spec_id.into();
876        self
877    }
878
879    /// Returns the set caller address
880    pub const fn caller_address(&self) -> Option<Address> {
881        self.inner.caller
882    }
883
884    /// Failures occurred in state snapshots are tracked when the state snapshot is reverted.
885    ///
886    /// If an error occurs in a restored state snapshot, the test is considered failed.
887    ///
888    /// This returns whether there was a reverted state snapshot that recorded an error.
889    pub const fn has_state_snapshot_failure(&self) -> bool {
890        self.inner.has_state_snapshot_failure
891    }
892
893    /// Sets the state snapshot failure flag.
894    pub const fn set_state_snapshot_failure(&mut self, has_state_snapshot_failure: bool) {
895        self.inner.has_state_snapshot_failure = has_state_snapshot_failure
896    }
897
898    /// When creating or switching forks, we update the AccountInfo of the contract
899    pub(crate) fn update_fork_db(
900        &self,
901        active_journaled_state: &mut JournaledState,
902        target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
903    ) {
904        self.update_fork_db_contracts(
905            self.inner.persistent_accounts.iter().copied(),
906            active_journaled_state,
907            target_fork,
908        )
909    }
910
911    /// Merges the state of all `accounts` from the currently active db into the given `fork`
912    pub(crate) fn update_fork_db_contracts(
913        &self,
914        accounts: impl IntoIterator<Item = Address>,
915        active_journaled_state: &mut JournaledState,
916        target_fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
917    ) {
918        if let Some(db) = self.active_fork_db() {
919            merge_account_data(accounts, db, active_journaled_state, target_fork)
920        } else {
921            merge_account_data(accounts, &self.mem_db, active_journaled_state, target_fork)
922        }
923    }
924
925    /// Returns the memory db used if not in forking mode
926    pub const fn mem_db(&self) -> &FoundryEvmInMemoryDB {
927        &self.mem_db
928    }
929
930    /// Returns true if the `id` is currently active
931    pub fn is_active_fork(&self, id: LocalForkId) -> bool {
932        self.active_fork_ids.map(|(i, _)| i == id).unwrap_or_default()
933    }
934
935    /// Returns `true` if the `Backend` is currently in forking mode
936    pub fn is_in_forking_mode(&self) -> bool {
937        self.active_fork().is_some()
938    }
939
940    /// Returns the currently active `Fork`, if any
941    pub fn active_fork(&self) -> Option<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
942        self.active_fork_ids.map(|(_, idx)| self.inner.get_fork(idx))
943    }
944
945    /// Returns the currently active `Fork`, if any
946    pub fn active_fork_mut(&mut self) -> Option<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
947        self.active_fork_ids.map(|(_, idx)| self.inner.get_fork_mut(idx))
948    }
949
950    /// Returns the currently active `ForkDB`, if any
951    pub fn active_fork_db(&self) -> Option<&ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
952        self.active_fork().map(|f| &f.db)
953    }
954
955    /// Returns the currently active `ForkDB`, if any
956    pub fn active_fork_db_mut(&mut self) -> Option<&mut ForkDB<AnyNetwork, BlockEnvFor<FEN>>> {
957        self.active_fork_mut().map(|f| &mut f.db)
958    }
959
960    /// Returns the current database implementation as a `&dyn` value.
961    pub fn db(&self) -> &dyn Database<Error = DatabaseError> {
962        match self.active_fork_db() {
963            Some(fork_db) => fork_db,
964            None => &self.mem_db,
965        }
966    }
967
968    /// Returns the current database implementation as a `&mut dyn` value.
969    pub fn db_mut(&mut self) -> &mut dyn Database<Error = DatabaseError> {
970        match self.active_fork_ids.map(|(_, idx)| &mut self.inner.get_fork_mut(idx).db) {
971            Some(fork_db) => fork_db,
972            None => &mut self.mem_db,
973        }
974    }
975
976    /// Creates a snapshot of the currently active database
977    pub(crate) fn create_db_snapshot(
978        &self,
979    ) -> BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>> {
980        if let Some((id, idx)) = self.active_fork_ids {
981            let fork = self.inner.get_fork(idx).clone();
982            let fork_id = self.inner.ensure_fork_id(id).cloned().expect("Exists; qed");
983            BackendDatabaseSnapshot::Forked(id, fork_id, idx, Box::new(fork))
984        } else {
985            BackendDatabaseSnapshot::InMemory(self.mem_db.clone())
986        }
987    }
988
989    /// Since each `Fork` tracks logs separately, we need to merge them to get _all_ of them
990    pub fn merged_logs(&self, mut logs: Vec<Log>) -> Vec<Log> {
991        if let Some((_, active)) = self.active_fork_ids {
992            let mut all_logs = Vec::with_capacity(logs.len());
993
994            self.inner
995                .forks
996                .iter()
997                .enumerate()
998                .filter_map(|(idx, f)| f.as_ref().map(|f| (idx, f)))
999                .for_each(|(idx, f)| {
1000                    if idx == active {
1001                        all_logs.append(&mut logs);
1002                    } else {
1003                        all_logs.extend(f.journaled_state.logs.clone())
1004                    }
1005                });
1006            return all_logs;
1007        }
1008
1009        logs
1010    }
1011
1012    /// Initializes settings we need to keep track of.
1013    ///
1014    /// We need to track these mainly to prevent issues when switching between different evms
1015    pub(crate) fn initialize(
1016        &mut self,
1017        spec_id: impl Into<SpecFor<FEN>>,
1018        caller: Address,
1019        tx_kind: TxKind,
1020    ) {
1021        self.set_caller(caller);
1022        self.set_spec_id(spec_id);
1023
1024        let test_contract = match tx_kind {
1025            TxKind::Call(to) => to,
1026            TxKind::Create => {
1027                let nonce =
1028                    self.basic_ref(caller).map(|b| b.unwrap_or_default().nonce).unwrap_or_default();
1029                caller.create(nonce)
1030            }
1031        };
1032        self.set_test_contract(test_contract);
1033    }
1034
1035    /// Executes the configured test call of the `env` without committing state changes.
1036    ///
1037    /// Note: in case there are any cheatcodes executed that modify the environment, this will
1038    /// update the given `env` with the new values.
1039    #[instrument(name = "inspect", level = "debug", skip_all)]
1040    pub fn inspect<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1041        &mut self,
1042        evm_env: &mut EvmEnvFor<FEN>,
1043        tx_env: &mut TxEnvFor<FEN>,
1044        inspector: I,
1045    ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1046        let chain_context = self.chain_context_for_synthetic_transaction(tx_env)?;
1047        self.inspect_with_context(evm_env, tx_env, chain_context, inspector)
1048    }
1049
1050    /// Executes the configured test call with explicit network-specific context.
1051    #[instrument(name = "inspect", level = "debug", skip_all)]
1052    pub fn inspect_with_context<I: for<'db> FoundryInspectorExt<FoundryContextFor<'db, FEN>>>(
1053        &mut self,
1054        evm_env: &mut EvmEnvFor<FEN>,
1055        tx_env: &mut TxEnvFor<FEN>,
1056        chain_context: ChainFor<FEN>,
1057        inspector: I,
1058    ) -> eyre::Result<ResultAndState<HaltReasonFor<FEN>>> {
1059        self.initialize(evm_env.cfg_env.spec, tx_env.caller(), tx_env.kind());
1060        let factory = FEN::EvmFactory::default();
1061        let mut evm =
1062            factory.create_foundry_evm_with_inspector(self, evm_env.to_owned(), inspector);
1063        *evm.chain_mut() = chain_context;
1064        let res = evm.transact(tx_env.clone()).wrap_err("EVM error")?;
1065
1066        *tx_env = evm.tx().clone();
1067        *evm_env = evm.finish().1;
1068
1069        Ok(res)
1070    }
1071
1072    /// Returns true if the address is a precompile
1073    pub fn is_existing_precompile(&self, addr: &Address) -> bool {
1074        self.inner.precompile_addresses().contains(addr)
1075    }
1076
1077    /// Sets the initial journaled state to use when initializing forks
1078    #[inline]
1079    fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
1080        trace!("recording fork init journaled_state");
1081        self.fork_init_journaled_state = journaled_state;
1082    }
1083
1084    /// Cleans up already loaded accounts that would be initialized without the correct data from
1085    /// the fork.
1086    ///
1087    /// It can happen that an account is loaded before the first fork is selected, like
1088    /// `getNonce(addr)`, which will load an empty account by default.
1089    ///
1090    /// This account data then would not match the account data of a fork if it exists.
1091    /// So when the first fork is initialized we replace these accounts with the actual account as
1092    /// it exists on the fork.
1093    fn prepare_init_journal_state(&mut self) -> Result<(), BackendError> {
1094        let loaded_accounts = self
1095            .fork_init_journaled_state
1096            .state
1097            .iter()
1098            .filter(|(addr, _)| {
1099                !self.is_existing_precompile(addr)
1100                    && !self.inner.persistent_accounts.contains(*addr)
1101            })
1102            .map(|(addr, _)| addr)
1103            .copied()
1104            .collect::<Vec<_>>();
1105
1106        for fork in self.inner.forks_iter_mut() {
1107            let mut journaled_state = self.fork_init_journaled_state.clone();
1108            for loaded_account in loaded_accounts.iter().copied() {
1109                trace!(?loaded_account, "replacing account on init");
1110                let init_account =
1111                    journaled_state.state.get_mut(&loaded_account).expect("exists; qed");
1112
1113                // here's an edge case where we need to check if this account has been created, in
1114                // which case we don't need to replace it with the account from the fork because the
1115                // created account takes precedence: for example contract creation in setups
1116                if init_account.is_created() {
1117                    trace!(?loaded_account, "skipping created account");
1118                    continue;
1119                }
1120
1121                // otherwise we need to replace the account's info with the one from the fork's
1122                // database
1123                let fork_account = Database::basic(&mut fork.db, loaded_account)?
1124                    .ok_or(BackendError::MissingAccount(loaded_account))?;
1125                init_account.info = fork_account;
1126            }
1127            fork.journaled_state = journaled_state;
1128        }
1129        Ok(())
1130    }
1131
1132    /// Returns the block numbers required for replaying a transaction
1133    fn get_block_number_and_block_for_transaction(
1134        &self,
1135        id: LocalForkId,
1136        transaction: B256,
1137    ) -> eyre::Result<TransactionForkTarget> {
1138        let fork = self.inner.get_fork_by_id(id)?;
1139        let tx = fork.backend().get_transaction(transaction)?;
1140
1141        // get the block number we need to fork
1142        if let Some(tx_block) = tx.block_number() {
1143            let tx_block_hash = tx
1144                .block_hash()
1145                .ok_or_else(|| eyre::eyre!("mined transaction is missing its block hash"))?;
1146            let block = fork.backend().get_full_block(tx_block_hash)?;
1147            eyre::ensure!(
1148                block.header().number() == tx_block && block.header().hash == tx_block_hash,
1149                "transaction block changed: expected {} ({}), got {} ({})",
1150                tx_block,
1151                tx_block_hash,
1152                block.header().number(),
1153                block.header().hash
1154            );
1155            let position = if let BlockTransactions::Full(transactions) = block.transactions() {
1156                let index = transactions.iter().position(|tx| tx.tx_hash() == transaction);
1157                if self.networks.is_monad() && index.is_none() {
1158                    eyre::bail!(
1159                        "transaction {transaction:?} is missing from block {}",
1160                        block.header().number()
1161                    );
1162                }
1163                index.map(|index| TransactionPosition {
1164                    index,
1165                    #[cfg(feature = "monad")]
1166                    count: transactions.len(),
1167                })
1168            } else {
1169                if self.networks.is_monad() {
1170                    eyre::bail!(
1171                        "block {} does not contain full transactions",
1172                        block.header().number()
1173                    );
1174                }
1175                None
1176            };
1177
1178            // we need to subtract 1 here because we want the state before the transaction
1179            // was mined
1180            let fork_block = BlockNumHash::new(
1181                tx_block.checked_sub(1).ok_or_else(|| {
1182                    eyre::eyre!("cannot replay a transaction in the genesis block")
1183                })?,
1184                block.header().parent_hash(),
1185            );
1186            Ok(TransactionForkTarget { fork_block, transaction: tx, block, mined: true, position })
1187        } else {
1188            if self.networks.is_monad() {
1189                eyre::bail!(
1190                    "transaction {transaction} is pending and has no canonical block context"
1191                );
1192            }
1193            let block = fork.backend().get_full_block(BlockNumberOrTag::Latest)?;
1194
1195            let fork_block = BlockNumHash::new(block.header().number(), block.header().hash);
1196
1197            Ok(TransactionForkTarget {
1198                fork_block,
1199                transaction: tx,
1200                block,
1201                mined: false,
1202                position: None,
1203            })
1204        }
1205    }
1206
1207    /// Converts all transactions in a full RPC block into this backend's transaction environment.
1208    #[cfg(feature = "monad")]
1209    fn full_block_tx_envs(block: &AnyRpcBlock) -> eyre::Result<Vec<TxEnvFor<FEN>>> {
1210        let BlockTransactions::Full(transactions) = block.transactions() else {
1211            eyre::bail!("block {} does not contain full transactions", block.header().number());
1212        };
1213        transactions.iter().map(TxEnvFor::<FEN>::from_any_rpc_transaction).collect()
1214    }
1215
1216    /// Converts a replayable transaction while preserving the established behavior of skipping
1217    /// system envelopes that this build cannot decode.
1218    fn replay_tx_env(tx: &AnyRpcTransaction) -> eyre::Result<Option<TxEnvFor<FEN>>> {
1219        let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1220        if is_system {
1221            #[cfg(not(feature = "monad"))]
1222            return Ok(None);
1223            #[cfg(feature = "monad")]
1224            return Ok(TxEnvFor::<FEN>::from_any_rpc_transaction(tx).ok());
1225        }
1226
1227        TxEnvFor::<FEN>::from_any_rpc_transaction(tx).map(Some)
1228    }
1229
1230    /// Returns the transaction environments needed to construct exact block context.
1231    #[cfg(feature = "monad")]
1232    fn block_context_inputs_from_backend(
1233        backend: &SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
1234        block: &AnyRpcBlock,
1235    ) -> eyre::Result<BlockContext<FEN>> {
1236        let current = Self::full_block_tx_envs(block)?;
1237
1238        let parent_hash = block.header().parent_hash();
1239        let parent_block = if parent_hash.is_zero() {
1240            None
1241        } else {
1242            let parent_number = block.header().number().checked_sub(1).ok_or_else(|| {
1243                eyre::eyre!("genesis block has non-zero parent hash {parent_hash}")
1244            })?;
1245            let parent = backend
1246                .get_full_block(parent_hash)
1247                .wrap_err_with(|| format!("failed to fetch parent block {parent_hash}"))?;
1248            ensure_block_identity(
1249                &parent,
1250                BlockNumHash::new(parent_number, parent_hash),
1251                "parent",
1252            )?;
1253            Some(parent)
1254        };
1255        let parent =
1256            parent_block.as_ref().map(Self::full_block_tx_envs).transpose()?.unwrap_or_default();
1257
1258        let grandparent = if let Some(parent_block) = &parent_block {
1259            let grandparent_hash = parent_block.header().parent_hash();
1260            if grandparent_hash.is_zero() {
1261                Vec::new()
1262            } else {
1263                let block = backend.get_full_block(grandparent_hash).wrap_err_with(|| {
1264                    format!("failed to fetch grandparent block {grandparent_hash}")
1265                })?;
1266                let grandparent_number =
1267                    parent_block.header().number().checked_sub(1).ok_or_else(|| {
1268                        eyre::eyre!("genesis block has non-zero parent hash {grandparent_hash}")
1269                    })?;
1270                ensure_block_identity(
1271                    &block,
1272                    BlockNumHash::new(grandparent_number, grandparent_hash),
1273                    "grandparent",
1274                )?;
1275                Self::full_block_tx_envs(&block)?
1276            }
1277        } else {
1278            Vec::new()
1279        };
1280
1281        Ok(BlockContext::new(grandparent, parent, current))
1282    }
1283
1284    /// Returns the transaction environments needed to construct exact block context for a fork.
1285    #[cfg(feature = "monad")]
1286    fn block_context_inputs(
1287        &self,
1288        id: LocalForkId,
1289        block: &AnyRpcBlock,
1290    ) -> eyre::Result<BlockContext<FEN>> {
1291        let fork = self.inner.get_fork_by_id(id)?;
1292        Self::block_context_inputs_from_backend(fork.backend(), block)
1293    }
1294
1295    /// Builds transaction context for `tx` at a known position in `block_context`.
1296    #[cfg(feature = "monad")]
1297    fn context_for_block_position(
1298        block_context: BlockContext<FEN>,
1299        position: ForkPosition,
1300        tx: &TxEnvFor<FEN>,
1301    ) -> eyre::Result<ChainFor<FEN>> {
1302        let cursor = match position {
1303            ForkPosition::AfterBlock { .. } => block_context.into_child(),
1304            ForkPosition::BeforeTransaction { transaction_index, .. } => {
1305                block_context.before_transaction(transaction_index)?
1306            }
1307        };
1308        Ok(cursor.next_transaction(tx))
1309    }
1310
1311    /// Builds context for a synthetic transaction at a fork's current position.
1312    #[cfg(feature = "monad")]
1313    fn context_for_fork_synthetic_transaction(
1314        &self,
1315        id: LocalForkId,
1316        tx: &TxEnvFor<FEN>,
1317    ) -> eyre::Result<ChainFor<FEN>> {
1318        if !self.networks.is_monad() {
1319            return Ok(ChainFor::<FEN>::for_transaction(tx));
1320        }
1321
1322        let fork = self.inner.get_fork_by_id(id)?;
1323        let (position_block, position) = match fork.position {
1324            position @ (ForkPosition::AfterBlock { block }
1325            | ForkPosition::BeforeTransaction { block, .. }) => (block, position),
1326        };
1327        let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1328            format!(
1329                "failed to fetch fork block {} ({})",
1330                position_block.number, position_block.hash
1331            )
1332        })?;
1333        ensure_block_identity(&block, position_block, "fork")?;
1334        let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1335        Self::context_for_block_position(context, position, tx)
1336    }
1337
1338    /// Returns the block cursor matching the active fork's database position.
1339    #[cfg(feature = "monad")]
1340    pub fn block_context_for_synthetic_transaction(
1341        &self,
1342    ) -> eyre::Result<Option<BlockContext<FEN>>> {
1343        if !self.networks.is_monad() {
1344            return Ok(None);
1345        }
1346        let Some(id) = self.active_fork_id() else {
1347            return Ok(None);
1348        };
1349
1350        let fork = self.inner.get_fork_by_id(id)?;
1351        let (position_block, transaction_index) = match fork.position {
1352            ForkPosition::AfterBlock { block } => (block, None),
1353            ForkPosition::BeforeTransaction { block, transaction_index } => {
1354                (block, Some(transaction_index))
1355            }
1356        };
1357        let block = fork.backend().get_full_block(position_block.hash).wrap_err_with(|| {
1358            format!(
1359                "failed to fetch active fork block {} ({})",
1360                position_block.number, position_block.hash
1361            )
1362        })?;
1363        ensure_block_identity(&block, position_block, "active fork")?;
1364        let context = Self::block_context_inputs_from_backend(fork.backend(), &block)?;
1365
1366        match transaction_index {
1367            Some(index) => context.before_transaction(index).map(Some),
1368            None => Ok(Some(context.into_child())),
1369        }
1370    }
1371
1372    /// Applies replay changes using the targeted fork's chain rather than the active environment.
1373    fn apply_fork_tx_replay_env_changes(
1374        &self,
1375        id: LocalForkId,
1376        evm_env: &mut EvmEnvFor<FEN>,
1377    ) -> eyre::Result<()> {
1378        let fork_id = self.inner.ensure_fork_id(id).cloned()?;
1379        let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1380        self.apply_fork_tx_replay_env_changes_for(&fork_id, source_chain_id, evm_env)
1381    }
1382
1383    /// Applies replay changes using an explicitly staged fork identity.
1384    fn apply_fork_tx_replay_env_changes_for(
1385        &self,
1386        fork_id: &ForkId,
1387        source_chain_id: ChainId,
1388        evm_env: &mut EvmEnvFor<FEN>,
1389    ) -> eyre::Result<()> {
1390        let fork_evm_env = self
1391            .forks
1392            .get_evm_env(fork_id.clone())?
1393            .ok_or_else(|| eyre::eyre!("Requested fork `{fork_id}` does not exist"))?;
1394        evm_env.cfg_env.chain_id = fork_evm_env.cfg_env.chain_id;
1395        apply_chain_specific_tx_replay_env_changes_for_chain(evm_env, source_chain_id);
1396        Ok(())
1397    }
1398
1399    /// Populates a rolled active fork and the outer journal at the new block state.
1400    fn populate_rolled_active_fork(
1401        fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1402        persistent_accounts: &AddressSet,
1403        caller: Option<Address>,
1404        journaled_state: &mut JournaledState,
1405    ) {
1406        let mut persistent_addrs = persistent_accounts.clone();
1407        persistent_addrs.extend(caller);
1408
1409        fork.journaled_state.depth = journaled_state.depth;
1410
1411        for addr in persistent_addrs {
1412            merge_journaled_state_data(addr, journaled_state, &mut fork.journaled_state);
1413        }
1414
1415        for (addr, acc) in &journaled_state.state {
1416            if acc.is_created() && acc.is_touched() {
1417                merge_journaled_state_data(*addr, journaled_state, &mut fork.journaled_state);
1418            } else if !acc.is_created() {
1419                let _ = fork.journaled_state.load_account(&mut fork.db, *addr);
1420            }
1421        }
1422
1423        *journaled_state = fork.journaled_state.clone();
1424    }
1425
1426    /// Reinitializes a rolled active fork before populating its journal at the new block state.
1427    fn reset_rolled_active_fork(
1428        fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1429        fork_init_journaled_state: &JournaledState,
1430        persistent_accounts: &AddressSet,
1431        caller: Option<Address>,
1432        journaled_state: &mut JournaledState,
1433    ) {
1434        fork.journaled_state = fork_init_journaled_state.clone();
1435        Self::populate_rolled_active_fork(fork, persistent_accounts, caller, journaled_state);
1436    }
1437
1438    /// Rolls a fork while preparing active transaction context before publishing the new fork.
1439    fn roll_fork_with_context(
1440        &mut self,
1441        id: Option<LocalForkId>,
1442        block_number: u64,
1443        evm_env: &mut EvmEnvFor<FEN>,
1444        tx_env: Option<&TxEnvFor<FEN>>,
1445        journaled_state: &mut JournaledState,
1446    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1447        trace!(?id, ?block_number, "roll fork");
1448        let id = self.ensure_fork(id)?;
1449        let rolled = self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?;
1450        self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1451    }
1452
1453    fn roll_fork_exact_with_context(
1454        &mut self,
1455        id: LocalForkId,
1456        block: BlockNumHash,
1457        evm_env: &mut EvmEnvFor<FEN>,
1458        tx_env: Option<&TxEnvFor<FEN>>,
1459        journaled_state: &mut JournaledState,
1460    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1461        trace!(?id, ?block, "roll fork to exact block");
1462        let rolled = self.forks.roll_fork_exact(self.inner.ensure_fork_id(id).cloned()?, block)?;
1463        self.apply_rolled_fork_with_context(id, rolled, evm_env, tx_env, journaled_state)
1464    }
1465
1466    fn apply_rolled_fork_with_context(
1467        &mut self,
1468        id: LocalForkId,
1469        rolled: ForkResult<AnyNetwork, SpecFor<FEN>, BlockEnvFor<FEN>>,
1470        evm_env: &mut EvmEnvFor<FEN>,
1471        _tx_env: Option<&TxEnvFor<FEN>>,
1472        journaled_state: &mut JournaledState,
1473    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1474        let ForkResult { id: fork_id, backend, env: fork_env, resolved } = rolled;
1475        let context = resolved.context();
1476        let block = resolved.block();
1477        let _affects_active = self.is_active_fork(id);
1478
1479        #[cfg(feature = "monad")]
1480        let context_update = if _affects_active && let Some(tx) = _tx_env {
1481            let chain_context = if self.networks.is_monad() {
1482                let block_data = backend.get_full_block(block.hash).wrap_err_with(|| {
1483                    format!("failed to fetch rolled fork block {} ({})", block.number, block.hash)
1484                })?;
1485                ensure_block_identity(&block_data, block, "rolled fork")?;
1486                let block_context = Self::block_context_inputs_from_backend(&backend, &block_data)?;
1487                block_context.into_child().next_transaction(tx)
1488            } else {
1489                ChainFor::<FEN>::for_transaction(tx)
1490            };
1491            ContextUpdate::Replace(chain_context)
1492        } else {
1493            ContextUpdate::Unchanged
1494        };
1495        #[cfg(not(feature = "monad"))]
1496        let context_update = std::marker::PhantomData;
1497
1498        // Update the local mapping only after all context fetches and decoding have succeeded.
1499        self.inner.roll_fork(id, fork_id, block, context.source_chain_id, backend)?;
1500
1501        if let Some((active_id, active_idx)) = self.active_fork_ids
1502            && active_id == id
1503        {
1504            let preserved_spec = evm_env.cfg_env.spec;
1505            *evm_env = fork_env;
1506            evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1507
1508            let persistent_accounts = self.inner.persistent_accounts.clone();
1509            let caller = self.inner.caller;
1510            let active = self.inner.get_fork_mut(active_idx);
1511            Self::reset_rolled_active_fork(
1512                active,
1513                &self.fork_init_journaled_state,
1514                &persistent_accounts,
1515                caller,
1516                journaled_state,
1517            );
1518        }
1519
1520        Ok(context_update)
1521    }
1522
1523    /// Rolls a fork to a transaction while reusing one precomputed block context for replay and
1524    /// the final active-fork cursor.
1525    fn roll_fork_to_transaction_with_context(
1526        &mut self,
1527        id: Option<LocalForkId>,
1528        transaction: B256,
1529        evm_env: &mut EvmEnvFor<FEN>,
1530        tx_env: Option<&TxEnvFor<FEN>>,
1531        journaled_state: &mut JournaledState,
1532    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1533        if !self.networks.is_monad() {
1534            return self.roll_fork_to_transaction_inner(
1535                id,
1536                transaction,
1537                evm_env,
1538                tx_env,
1539                journaled_state,
1540            );
1541        }
1542
1543        #[cfg(not(feature = "monad"))]
1544        unreachable!("block context is only required when Monad support is enabled");
1545
1546        #[cfg(feature = "monad")]
1547        {
1548            trace!(?id, ?transaction, "roll fork to transaction");
1549            let id = self.ensure_fork(id)?;
1550            let affects_active = self.is_active_fork(id);
1551            let TransactionForkTarget { fork_block, block, position, .. } =
1552                self.get_block_number_and_block_for_transaction(id, transaction)?;
1553            let position = position.expect("Monad transaction target includes canonical position");
1554            let block_context = self.block_context_inputs(id, &block)?;
1555            let context_update = if affects_active && let Some(tx) = tx_env {
1556                let fork_position = ForkPosition::BeforeTransaction {
1557                    block: BlockNumHash::new(block.header().number(), block.header().hash),
1558                    transaction_index: position.index,
1559                };
1560                ContextUpdate::Replace(Self::context_for_block_position(
1561                    block_context.clone(),
1562                    fork_position,
1563                    tx,
1564                )?)
1565            } else if affects_active {
1566                ContextUpdate::Unchanged
1567            } else {
1568                ContextUpdate::Rebase
1569            };
1570
1571            let current_fork_id = self.inner.ensure_fork_id(id).cloned()?;
1572            let ForkResult { id: fork_id, backend, env: fork_env, resolved } =
1573                self.forks.roll_fork_exact(current_fork_id, fork_block)?;
1574            let staged_fork_journaled_state = if affects_active {
1575                self.fork_init_journaled_state.clone()
1576            } else {
1577                self.inner.get_fork_by_id(id)?.journaled_state.clone()
1578            };
1579            let mut staged_fork = self.inner.stage_fork_roll(
1580                id,
1581                fork_id,
1582                fork_block,
1583                resolved.context().source_chain_id,
1584                backend,
1585                staged_fork_journaled_state,
1586            )?;
1587            let mut staged_evm_env = evm_env.clone();
1588            let mut staged_journaled_state = journaled_state.clone();
1589
1590            if affects_active {
1591                let preserved_spec = staged_evm_env.cfg_env.spec;
1592                staged_evm_env = fork_env;
1593                staged_evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
1594                Self::populate_rolled_active_fork(
1595                    &mut staged_fork.fork,
1596                    &self.inner.persistent_accounts,
1597                    self.inner.caller,
1598                    &mut staged_journaled_state,
1599                );
1600            }
1601
1602            update_env_block::<AnyNetwork, _, _>(
1603                &mut staged_evm_env,
1604                &block,
1605                staged_fork.fork.source_chain_id,
1606                self.networks,
1607            );
1608            let mut replay_env = staged_evm_env.clone();
1609            self.apply_fork_tx_replay_env_changes_for(
1610                &staged_fork.fork_id,
1611                staged_fork.fork.source_chain_id,
1612                &mut replay_env,
1613            )?;
1614            let target = Self::replay_until(
1615                &mut staged_fork.fork,
1616                ReplayInputs {
1617                    fork_id: staged_fork.fork_id.clone(),
1618                    forks: self.forks.clone(),
1619                    evm_env: replay_env,
1620                    networks: self.networks,
1621                },
1622                &block,
1623                Some(&block_context),
1624                transaction,
1625                &mut staged_journaled_state,
1626                &self.inner.persistent_accounts,
1627            )?;
1628            eyre::ensure!(
1629                target.is_some(),
1630                "transaction {transaction:?} is missing from block {}",
1631                block.header().number()
1632            );
1633            staged_fork.fork.position = ForkPosition::BeforeTransaction {
1634                block: BlockNumHash::new(block.header().number(), block.header().hash),
1635                transaction_index: position.index,
1636            };
1637
1638            // Once the handler update is enqueued, all remaining publication is infallible.
1639            self.forks
1640                .update_block_env(staged_fork.fork_id.clone(), staged_evm_env.block_env.clone())?;
1641            self.inner.publish_fork_roll(staged_fork);
1642            *evm_env = staged_evm_env;
1643            *journaled_state = staged_journaled_state;
1644            Ok(context_update)
1645        }
1646    }
1647
1648    /// Performs a transaction-level roll on the provided backend, environment, and journal.
1649    fn roll_fork_to_transaction_inner(
1650        &mut self,
1651        id: Option<LocalForkId>,
1652        transaction: B256,
1653        evm_env: &mut EvmEnvFor<FEN>,
1654        _tx_env: Option<&TxEnvFor<FEN>>,
1655        journaled_state: &mut JournaledState,
1656    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
1657        trace!(?id, ?transaction, "roll fork to transaction");
1658        let id = self.ensure_fork(id)?;
1659        let _affects_active = self.is_active_fork(id);
1660
1661        let TransactionForkTarget { fork_block, block, mined, position, .. } =
1662            self.get_block_number_and_block_for_transaction(id, transaction)?;
1663        #[cfg(feature = "monad")]
1664        let block_context = if self.networks.is_monad() {
1665            Some(self.block_context_inputs(id, &block)?)
1666        } else {
1667            None
1668        };
1669        #[cfg(feature = "monad")]
1670        let context_update = if _affects_active && let Some(tx) = _tx_env {
1671            let chain_context = if let Some(context) = &block_context {
1672                let fork_position = ForkPosition::BeforeTransaction {
1673                    block: BlockNumHash::new(block.header().number(), block.header().hash),
1674                    transaction_index: position
1675                        .expect("Monad transaction target includes canonical position")
1676                        .index,
1677                };
1678                Self::context_for_block_position(context.clone(), fork_position, tx)?
1679            } else {
1680                ChainFor::<FEN>::for_transaction(tx)
1681            };
1682            ContextUpdate::Replace(chain_context)
1683        } else if _affects_active {
1684            ContextUpdate::Unchanged
1685        } else {
1686            ContextUpdate::Rebase
1687        };
1688        #[cfg(not(feature = "monad"))]
1689        let context_update = std::marker::PhantomData;
1690
1691        // The parent roll must not prepare an intermediate synthetic context.
1692        self.roll_fork_exact_with_context(id, fork_block, evm_env, None, journaled_state)?;
1693
1694        let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
1695        update_env_block::<AnyNetwork, _, _>(evm_env, &block, source_chain_id, self.networks);
1696
1697        let mut replay_env = evm_env.clone();
1698        self.apply_fork_tx_replay_env_changes(id, &mut replay_env)?;
1699        let persistent_accounts = self.inner.persistent_accounts.clone();
1700        let target = if mined {
1701            let fork_id = self.inner.ensure_fork_id(id)?.clone();
1702            let forks = self.forks.clone();
1703            let fork = self.inner.get_fork_by_id_mut(id)?;
1704            Self::replay_until(
1705                fork,
1706                ReplayInputs { fork_id, forks, evm_env: replay_env, networks: self.networks },
1707                &block,
1708                #[cfg(feature = "monad")]
1709                block_context.as_ref(),
1710                transaction,
1711                journaled_state,
1712                &persistent_accounts,
1713            )?
1714        } else {
1715            None
1716        };
1717        if target.is_some()
1718            && let Some(position) = position
1719        {
1720            self.inner.get_fork_by_id_mut(id)?.position = ForkPosition::BeforeTransaction {
1721                block: BlockNumHash::new(block.header().number(), block.header().hash),
1722                transaction_index: position.index,
1723            };
1724        }
1725
1726        // Replay uses the staged environment directly. Publish the handler environment only after
1727        // all fallible replay and cursor updates have succeeded.
1728        let fork_id = self.inner.ensure_fork_id(id).cloned().expect("fork was resolved above");
1729        let _ = self.forks.update_block_env(fork_id, evm_env.block_env.clone());
1730
1731        Ok(context_update)
1732    }
1733
1734    /// Replays all the transactions at the forks current block that were mined before the `tx`
1735    ///
1736    /// Returns the _unmined_ transaction that corresponds to the given `tx_hash`
1737    fn replay_until(
1738        fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
1739        replay: ReplayInputs<FEN>,
1740        full_block: &AnyRpcBlock,
1741        #[cfg(feature = "monad")] block_context: Option<&BlockContext<FEN>>,
1742        tx_hash: B256,
1743        journaled_state: &mut JournaledState,
1744        persistent_accounts: &AddressSet,
1745    ) -> eyre::Result<Option<AnyRpcTransaction>> {
1746        let ReplayInputs { fork_id, forks, evm_env, networks } = replay;
1747        trace!(?tx_hash, "replay until transaction");
1748        #[cfg(feature = "monad")]
1749        eyre::ensure!(
1750            !networks.is_monad() || block_context.is_some(),
1751            "block context is required to replay transactions for this network"
1752        );
1753
1754        let BlockTransactions::Full(transactions) = full_block.transactions() else {
1755            eyre::bail!(
1756                "block {} does not contain full transactions",
1757                full_block.header().number()
1758            );
1759        };
1760        let Some(target_index) = transactions.iter().position(|tx| tx.tx_hash() == tx_hash) else {
1761            return Ok(None);
1762        };
1763        if networks.is_monad() {
1764            eyre::ensure!(
1765                fork.position
1766                    .after_transaction(
1767                        BlockNumHash::new(full_block.header().number(), full_block.header().hash),
1768                        full_block.header().parent_hash(),
1769                        0,
1770                        transactions.len(),
1771                    )
1772                    .is_some(),
1773                "block {} does not immediately follow the active fork position",
1774                full_block.header().number()
1775            );
1776        }
1777        let target_tx = transactions[target_index].clone();
1778        let factory = FEN::EvmFactory::default();
1779        let mut txs_to_replay = Vec::with_capacity(target_index);
1780        for (index, tx) in transactions[..target_index].iter().enumerate() {
1781            let Some(tx_env) = Self::replay_tx_env(tx)? else { continue };
1782            let is_system = is_known_system_sender(tx.from()) || tx.ty() == SYSTEM_TRANSACTION_TYPE;
1783            txs_to_replay.push((index, tx.clone(), tx_env, is_system));
1784        }
1785
1786        // Replay all preceding transactions against a cloned ForkDB.
1787        if !txs_to_replay.is_empty() {
1788            let now = Instant::now();
1789
1790            // Stage the prefix against one cloned fork cache. The temporary backend also
1791            // supplies the existing DatabaseExt boundary needed by nested execution.
1792            let chain_id = evm_env.cfg_env.chain_id;
1793            let timestamp = evm_env.block_env.timestamp().saturating_to();
1794            let mut replay_backend = Self::new_with_fork_manager(
1795                forks,
1796                &fork_id,
1797                fork.clone(),
1798                journaled_state.clone(),
1799                networks,
1800            )?;
1801
1802            #[cfg(feature = "monad")]
1803            if let Some(context) = block_context {
1804                for (index, tx, tx_env, is_system) in &txs_to_replay {
1805                    let mut evm = factory.create_nested_evm(&mut replay_backend, evm_env.clone());
1806                    *evm.chain_mut() = context.transaction(*index);
1807                    inject_replay_precompiles(networks, evm.precompiles_mut(), chain_id, timestamp);
1808                    trace!(tx=?tx.tx_hash(), "committing transaction");
1809                    let result = evm
1810                        .transact_replay(tx_env.clone(), *is_system)
1811                        .wrap_err("backend: failed replaying transaction")?;
1812                    drop(evm);
1813                    if let Some(result) = result {
1814                        replay_backend.commit(result.state);
1815                    }
1816                }
1817            }
1818            #[cfg(feature = "monad")]
1819            let replay_without_context = block_context.is_none();
1820            #[cfg(not(feature = "monad"))]
1821            let replay_without_context = true;
1822            if replay_without_context {
1823                // Keep one regular Alloy EVM for ordinary transactions. Only system envelopes
1824                // need the nested replay operation; it borrows the same staged database.
1825                let mut evm = factory.create_evm(&mut replay_backend, evm_env.clone());
1826                inject_replay_precompiles(networks, evm.precompiles_mut(), chain_id, timestamp);
1827                for (_, tx, tx_env, is_system) in &txs_to_replay {
1828                    trace!(tx=?tx.tx_hash(), "committing transaction");
1829                    let state = if *is_system {
1830                        let mut replay =
1831                            factory.create_nested_evm(&mut **evm.db_mut(), evm_env.clone());
1832                        inject_replay_precompiles(
1833                            networks,
1834                            replay.precompiles_mut(),
1835                            chain_id,
1836                            timestamp,
1837                        );
1838                        let Some(result) = replay
1839                            .transact_replay(tx_env.clone(), true)
1840                            .wrap_err("backend: failed replaying system transaction")?
1841                        else {
1842                            continue;
1843                        };
1844                        result.state
1845                    } else {
1846                        evm.transact(tx_env.clone())
1847                            .wrap_err("backend: failed replaying transaction")?
1848                            .state
1849                    };
1850                    evm.db_mut().commit(state);
1851                }
1852            }
1853
1854            let (_, index) = replay_backend.active_fork_ids.expect("replay fork is active");
1855            fork.db = replay_backend.inner.take_fork(index).db;
1856
1857            // Refresh journaled states from the updated database, preserving persistent
1858            // accounts (cheatcode address, CREATE2 deployer, test contract, etc.).
1859            fork.refresh_journaled_states(journaled_state, persistent_accounts)?;
1860
1861            trace!(elapsed=?now.elapsed(), count=txs_to_replay.len(), "replayed transactions");
1862        }
1863
1864        Ok(Some(target_tx))
1865    }
1866}
1867
1868#[cfg(feature = "monad")]
1869fn ensure_block_identity(
1870    block: &AnyRpcBlock,
1871    expected: BlockNumHash,
1872    relation: &str,
1873) -> eyre::Result<()> {
1874    eyre::ensure!(
1875        block.header().number() == expected.number && block.header().hash == expected.hash,
1876        "{relation} block changed: expected {} ({}), got {} ({})",
1877        expected.number,
1878        expected.hash,
1879        block.header().number(),
1880        block.header().hash
1881    );
1882    Ok(())
1883}
1884
1885impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
1886    fn chain_context_for_synthetic_transaction(
1887        &self,
1888        tx: &TxEnvFor<FEN>,
1889    ) -> eyre::Result<ChainFor<FEN>> {
1890        #[cfg(feature = "monad")]
1891        if let Some(context) = self.block_context_for_synthetic_transaction()? {
1892            return Ok(context.next_transaction(tx));
1893        }
1894        Ok(ChainFor::<FEN>::for_transaction(tx))
1895    }
1896
1897    fn snapshot_state(
1898        &mut self,
1899        journaled_state: &JournaledState,
1900        evm_env: &EvmEnvFor<FEN>,
1901    ) -> U256 {
1902        trace!("create snapshot");
1903        let id = self.inner.state_snapshots.insert(BackendStateSnapshot::new(
1904            self.create_db_snapshot(),
1905            journaled_state.clone(),
1906            evm_env.clone(),
1907        ));
1908        trace!(target: "backend", "Created new snapshot {}", id);
1909        id
1910    }
1911
1912    fn revert_state(
1913        &mut self,
1914        id: U256,
1915        current_state: &JournaledState,
1916        evm_env: &mut EvmEnvFor<FEN>,
1917        caller: Address,
1918        action: RevertStateSnapshotAction,
1919    ) -> Option<JournaledState> {
1920        trace!(?id, "revert snapshot");
1921        if let Some(mut snapshot) = self.inner.state_snapshots.remove_at(id) {
1922            // Re-insert snapshot to persist it
1923            if action.is_keep() {
1924                self.inner.state_snapshots.insert_at(snapshot.clone(), id);
1925            }
1926
1927            // https://github.com/foundry-rs/foundry/issues/3055
1928            // Check if an error occurred either during or before the snapshot.
1929            // DSTest contracts don't have snapshot functionality, so this slot is enough to check
1930            // for failure here.
1931            if let Some(account) = current_state.state.get(&CHEATCODE_ADDRESS)
1932                && let Some(slot) = account.storage.get(&GLOBAL_FAIL_SLOT)
1933                && !slot.present_value.is_zero()
1934            {
1935                self.set_state_snapshot_failure(true);
1936            }
1937
1938            // merge additional logs
1939            snapshot.merge(current_state);
1940            let BackendStateSnapshot { db, mut journaled_state, snap_evm_env } = snapshot;
1941            match db {
1942                BackendDatabaseSnapshot::InMemory(mem_db) => {
1943                    self.mem_db = mem_db;
1944                }
1945                BackendDatabaseSnapshot::Forked(id, fork_id, idx, mut fork) => {
1946                    // there might be the case where the snapshot was created during `setUp` with
1947                    // another caller, so we need to ensure the caller account is present in the
1948                    // journaled state and database
1949                    journaled_state.state.entry(caller).or_insert_with(|| {
1950                        let caller_account = current_state
1951                            .state
1952                            .get(&caller)
1953                            .map(|acc| acc.info.clone())
1954                            .unwrap_or_default();
1955
1956                        if !fork.db.cache.accounts.contains_key(&caller) {
1957                            // update the caller account which is required by the evm
1958                            fork.db.insert_account_info(caller, caller_account.clone());
1959                        }
1960                        caller_account.into()
1961                    });
1962                    self.inner.revert_state_snapshot(id, fork_id, idx, *fork);
1963                    self.active_fork_ids = Some((id, idx))
1964                }
1965            }
1966
1967            *evm_env = snap_evm_env;
1968            trace!(target: "backend", "Reverted snapshot {}", id);
1969
1970            Some(journaled_state)
1971        } else {
1972            warn!(target: "backend", "No snapshot to revert for {}", id);
1973            None
1974        }
1975    }
1976
1977    fn delete_state_snapshot(&mut self, id: U256) -> bool {
1978        self.inner.state_snapshots.remove_at(id).is_some()
1979    }
1980
1981    fn delete_state_snapshots(&mut self) {
1982        self.inner.state_snapshots.clear()
1983    }
1984
1985    fn create_fork(&mut self, create_fork: CreateFork) -> eyre::Result<LocalForkId> {
1986        trace!("create fork");
1987        let ForkResult { id: fork_id, backend: fork, resolved, .. } =
1988            self.forks.create_fork(create_fork)?;
1989        let context = resolved.context();
1990        let block = resolved.block();
1991        let fork_db = ForkDB::new(fork);
1992        let (id, _) = self.inner.insert_new_fork(
1993            fork_id,
1994            block,
1995            context.source_chain_id,
1996            fork_db,
1997            self.fork_init_journaled_state.clone(),
1998        );
1999        Ok(id)
2000    }
2001
2002    fn create_fork_at_transaction(
2003        &mut self,
2004        fork: CreateFork,
2005        transaction: B256,
2006    ) -> eyre::Result<LocalForkId> {
2007        trace!(?transaction, "create fork at transaction");
2008        let id = self.create_fork(fork)?;
2009        let fork_id = self.ensure_fork_id(id).cloned()?;
2010        let mut evm_env = self
2011            .forks
2012            .get_evm_env(fork_id)?
2013            .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2014
2015        // we still need to roll to the transaction, but we only need an empty dummy state since we
2016        // don't need to update the active journaled state yet
2017        self.roll_fork_to_transaction_with_context(
2018            Some(id),
2019            transaction,
2020            &mut evm_env,
2021            None,
2022            &mut self.inner.new_journaled_state(),
2023        )?;
2024
2025        Ok(id)
2026    }
2027
2028    /// Select an existing fork by id.
2029    /// When switching forks we copy the shared state
2030    fn select_fork(
2031        &mut self,
2032        id: LocalForkId,
2033        evm_env: &mut EvmEnvFor<FEN>,
2034        tx_env: &mut TxEnvFor<FEN>,
2035        active_journaled_state: &mut JournaledState,
2036    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2037        trace!(?id, "select fork");
2038        if self.is_active_fork(id) {
2039            // nothing to do
2040            #[cfg(feature = "monad")]
2041            return Ok(ContextUpdate::Unchanged);
2042            #[cfg(not(feature = "monad"))]
2043            return Ok(std::marker::PhantomData);
2044        }
2045
2046        #[cfg(feature = "monad")]
2047        let chain_context = self.context_for_fork_synthetic_transaction(id, tx_env)?;
2048
2049        // Update block number and timestamp of active fork (if any) with current env values,
2050        // in order to preserve values changed by using `roll` and `warp` cheatcodes.
2051        if let Some(active_fork_id) = self.active_fork_id() {
2052            self.forks.update_block(
2053                self.ensure_fork_id(active_fork_id).cloned()?,
2054                evm_env.block_env.number(),
2055                evm_env.block_env.timestamp(),
2056            )?;
2057        }
2058
2059        let fork_id = self.ensure_fork_id(id).cloned()?;
2060        let idx = self.inner.ensure_fork_index(&fork_id)?;
2061        let fork_evm_env = self
2062            .forks
2063            .get_evm_env(fork_id)?
2064            .ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exist", id))?;
2065
2066        // If we're currently in forking mode we need to update the journaled_state to this point,
2067        // this ensures the changes performed while the fork was active are recorded
2068        if let Some(active) = self.active_fork_mut() {
2069            active.journaled_state = active_journaled_state.clone();
2070
2071            let caller = tx_env.caller();
2072            let caller_account = active.journaled_state.state.get(&caller).cloned();
2073            let target_fork = self.inner.get_fork_mut(idx);
2074
2075            // depth 0 will be the default value when the fork was created
2076            if target_fork.journaled_state.depth == 0 {
2077                // Initialize caller with its fork info
2078                if let Some(mut acc) = caller_account {
2079                    let fork_account = Database::basic(&mut target_fork.db, caller)?
2080                        .ok_or(BackendError::MissingAccount(caller))?;
2081
2082                    acc.info = fork_account;
2083                    target_fork.journaled_state.state.insert(caller, acc);
2084                }
2085            }
2086        } else {
2087            // this is the first time a fork is selected. This means up to this point all changes
2088            // are made in a single `JournaledState`, for example after a `setup` that only created
2089            // different forks. Since the `JournaledState` is valid for all forks until the
2090            // first fork is selected, we need to update it for all forks and use it as init state
2091            // for all future forks
2092
2093            self.set_init_journaled_state(active_journaled_state.clone());
2094            self.prepare_init_journal_state()?;
2095
2096            // Make sure that the next created fork has a depth of 0.
2097            self.fork_init_journaled_state.depth = 0;
2098        }
2099
2100        {
2101            // update the shared state and track
2102            let mut fork = self.inner.take_fork(idx);
2103
2104            // Make sure all persistent accounts on the newly selected fork reflect same state as
2105            // the active db / previous fork.
2106            // This can get out of sync when multiple forks are created on test `setUp`, then a
2107            // fork is selected and persistent contract is changed. If first action in test is to
2108            // select a different fork, then the persistent contract state won't reflect changes
2109            // done in `setUp` for the other fork.
2110            // See <https://github.com/foundry-rs/foundry/issues/10296> and <https://github.com/foundry-rs/foundry/issues/10552>.
2111            let persistent_accounts = self.inner.persistent_accounts.clone();
2112            if let Some(db) = self.active_fork_db_mut() {
2113                for addr in persistent_accounts {
2114                    let Ok(db_account) = db.load_account(addr) else { continue };
2115
2116                    let Some(fork_account) = fork.journaled_state.state.get_mut(&addr) else {
2117                        continue;
2118                    };
2119
2120                    for (key, val) in &db_account.storage {
2121                        if let Some(fork_storage) = fork_account.storage.get_mut(key) {
2122                            fork_storage.present_value = *val;
2123                        }
2124                    }
2125                }
2126            }
2127
2128            // since all forks handle their state separately, the depth can drift
2129            // this is a handover where the target fork starts at the same depth where it was
2130            // selected. This ensures that there are no gaps in depth which would
2131            // otherwise cause issues with the tracer
2132            fork.journaled_state.depth = active_journaled_state.depth;
2133
2134            // another edge case where a fork is created and selected during setup with not
2135            // necessarily the same caller as for the test, however we must always
2136            // ensure that fork's state contains the current sender
2137            let caller = tx_env.caller();
2138            fork.journaled_state.state.entry(caller).or_insert_with(|| {
2139                let caller_account = active_journaled_state
2140                    .state
2141                    .get(&caller)
2142                    .map(|acc| acc.info.clone())
2143                    .unwrap_or_default();
2144
2145                if !fork.db.cache.accounts.contains_key(&caller) {
2146                    // update the caller account which is required by the evm
2147                    fork.db.insert_account_info(caller, caller_account.clone());
2148                }
2149                caller_account.into()
2150            });
2151
2152            self.update_fork_db(active_journaled_state, &mut fork);
2153
2154            // insert the fork back
2155            self.inner.set_fork(idx, fork);
2156        }
2157
2158        self.active_fork_ids = Some((id, idx));
2159        // Update current environment with environment of newly selected fork.
2160        // Preserve the configured spec (evm_version) from the current environment — the fork's
2161        // evm_env is built with SPEC::default() and must not override the user's hardfork setting.
2162        let preserved_spec = evm_env.cfg_env.spec;
2163        tx_env.set_chain_id(Some(fork_evm_env.cfg_env.chain_id));
2164        *evm_env = fork_evm_env;
2165        evm_env.cfg_env.set_spec_and_mainnet_gas_params(preserved_spec);
2166
2167        #[cfg(feature = "monad")]
2168        return Ok(ContextUpdate::Replace(chain_context));
2169        #[cfg(not(feature = "monad"))]
2170        Ok(std::marker::PhantomData)
2171    }
2172
2173    /// This is effectively the same as [`Self::create_select_fork()`] but updating an existing
2174    /// [ForkId] that is mapped to the [LocalForkId]
2175    fn roll_fork(
2176        &mut self,
2177        id: Option<LocalForkId>,
2178        block_number: u64,
2179        evm_env: &mut EvmEnvFor<FEN>,
2180        tx_env: &TxEnvFor<FEN>,
2181        journaled_state: &mut JournaledState,
2182    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2183        self.roll_fork_with_context(id, block_number, evm_env, Some(tx_env), journaled_state)
2184    }
2185
2186    fn roll_fork_to_transaction(
2187        &mut self,
2188        id: Option<LocalForkId>,
2189        transaction: B256,
2190        evm_env: &mut EvmEnvFor<FEN>,
2191        tx_env: &TxEnvFor<FEN>,
2192        journaled_state: &mut JournaledState,
2193    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2194        self.roll_fork_to_transaction_with_context(
2195            id,
2196            transaction,
2197            evm_env,
2198            Some(tx_env),
2199            journaled_state,
2200        )
2201    }
2202
2203    fn transact(
2204        &mut self,
2205        maybe_id: Option<LocalForkId>,
2206        transaction: B256,
2207        mut evm_env: EvmEnvFor<FEN>,
2208        _outer_tx_env: &TxEnvFor<FEN>,
2209        journaled_state: &mut JournaledState,
2210        inspector: &mut dyn for<'db> FoundryInspectorExt<
2211            <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2212        >,
2213    ) -> eyre::Result<ContextUpdateFor<FEN::EvmFactory>> {
2214        trace!(?maybe_id, ?transaction, "execute transaction");
2215        let persistent_accounts = self.inner.persistent_accounts.clone();
2216        let id = self.ensure_fork(maybe_id)?;
2217        let _affects_active = self.is_active_fork(id);
2218        let fork_id = self.ensure_fork_id(id).cloned()?;
2219
2220        // This is a bit ambiguous because the user wants to transact an arbitrary transaction in
2221        // the current context, but we're assuming the user wants to transact the transaction as it
2222        // was mined. Usually this is used in a combination of a fork at the transaction's parent
2223        // transaction in the block and then the transaction is transacted:
2224        // <https://github.com/foundry-rs/foundry/issues/6538>
2225        // So we modify the env to match the transaction's block.
2226        let TransactionForkTarget {
2227            transaction: tx,
2228            block,
2229            #[cfg(feature = "monad")]
2230            position,
2231            ..
2232        } = self.get_block_number_and_block_for_transaction(id, transaction)?;
2233        let tx_env = TxEnvFor::<FEN>::from_any_rpc_transaction(&tx)?;
2234        let source_chain_id = self.inner.get_fork_by_id(id)?.source_chain_id;
2235        update_env_block::<AnyNetwork, _, _>(&mut evm_env, &block, source_chain_id, self.networks);
2236        self.apply_fork_tx_replay_env_changes(id, &mut evm_env)?;
2237
2238        #[cfg(feature = "monad")]
2239        let block_context = if self.networks.is_monad() {
2240            Some(self.block_context_inputs(id, &block)?)
2241        } else {
2242            None
2243        };
2244        #[cfg(feature = "monad")]
2245        let chain_context = if let Some(context) = &block_context {
2246            context.transaction(
2247                position.expect("Monad transaction target includes canonical position").index,
2248            )
2249        } else {
2250            ChainFor::<FEN>::for_transaction(&tx_env)
2251        };
2252
2253        #[cfg(not(feature = "monad"))]
2254        let chain_context = ChainFor::<FEN>::for_transaction(&tx_env);
2255
2256        #[cfg(feature = "monad")]
2257        let next_position = if block_context.is_some() {
2258            let position = position.expect("Monad transaction target includes canonical position");
2259            Some(
2260                self.inner
2261                    .get_fork_by_id(id)?
2262                    .position
2263                    .after_transaction(
2264                        BlockNumHash::new(block.header().number(), block.header().hash),
2265                        block.header().parent_hash(),
2266                        position.index,
2267                        position.count,
2268                    )
2269                    .ok_or_else(|| {
2270                        eyre::eyre!(
2271                            "transaction {transaction} does not immediately follow the active \
2272                             fork position"
2273                        )
2274                    })?,
2275            )
2276        } else {
2277            None
2278        };
2279        #[cfg(feature = "monad")]
2280        let context_update = if _affects_active {
2281            ContextUpdate::Replace(if let Some(context) = block_context {
2282                Self::context_for_block_position(
2283                    context,
2284                    next_position.expect("block context has a next position"),
2285                    _outer_tx_env,
2286                )?
2287            } else {
2288                ChainFor::<FEN>::for_transaction(_outer_tx_env)
2289            })
2290        } else {
2291            ContextUpdate::Rebase
2292        };
2293        #[cfg(not(feature = "monad"))]
2294        let context_update = std::marker::PhantomData;
2295
2296        let fork = self.inner.get_fork_by_id_mut(id)?;
2297        commit_transaction::<FEN>(
2298            TransactionInputs {
2299                evm_env,
2300                tx_env,
2301                chain_context,
2302                rpc_block_number: block.header().number(),
2303            },
2304            journaled_state,
2305            fork,
2306            &fork_id,
2307            self.networks,
2308            &persistent_accounts,
2309            inspector,
2310        )?;
2311        #[cfg(feature = "monad")]
2312        if let Some(position) = next_position {
2313            fork.position = position;
2314        }
2315        Ok(context_update)
2316    }
2317
2318    fn transact_from_tx(
2319        &mut self,
2320        tx_env: TxEnvFor<FEN>,
2321        evm_env: EvmEnvFor<FEN>,
2322        journaled_state: &mut JournaledState,
2323        inspector: &mut dyn for<'db> FoundryInspectorExt<
2324            <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
2325        >,
2326    ) -> eyre::Result<()> {
2327        trace!("execute signed transaction");
2328
2329        self.commit(journaled_state.state.clone());
2330
2331        let res = {
2332            let mut db = self.clone();
2333            let depth = journaled_state.depth + 1;
2334            let factory = FEN::EvmFactory::default();
2335            let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
2336            let mut evm = factory.create_nested_evm_with_inspector(&mut db, evm_env, inspector);
2337            *evm.chain_mut() = chain_context;
2338            evm.journal_inner_mut().depth = depth;
2339            evm.transact_raw(tx_env)?
2340        };
2341
2342        self.commit(res.state);
2343        update_state(&mut journaled_state.state, self, None)?;
2344
2345        Ok(())
2346    }
2347
2348    fn active_fork_id(&self) -> Option<LocalForkId> {
2349        self.active_fork_ids.map(|(id, _)| id)
2350    }
2351
2352    fn active_fork_url(&self) -> Option<String> {
2353        let fork = self.inner.issued_local_fork_ids.get(&self.active_fork_id()?)?;
2354        self.forks.get_fork_url(fork.clone()).ok()?
2355    }
2356
2357    fn active_fork_source_chain_id(&self) -> Option<u64> {
2358        Some(self.inner.get_fork_by_id(self.active_fork_id()?).ok()?.source_chain_id)
2359    }
2360
2361    fn active_fork_block_number(&self) -> Option<u64> {
2362        if let Some(block_number) = self.fork_block_number_override {
2363            return Some(block_number);
2364        }
2365        let fork = self.inner.get_fork_by_id(self.active_fork_id()?).ok()?;
2366        Some(match fork.position {
2367            ForkPosition::AfterBlock { block } | ForkPosition::BeforeTransaction { block, .. } => {
2368                block.number
2369            }
2370        })
2371    }
2372
2373    fn ensure_fork(&self, id: Option<LocalForkId>) -> eyre::Result<LocalForkId> {
2374        if let Some(id) = id {
2375            if self.inner.issued_local_fork_ids.contains_key(&id) {
2376                return Ok(id);
2377            }
2378            eyre::bail!("Requested fork `{}` does not exist", id);
2379        }
2380        if let Some(id) = self.active_fork_id() {
2381            Ok(id)
2382        } else {
2383            eyre::bail!("No fork active");
2384        }
2385    }
2386
2387    fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2388        self.inner.ensure_fork_id(id)
2389    }
2390
2391    fn diagnose_revert(&self, callee: Address, evm_state: &EvmState) -> Option<RevertDiagnostic> {
2392        let active_id = self.active_fork_id()?;
2393        let active_fork = self.active_fork()?;
2394
2395        if self.inner.forks.len() == 1 {
2396            // we only want to provide additional diagnostics here when in multifork mode with > 1
2397            // forks
2398            return None;
2399        }
2400
2401        if !active_fork.is_contract(callee) && !is_contract_in_state(evm_state, callee) {
2402            // no contract for `callee` available on current fork, check if available on other forks
2403            let mut available_on = Vec::new();
2404            for (id, fork) in self.inner.forks_iter().filter(|(id, _)| *id != active_id) {
2405                trace!(?id, address=?callee, "checking if account exists");
2406                if fork.is_contract(callee) {
2407                    available_on.push(id);
2408                }
2409            }
2410
2411            return if available_on.is_empty() {
2412                Some(RevertDiagnostic::ContractDoesNotExist {
2413                    contract: callee,
2414                    active: active_id,
2415                    persistent: self.is_persistent(&callee),
2416                })
2417            } else {
2418                // likely user error: called a contract that's not available on active fork but is
2419                // present other forks
2420                Some(RevertDiagnostic::ContractExistsOnOtherForks {
2421                    contract: callee,
2422                    active: active_id,
2423                    available_on,
2424                })
2425            };
2426        }
2427        None
2428    }
2429
2430    /// Loads the account allocs from the given `allocs` map into the passed [JournaledState].
2431    ///
2432    /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise.
2433    fn load_allocs(
2434        &mut self,
2435        allocs: &BTreeMap<Address, GenesisAccount>,
2436        journaled_state: &mut JournaledState,
2437    ) -> Result<(), BackendError> {
2438        // Loop through all of the allocs defined in the map and commit them to the journal.
2439        for (addr, acc) in allocs {
2440            self.clone_account(acc, addr, journaled_state)?;
2441        }
2442
2443        Ok(())
2444    }
2445
2446    /// Copies bytecode, storage, nonce and balance from the given genesis account to the target
2447    /// address.
2448    ///
2449    /// Returns [Ok] if data was successfully inserted into the journal, [Err] otherwise.
2450    fn clone_account(
2451        &mut self,
2452        source: &GenesisAccount,
2453        target: &Address,
2454        journaled_state: &mut JournaledState,
2455    ) -> Result<(), BackendError> {
2456        // Fetch the account from the journaled state. Will create a new account if it does
2457        // not already exist.
2458        let mut state_acc = journaled_state.load_account_mut(self, *target)?;
2459
2460        // Set the account's bytecode and code hash, if the `bytecode` field is present.
2461        if let Some(bytecode) = source.code.as_ref() {
2462            let bytecode_hash = keccak256(bytecode);
2463            let bytecode = Bytecode::new_raw(bytecode.0.clone().into());
2464            state_acc.set_code(bytecode_hash, bytecode);
2465        }
2466
2467        // Set the account's balance.
2468        state_acc.set_balance(source.balance);
2469
2470        // Set the account's storage, if the `storage` field is present.
2471        if let Some(acc) = journaled_state.state.get_mut(target) {
2472            if let Some(storage) = source.storage.as_ref() {
2473                for (slot, value) in storage {
2474                    let slot = U256::from_be_bytes(slot.0);
2475                    acc.storage.insert(
2476                        slot,
2477                        EvmStorageSlot::new_changed(
2478                            acc.storage.get(&slot).map(|s| s.present_value).unwrap_or_default(),
2479                            U256::from_be_bytes(value.0),
2480                            TransactionId::ZERO,
2481                        ),
2482                    );
2483                }
2484            }
2485
2486            // Set the account's nonce.
2487            acc.info.nonce = source.nonce.unwrap_or_default();
2488        };
2489
2490        // Touch the account to ensure the loaded information persists if called in `setUp`.
2491        journaled_state.touch(*target);
2492
2493        Ok(())
2494    }
2495
2496    fn add_persistent_account(&mut self, account: Address) -> bool {
2497        trace!(?account, "add persistent account");
2498        self.inner.persistent_accounts.insert(account)
2499    }
2500
2501    fn refresh_fork_account(
2502        &mut self,
2503        address: Address,
2504        field: ForkAccountField,
2505        journaled_state: &mut JournaledState,
2506    ) -> Result<(), BackendError> {
2507        let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2508        trace!(?address, ?field, "refresh fork account");
2509        fork.db.db.data().accounts.write().remove(&address);
2510
2511        let cache_modified = fork
2512            .db
2513            .cache
2514            .accounts
2515            .get(&address)
2516            .is_some_and(|account| account.account_state != AccountState::None);
2517        if !cache_modified {
2518            fork.db.cache.accounts.remove(&address);
2519        }
2520
2521        if !cache_modified
2522            && !journaled_state.state.contains_key(&address)
2523            && !fork.journaled_state.state.contains_key(&address)
2524        {
2525            return Ok(());
2526        }
2527
2528        let mut refreshed = DatabaseRef::basic_ref(&fork.db.db, address)?.unwrap_or_default();
2529        if matches!(field, ForkAccountField::Code) {
2530            fork.db.insert_contract(&mut refreshed);
2531        }
2532        if let Some(cached) = fork.db.cache.accounts.get_mut(&address) {
2533            field.update(&mut cached.info, &refreshed);
2534            if cached.account_state == AccountState::NotExisting && !refreshed.is_empty() {
2535                cached.account_state = AccountState::None;
2536            }
2537        }
2538        if let Some(journaled_account) = journaled_state.state.get_mut(&address) {
2539            field.update(&mut journaled_account.info, &refreshed);
2540        }
2541        if let Some(journaled_account) = fork.journaled_state.state.get_mut(&address) {
2542            field.update(&mut journaled_account.info, &refreshed);
2543        }
2544        Ok(())
2545    }
2546
2547    fn refresh_fork_storage(
2548        &mut self,
2549        address: Address,
2550        slot: U256,
2551        journaled_state: &mut JournaledState,
2552    ) -> Result<(), BackendError> {
2553        let Some(fork) = self.active_fork_mut() else { return Ok(()) };
2554        trace!(?address, ?slot, "refresh fork storage");
2555        if let Some(storage) = fork.db.db.data().storage.write().get_mut(&address) {
2556            storage.remove(&slot);
2557        }
2558        let cache_modified = fork
2559            .db
2560            .cache
2561            .accounts
2562            .get(&address)
2563            .is_some_and(|account| account.account_state != AccountState::None);
2564        if !cache_modified && let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2565            account.storage.remove(&slot);
2566        }
2567
2568        let outer_loaded = journaled_state
2569            .state
2570            .get(&address)
2571            .is_some_and(|account| account.storage.contains_key(&slot));
2572        let fork_loaded = fork
2573            .journaled_state
2574            .state
2575            .get(&address)
2576            .is_some_and(|account| account.storage.contains_key(&slot));
2577        if !cache_modified && !outer_loaded && !fork_loaded {
2578            return Ok(());
2579        }
2580
2581        let value = DatabaseRef::storage_ref(&fork.db.db, address, slot)?;
2582        if let Some(account) = fork.db.cache.accounts.get_mut(&address) {
2583            account.storage.insert(slot, value);
2584            if account.account_state == AccountState::NotExisting && !value.is_zero() {
2585                account.account_state = AccountState::None;
2586            }
2587        }
2588        if let Some(storage) = journaled_state
2589            .state
2590            .get_mut(&address)
2591            .and_then(|account| account.storage.get_mut(&slot))
2592        {
2593            storage.present_value = value;
2594        }
2595        if let Some(storage) = fork
2596            .journaled_state
2597            .state
2598            .get_mut(&address)
2599            .and_then(|account| account.storage.get_mut(&slot))
2600        {
2601            storage.present_value = value;
2602        }
2603        Ok(())
2604    }
2605
2606    fn remove_persistent_account(&mut self, account: &Address) -> bool {
2607        trace!(?account, "remove persistent account");
2608        self.inner.persistent_accounts.remove(account)
2609    }
2610
2611    fn is_persistent(&self, acc: &Address) -> bool {
2612        self.inner.persistent_accounts.contains(acc)
2613    }
2614
2615    fn allow_cheatcode_access(&mut self, account: Address) -> bool {
2616        trace!(?account, "allow cheatcode access");
2617        self.inner.cheatcode_access_accounts.insert(account)
2618    }
2619
2620    fn revoke_cheatcode_access(&mut self, account: &Address) -> bool {
2621        trace!(?account, "revoke cheatcode access");
2622        self.inner.cheatcode_access_accounts.remove(account)
2623    }
2624
2625    fn has_cheatcode_access(&self, account: &Address) -> bool {
2626        self.inner.cheatcode_access_accounts.contains(account)
2627    }
2628
2629    fn set_blockhash(&mut self, block_number: U256, block_hash: B256) {
2630        if let Some(db) = self.active_fork_db_mut() {
2631            db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2632        } else {
2633            self.mem_db.cache.block_hashes.insert(block_number.saturating_to(), block_hash);
2634        }
2635    }
2636}
2637
2638impl<FEN: FoundryEvmNetwork> DatabaseRef for Backend<FEN> {
2639    type Error = DatabaseError;
2640
2641    fn basic_ref(&self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2642        let mut account = if let Some(db) = self.active_fork_db() {
2643            db.basic_ref(address)?
2644        } else {
2645            self.mem_db.basic_ref(address)?
2646        };
2647        self.apply_bal_account(address, &mut account)?;
2648        Ok(account)
2649    }
2650
2651    fn code_by_hash_ref(&self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2652        if let Some(db) = self.active_fork_db() {
2653            db.code_by_hash_ref(code_hash)
2654        } else {
2655            Ok(self.mem_db.code_by_hash_ref(code_hash)?)
2656        }
2657    }
2658
2659    fn storage_ref(&self, address: Address, index: U256) -> Result<U256, Self::Error> {
2660        if let Some(value) = self.bal_storage(address, index)? {
2661            return Ok(value);
2662        }
2663        if let Some(db) = self.active_fork_db() {
2664            DatabaseRef::storage_ref(db, address, index)
2665        } else {
2666            Ok(DatabaseRef::storage_ref(&self.mem_db, address, index)?)
2667        }
2668    }
2669
2670    fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
2671        if let Some(db) = self.active_fork_db() {
2672            db.block_hash_ref(number)
2673        } else {
2674            Ok(self.mem_db.block_hash_ref(number)?)
2675        }
2676    }
2677}
2678
2679impl<FEN: FoundryEvmNetwork> DatabaseCommit for Backend<FEN> {
2680    fn commit(&mut self, changes: AddressMap<Account>) {
2681        if self.bal.take().is_some() {
2682            // Keep code read through BAL available for trace decoding. CacheDB's normal commit
2683            // skips untouched accounts; touched accounts must retain the target's final values.
2684            for (address, account) in &changes {
2685                if !account.is_touched() && !account.is_loaded_as_not_existing() {
2686                    self.insert_account_info(*address, account.info.clone());
2687                }
2688            }
2689        }
2690        if let Some(db) = self.active_fork_db_mut() {
2691            db.commit(changes)
2692        } else {
2693            self.mem_db.commit(changes)
2694        }
2695    }
2696}
2697
2698impl<FEN: FoundryEvmNetwork> Database for Backend<FEN> {
2699    type Error = DatabaseError;
2700    fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
2701        let mut account = if let Some(db) = self.active_fork_db_mut() {
2702            db.basic(address)?
2703        } else {
2704            self.mem_db.basic(address)?
2705        };
2706        self.apply_bal_account(address, &mut account)?;
2707        Ok(account)
2708    }
2709
2710    fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
2711        if let Some(db) = self.active_fork_db_mut() {
2712            Ok(db.code_by_hash(code_hash)?)
2713        } else {
2714            Ok(self.mem_db.code_by_hash(code_hash)?)
2715        }
2716    }
2717
2718    fn storage(&mut self, address: Address, index: U256) -> Result<U256, Self::Error> {
2719        if let Some(value) = self.bal_storage(address, index)? {
2720            return Ok(value);
2721        }
2722        if let Some(db) = self.active_fork_db_mut() {
2723            Ok(Database::storage(db, address, index)?)
2724        } else {
2725            Ok(Database::storage(&mut self.mem_db, address, index)?)
2726        }
2727    }
2728
2729    fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
2730        if let Some(db) = self.active_fork_db_mut() {
2731            Ok(db.block_hash(number)?)
2732        } else {
2733            Ok(self.mem_db.block_hash(number)?)
2734        }
2735    }
2736}
2737
2738/// Variants of a [revm::Database]
2739#[derive(Clone, Debug)]
2740pub enum BackendDatabaseSnapshot<N: Network, B: ForkBlockEnv = BlockEnv> {
2741    /// Simple in-memory [revm::Database]
2742    InMemory(FoundryEvmInMemoryDB),
2743    /// Contains the entire forking mode database
2744    Forked(LocalForkId, ForkId, ForkLookupIndex, Box<Fork<N, B>>),
2745}
2746
2747/// Represents a fork
2748#[derive(Clone, Debug)]
2749pub struct Fork<N: Network, B: ForkBlockEnv = BlockEnv> {
2750    db: ForkDB<N, B>,
2751    journaled_state: JournaledState,
2752    source_chain_id: ChainId,
2753    position: ForkPosition,
2754}
2755
2756impl<N: Network, B: ForkBlockEnv> Fork<N, B> {
2757    /// Returns a reference to the underlying [`SharedBackend`].
2758    pub const fn backend(&self) -> &SharedBackend<N, B> {
2759        &self.db.db
2760    }
2761
2762    /// Returns true if the account is a contract
2763    pub fn is_contract(&self, acc: Address) -> bool {
2764        if let Ok(Some(acc)) = self.db.basic_ref(acc)
2765            && acc.code_hash != KECCAK_EMPTY
2766        {
2767            return true;
2768        }
2769        is_contract_in_state(&self.journaled_state.state, acc)
2770    }
2771
2772    /// Refreshes the given journaled state and the fork's own journaled state from the
2773    /// database, preserving persistent accounts.
2774    fn refresh_journaled_states(
2775        &mut self,
2776        journaled_state: &mut JournaledState,
2777        persistent_accounts: &AddressSet,
2778    ) -> Result<(), BackendError> {
2779        update_state(&mut journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2780        update_state(&mut self.journaled_state.state, &mut self.db, Some(persistent_accounts))?;
2781        Ok(())
2782    }
2783}
2784
2785/// Container type for various Backend related data
2786pub struct BackendInner<FEN: FoundryEvmNetwork> {
2787    /// Stores the `ForkId` of the fork the `Backend` launched with from the start.
2788    ///
2789    /// In other words if [`Backend::spawn()`] was called with a `CreateFork` command, to launch
2790    /// directly in fork mode, this holds the corresponding fork identifier of this fork.
2791    pub launched_with_fork: Option<(ForkId, LocalForkId, ForkLookupIndex)>,
2792    /// This tracks numeric fork ids and the `ForkId` used by the handler.
2793    ///
2794    /// This is necessary, because there can be multiple `Backends` associated with a single
2795    /// `ForkId` which is only a pair of endpoint + block. Since an existing fork can be
2796    /// modified (e.g. `roll_fork`), but this should only affect the fork that's unique for the
2797    /// test and not the `ForkId`
2798    ///
2799    /// This ensures we can treat forks as unique from the context of a test, so rolling to another
2800    /// is basically creating(or reusing) another `ForkId` that's then mapped to the previous
2801    /// issued _local_ numeric identifier, that remains constant, even if the underlying fork
2802    /// backend changes.
2803    pub issued_local_fork_ids: HashMap<LocalForkId, ForkId>,
2804    /// tracks all the created forks
2805    /// Contains the index of the corresponding `ForkDB` in the `forks` vec
2806    pub created_forks: HashMap<ForkId, ForkLookupIndex>,
2807    /// Holds all created fork databases
2808    // Note: data is stored in an `Option` so we can remove it without reshuffling
2809    pub forks: Vec<Option<Fork<AnyNetwork, BlockEnvFor<FEN>>>>,
2810    /// Contains state snapshots made at a certain point
2811    #[allow(clippy::type_complexity)]
2812    pub state_snapshots: StateSnapshots<
2813        BackendStateSnapshot<
2814            BackendDatabaseSnapshot<AnyNetwork, BlockEnvFor<FEN>>,
2815            SpecFor<FEN>,
2816            BlockEnvFor<FEN>,
2817        >,
2818    >,
2819    /// Tracks whether there was a failure in a snapshot that was reverted
2820    ///
2821    /// The Test contract contains a bool variable that is set to true when an `assert` function
2822    /// failed. When a snapshot is reverted, it reverts the state of the evm, but we still want
2823    /// to know if there was an `assert` that failed after the snapshot was taken so that we can
2824    /// check if the test function passed all asserts even across snapshots. When a snapshot is
2825    /// reverted we get the _current_ `revm::JournaledState` which contains the state that we can
2826    /// check if the `_failed` variable is set,
2827    /// additionally
2828    pub has_state_snapshot_failure: bool,
2829    /// Tracks the caller of the test function
2830    pub caller: Option<Address>,
2831    /// Tracks numeric identifiers for forks
2832    pub next_fork_id: LocalForkId,
2833    /// All accounts that should be kept persistent when switching forks.
2834    /// This means all accounts stored here _don't_ use a separate storage section on each fork
2835    /// instead the use only one that's persistent across fork swaps.
2836    pub persistent_accounts: AddressSet,
2837    /// The configured spec id
2838    pub spec_id: SpecFor<FEN>,
2839    /// All accounts that are allowed to execute cheatcodes
2840    pub cheatcode_access_accounts: AddressSet,
2841}
2842
2843impl<FEN: FoundryEvmNetwork> Clone for BackendInner<FEN> {
2844    fn clone(&self) -> Self {
2845        Self {
2846            launched_with_fork: self.launched_with_fork.clone(),
2847            issued_local_fork_ids: self.issued_local_fork_ids.clone(),
2848            created_forks: self.created_forks.clone(),
2849            forks: self.forks.clone(),
2850            state_snapshots: self.state_snapshots.clone(),
2851            has_state_snapshot_failure: self.has_state_snapshot_failure,
2852            caller: self.caller,
2853            next_fork_id: self.next_fork_id,
2854            persistent_accounts: self.persistent_accounts.clone(),
2855            spec_id: self.spec_id,
2856            cheatcode_access_accounts: self.cheatcode_access_accounts.clone(),
2857        }
2858    }
2859}
2860
2861impl<FEN: FoundryEvmNetwork> Debug for BackendInner<FEN> {
2862    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2863        f.debug_struct("BackendInner")
2864            .field("launched_with_fork", &self.launched_with_fork)
2865            .field("issued_local_fork_ids", &self.issued_local_fork_ids)
2866            .field("created_forks", &self.created_forks)
2867            .field("forks", &self.forks)
2868            .field("state_snapshots", &self.state_snapshots)
2869            .field("has_state_snapshot_failure", &self.has_state_snapshot_failure)
2870            .field("caller", &self.caller)
2871            .field("next_fork_id", &self.next_fork_id)
2872            .field("persistent_accounts", &self.persistent_accounts)
2873            .field("spec_id", &self.spec_id)
2874            .field("cheatcode_access_accounts", &self.cheatcode_access_accounts)
2875            .finish()
2876    }
2877}
2878
2879impl<FEN: FoundryEvmNetwork> BackendInner<FEN> {
2880    pub fn ensure_fork_id(&self, id: LocalForkId) -> eyre::Result<&ForkId> {
2881        self.issued_local_fork_ids
2882            .get(&id)
2883            .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2884    }
2885
2886    pub fn ensure_fork_index(&self, id: &ForkId) -> eyre::Result<ForkLookupIndex> {
2887        self.created_forks
2888            .get(id)
2889            .copied()
2890            .ok_or_else(|| eyre::eyre!("No matching fork found for {}", id))
2891    }
2892
2893    pub fn ensure_fork_index_by_local_id(&self, id: LocalForkId) -> eyre::Result<ForkLookupIndex> {
2894        self.ensure_fork_index(self.ensure_fork_id(id)?)
2895    }
2896
2897    /// Returns the underlying fork mapped to the index
2898    #[track_caller]
2899    fn get_fork(&self, idx: ForkLookupIndex) -> &Fork<AnyNetwork, BlockEnvFor<FEN>> {
2900        debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2901        self.forks[idx].as_ref().unwrap()
2902    }
2903
2904    /// Returns the underlying fork mapped to the index
2905    #[track_caller]
2906    fn get_fork_mut(&mut self, idx: ForkLookupIndex) -> &mut Fork<AnyNetwork, BlockEnvFor<FEN>> {
2907        debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2908        self.forks[idx].as_mut().unwrap()
2909    }
2910
2911    /// Returns the underlying fork corresponding to the id
2912    #[track_caller]
2913    fn get_fork_by_id_mut(
2914        &mut self,
2915        id: LocalForkId,
2916    ) -> eyre::Result<&mut Fork<AnyNetwork, BlockEnvFor<FEN>>> {
2917        let idx = self.ensure_fork_index_by_local_id(id)?;
2918        Ok(self.get_fork_mut(idx))
2919    }
2920
2921    /// Returns the underlying fork corresponding to the id
2922    #[track_caller]
2923    fn get_fork_by_id(&self, id: LocalForkId) -> eyre::Result<&Fork<AnyNetwork, BlockEnvFor<FEN>>> {
2924        let idx = self.ensure_fork_index_by_local_id(id)?;
2925        Ok(self.get_fork(idx))
2926    }
2927
2928    /// Removes the fork
2929    fn take_fork(&mut self, idx: ForkLookupIndex) -> Fork<AnyNetwork, BlockEnvFor<FEN>> {
2930        debug_assert!(idx < self.forks.len(), "fork lookup index must exist");
2931        self.forks[idx].take().unwrap()
2932    }
2933
2934    fn set_fork(&mut self, idx: ForkLookupIndex, fork: Fork<AnyNetwork, BlockEnvFor<FEN>>) {
2935        self.forks[idx] = Some(fork)
2936    }
2937
2938    /// Returns an iterator over Forks
2939    pub fn forks_iter(
2940        &self,
2941    ) -> impl Iterator<Item = (LocalForkId, &Fork<AnyNetwork, BlockEnvFor<FEN>>)> + '_ {
2942        self.issued_local_fork_ids
2943            .iter()
2944            .map(|(id, fork_id)| (*id, self.get_fork(self.created_forks[fork_id])))
2945    }
2946
2947    /// Returns a mutable iterator over all Forks
2948    pub fn forks_iter_mut(
2949        &mut self,
2950    ) -> impl Iterator<Item = &mut Fork<AnyNetwork, BlockEnvFor<FEN>>> + '_ {
2951        self.forks.iter_mut().filter_map(|f| f.as_mut())
2952    }
2953
2954    /// Reverts the entire fork database
2955    pub fn revert_state_snapshot(
2956        &mut self,
2957        id: LocalForkId,
2958        fork_id: ForkId,
2959        idx: ForkLookupIndex,
2960        fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
2961    ) {
2962        self.created_forks.insert(fork_id.clone(), idx);
2963        self.issued_local_fork_ids.insert(id, fork_id);
2964        self.set_fork(idx, fork)
2965    }
2966
2967    /// Updates the fork and the local mapping and returns the new index for the `fork_db`
2968    pub fn update_fork_mapping(
2969        &mut self,
2970        id: LocalForkId,
2971        fork_id: ForkId,
2972        block: BlockNumHash,
2973        source_chain_id: ChainId,
2974        db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
2975        journaled_state: JournaledState,
2976    ) -> ForkLookupIndex {
2977        let idx = self.forks.len();
2978        self.issued_local_fork_ids.insert(id, fork_id.clone());
2979        self.created_forks.insert(fork_id, idx);
2980
2981        let fork = Fork {
2982            db,
2983            journaled_state,
2984            source_chain_id,
2985            position: ForkPosition::AfterBlock { block },
2986        };
2987        self.forks.push(Some(fork));
2988        idx
2989    }
2990
2991    pub fn roll_fork(
2992        &mut self,
2993        id: LocalForkId,
2994        new_fork_id: ForkId,
2995        block: BlockNumHash,
2996        source_chain_id: ChainId,
2997        backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
2998    ) -> eyre::Result<ForkLookupIndex> {
2999        let fork_id = self.ensure_fork_id(id)?;
3000        let idx = self.ensure_fork_index(fork_id)?;
3001
3002        if let Some(active) = self.forks[idx].as_mut() {
3003            // Initialize a new `ForkDB` while retaining persistent account data.
3004            let mut new_db = ForkDB::new(backend);
3005            for addr in self.persistent_accounts.iter().copied() {
3006                merge_db_account_data(addr, &active.db, &mut new_db);
3007            }
3008            active.db = new_db;
3009            active.source_chain_id = source_chain_id;
3010            active.position = ForkPosition::AfterBlock { block };
3011        }
3012        self.issued_local_fork_ids.insert(id, new_fork_id.clone());
3013        self.created_forks.insert(new_fork_id, idx);
3014        Ok(idx)
3015    }
3016
3017    /// Prepares a replacement for one fork without changing its local mapping or database.
3018    #[cfg(feature = "monad")]
3019    fn stage_fork_roll(
3020        &self,
3021        id: LocalForkId,
3022        new_fork_id: ForkId,
3023        block: BlockNumHash,
3024        source_chain_id: ChainId,
3025        backend: SharedBackend<AnyNetwork, BlockEnvFor<FEN>>,
3026        journaled_state: JournaledState,
3027    ) -> eyre::Result<StagedForkRoll<FEN>> {
3028        let fork_id = self.ensure_fork_id(id)?;
3029        let idx = self.ensure_fork_index(fork_id)?;
3030        let current = self.get_fork(idx);
3031
3032        // Initialize a new `ForkDB` with persistent account data and the prepared journal. The
3033        // live fork remains untouched until publication.
3034        let mut new_db = ForkDB::new(backend);
3035        for addr in self.persistent_accounts.iter().copied() {
3036            merge_db_account_data(addr, &current.db, &mut new_db);
3037        }
3038
3039        Ok(StagedForkRoll {
3040            local_id: id,
3041            fork_id: new_fork_id,
3042            fork_index: idx,
3043            fork: Fork {
3044                db: new_db,
3045                journaled_state,
3046                source_chain_id,
3047                position: ForkPosition::AfterBlock { block },
3048            },
3049        })
3050    }
3051
3052    /// Atomically publishes a previously prepared fork replacement.
3053    #[cfg(feature = "monad")]
3054    fn publish_fork_roll(&mut self, staged: StagedForkRoll<FEN>) -> ForkLookupIndex {
3055        let StagedForkRoll { local_id, fork_id, fork_index, fork } = staged;
3056        self.set_fork(fork_index, fork);
3057        self.issued_local_fork_ids.insert(local_id, fork_id.clone());
3058        self.created_forks.insert(fork_id, fork_index);
3059        fork_index
3060    }
3061
3062    /// Inserts a _new_ `ForkDB` and issues a new local fork identifier
3063    ///
3064    /// Also returns the index where the `ForDB` is stored
3065    pub fn insert_new_fork(
3066        &mut self,
3067        fork_id: ForkId,
3068        block: BlockNumHash,
3069        source_chain_id: ChainId,
3070        db: ForkDB<AnyNetwork, BlockEnvFor<FEN>>,
3071        journaled_state: JournaledState,
3072    ) -> (LocalForkId, ForkLookupIndex) {
3073        self.insert_fork(
3074            fork_id,
3075            Fork {
3076                db,
3077                journaled_state,
3078                source_chain_id,
3079                position: ForkPosition::AfterBlock { block },
3080            },
3081        )
3082    }
3083
3084    /// Inserts an existing fork while preserving its exact chain position.
3085    fn insert_fork(
3086        &mut self,
3087        fork_id: ForkId,
3088        fork: Fork<AnyNetwork, BlockEnvFor<FEN>>,
3089    ) -> (LocalForkId, ForkLookupIndex) {
3090        let idx = self.forks.len();
3091        self.created_forks.insert(fork_id.clone(), idx);
3092        let id = self.next_id();
3093        self.issued_local_fork_ids.insert(id, fork_id);
3094        self.forks.push(Some(fork));
3095        (id, idx)
3096    }
3097
3098    fn next_id(&mut self) -> U256 {
3099        let id = self.next_fork_id;
3100        self.next_fork_id += U256::from(1);
3101        id
3102    }
3103
3104    /// Returns the number of issued ids
3105    pub fn len(&self) -> usize {
3106        self.issued_local_fork_ids.len()
3107    }
3108
3109    /// Returns true if no forks are issued
3110    pub fn is_empty(&self) -> bool {
3111        self.issued_local_fork_ids.is_empty()
3112    }
3113
3114    pub fn precompile_addresses(&self) -> AddressSet {
3115        let evm = FEN::EvmFactory::default().create_evm(
3116            EmptyDB::default(),
3117            EvmEnv::new(CfgEnv::new_with_spec(self.spec_id), Default::default()),
3118        );
3119        evm.precompiles().addresses().copied().collect()
3120    }
3121
3122    /// Returns a new, empty, `JournaledState` with set precompiles
3123    pub fn new_journaled_state(&self) -> JournaledState {
3124        let mut journal = {
3125            let mut journal_inner = JournalInner::new();
3126            journal_inner.set_spec_id(self.spec_id.into());
3127            journal_inner
3128        };
3129        let precompile_addresses = self.precompile_addresses();
3130        journal.warm_addresses.set_precompile_addresses(&precompile_addresses);
3131        journal
3132    }
3133}
3134
3135impl<FEN: FoundryEvmNetwork> Default for BackendInner<FEN> {
3136    fn default() -> Self {
3137        Self {
3138            launched_with_fork: None,
3139            issued_local_fork_ids: Default::default(),
3140            created_forks: Default::default(),
3141            forks: vec![],
3142            state_snapshots: Default::default(),
3143            has_state_snapshot_failure: false,
3144            caller: None,
3145            next_fork_id: Default::default(),
3146            persistent_accounts: Default::default(),
3147            spec_id: SpecFor::<FEN>::default(),
3148            // grant the cheatcode,default test and caller address access to execute cheatcodes
3149            // itself
3150            cheatcode_access_accounts: AddressSet::from_iter([
3151                CHEATCODE_ADDRESS,
3152                TEST_CONTRACT_ADDRESS,
3153                CALLER,
3154            ]),
3155        }
3156    }
3157}
3158
3159/// Clones the data of the given `accounts` from the `active` database into the `fork_db`
3160/// This includes the data held in storage (`CacheDB`) and kept in the `JournaledState`.
3161pub(crate) fn merge_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3162    accounts: impl IntoIterator<Item = Address>,
3163    active: &CacheDB<ExtDB>,
3164    active_journaled_state: &mut JournaledState,
3165    target_fork: &mut Fork<N, B>,
3166) {
3167    for addr in accounts {
3168        merge_db_account_data(addr, active, &mut target_fork.db);
3169        merge_journaled_state_data(addr, active_journaled_state, &mut target_fork.journaled_state);
3170    }
3171
3172    *active_journaled_state = target_fork.journaled_state.clone();
3173}
3174
3175/// Returns the account-loading policy required by a fork source.
3176///
3177/// A recognized source chain ID is authoritative. The endpoint's execution profile is only a
3178/// source hint for custom chain IDs, where the chain ID cannot identify the RPC semantics.
3179pub fn account_fetch_policy_for_source(
3180    source_chain_id: ChainId,
3181    network_profile: NetworkConfigs,
3182) -> AccountFetchPolicy {
3183    let source_chain = Chain::from_id(source_chain_id);
3184    let is_tempo =
3185        source_chain.is_tempo() || (source_chain.named().is_none() && network_profile.is_tempo());
3186    if is_tempo { AccountFetchPolicy::RequireAccountInfo } else { AccountFetchPolicy::Auto }
3187}
3188
3189/// Clones the account data from the `active_journaled_state`  into the `fork_journaled_state`
3190fn merge_journaled_state_data(
3191    addr: Address,
3192    active_journaled_state: &JournaledState,
3193    fork_journaled_state: &mut JournaledState,
3194) {
3195    if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() {
3196        trace!(?addr, "updating journaled_state account data");
3197        if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) {
3198            // This will merge the fork's tracked storage with active storage and update values
3199            fork_account.storage.extend(std::mem::take(&mut acc.storage));
3200            // swap them so we can insert the account as whole in the next step
3201            std::mem::swap(&mut fork_account.storage, &mut acc.storage);
3202        }
3203        fork_journaled_state.state.insert(addr, acc);
3204    }
3205}
3206
3207/// Clones the account data from the `active` db into the `ForkDB`
3208fn merge_db_account_data<ExtDB: DatabaseRef, N: Network, B: ForkBlockEnv>(
3209    addr: Address,
3210    active: &CacheDB<ExtDB>,
3211    fork_db: &mut ForkDB<N, B>,
3212) {
3213    trace!(?addr, "merging database data");
3214
3215    let Some(acc) = active.cache.accounts.get(&addr) else { return };
3216
3217    // port contract cache over
3218    if let Some(code) = active.cache.contracts.get(&acc.info.code_hash) {
3219        trace!("merging contract cache");
3220        fork_db.cache.contracts.insert(acc.info.code_hash, code.clone());
3221    }
3222
3223    // port account storage over
3224    use std::collections::hash_map::Entry;
3225    match fork_db.cache.accounts.entry(addr) {
3226        Entry::Vacant(vacant) => {
3227            trace!("target account not present - inserting from active");
3228            // if the fork_db doesn't have the target account
3229            // insert the entire thing
3230            vacant.insert(acc.clone());
3231        }
3232        Entry::Occupied(mut occupied) => {
3233            trace!("target account present - merging storage slots");
3234            // if the fork_db does have the system,
3235            // extend the existing storage (overriding)
3236            let fork_account = occupied.get_mut();
3237            fork_account.storage.extend(&acc.storage);
3238        }
3239    }
3240}
3241
3242/// Returns true of the address is a contract
3243fn is_contract_in_state(evm_state: &EvmState, acc: Address) -> bool {
3244    evm_state.get(&acc).map(|acc| acc.info.code_hash != KECCAK_EMPTY).unwrap_or_default()
3245}
3246
3247/// Updates the evm env's block with the block's data
3248fn update_env_block<N: Network, SPEC: Into<SpecId> + Copy, BLOCK: FoundryBlock>(
3249    evm_env: &mut EvmEnv<SPEC, BLOCK>,
3250    block: &N::BlockResponse,
3251    source_chain_id: ChainId,
3252    networks: NetworkConfigs,
3253) {
3254    let header = block.header();
3255    let block_env = &mut evm_env.block_env;
3256    block_env.set_timestamp(U256::from(header.timestamp()));
3257    block_env.set_beneficiary(header.beneficiary());
3258    block_env.set_difficulty(header.difficulty());
3259    block_env.set_prevrandao(header.mix_hash());
3260    block_env.set_basefee(header.base_fee_per_gas().unwrap_or_default());
3261    block_env.set_gas_limit(header.gas_limit());
3262    block_env.set_number(U256::from(header.number()));
3263    block_env.set_slot_num(header.slot_number().unwrap_or_default());
3264
3265    apply_chain_and_block_specific_env_changes_for_chain::<N, _, _>(
3266        evm_env,
3267        block,
3268        source_chain_id,
3269        networks,
3270    );
3271}
3272
3273/// Executes the given transaction and commits state changes to the database _and_ the journaled
3274/// state, with an inspector.
3275fn commit_transaction<FEN: FoundryEvmNetwork>(
3276    transaction: TransactionInputs<FEN>,
3277    journaled_state: &mut JournaledState,
3278    fork: &mut Fork<AnyNetwork, BlockEnvFor<FEN>>,
3279    fork_id: &ForkId,
3280    networks: NetworkConfigs,
3281    persistent_accounts: &AddressSet,
3282    inspector: &mut dyn for<'db> FoundryInspectorExt<
3283        <FEN::EvmFactory as FoundryEvmFactory>::FoundryContext<'db>,
3284    >,
3285) -> eyre::Result<()> {
3286    let TransactionInputs { evm_env, tx_env, chain_context, rpc_block_number } = transaction;
3287    let now = Instant::now();
3288    let res = {
3289        let fork = fork.clone();
3290        let journaled_state = journaled_state.clone();
3291        let depth = journaled_state.depth;
3292        let mut db: Backend<FEN> =
3293            Backend::new_with_fork(fork_id, fork, journaled_state, networks)?;
3294        db.fork_block_number_override = Some(rpc_block_number);
3295
3296        let mut evm = FEN::EvmFactory::default()
3297            .create_nested_evm_with_inspector(&mut db, evm_env, inspector);
3298        *evm.chain_mut() = chain_context;
3299        evm.journal_inner_mut().depth = depth + 1;
3300        evm.transact_raw(tx_env).wrap_err("backend: failed committing transaction")?
3301    };
3302    trace!(elapsed = ?now.elapsed(), "transacted transaction");
3303
3304    apply_state_changeset(res.state, journaled_state, fork, persistent_accounts)?;
3305    Ok(())
3306}
3307
3308/// Helper method which updates data in the state with the data from the database.
3309/// Does not change state for persistent accounts (for roll fork to transaction and transact).
3310pub fn update_state<DB: Database>(
3311    state: &mut EvmState,
3312    db: &mut DB,
3313    persistent_accounts: Option<&AddressSet>,
3314) -> Result<(), DB::Error> {
3315    for (addr, acc) in state.iter_mut() {
3316        if persistent_accounts.is_none_or(|accounts| !accounts.contains(addr)) {
3317            acc.info = db.basic(*addr)?.unwrap_or_default();
3318            for (key, val) in &mut acc.storage {
3319                val.present_value = db.storage(*addr, *key)?;
3320            }
3321        }
3322    }
3323
3324    Ok(())
3325}
3326
3327/// Applies the changeset of a transaction to the active journaled state and also commits it in the
3328/// forked db
3329fn apply_state_changeset<N: Network, B: ForkBlockEnv>(
3330    state: EvmState,
3331    journaled_state: &mut JournaledState,
3332    fork: &mut Fork<N, B>,
3333    persistent_accounts: &AddressSet,
3334) -> Result<(), BackendError> {
3335    // Refresh cloned journals against a cloned database so a failed read cannot publish only part
3336    // of the transaction state.
3337    let mut staged_db = fork.db.clone();
3338    let mut staged_journaled_state = journaled_state.clone();
3339    let mut staged_fork_journaled_state = fork.journaled_state.clone();
3340    staged_db.commit(state);
3341    update_state(&mut staged_journaled_state.state, &mut staged_db, Some(persistent_accounts))?;
3342    update_state(
3343        &mut staged_fork_journaled_state.state,
3344        &mut staged_db,
3345        Some(persistent_accounts),
3346    )?;
3347
3348    fork.db = staged_db;
3349    *journaled_state = staged_journaled_state;
3350    fork.journaled_state = staged_fork_journaled_state;
3351    Ok(())
3352}
3353
3354fn inject_replay_precompiles(
3355    networks: NetworkConfigs,
3356    precompiles: &mut PrecompilesMap,
3357    chain_id: ChainId,
3358    timestamp: u64,
3359) {
3360    networks.inject_precompiles(precompiles);
3361    apply_bsc_p256_precompile(precompiles, chain_id, timestamp);
3362}
3363
3364#[cfg(test)]
3365mod tests {
3366    use super::{Fork, ForkAccountField, ReplayInputs, apply_state_changeset, update_env_block};
3367    use crate::{
3368        backend::{Backend, DatabaseExt, ForkPosition},
3369        evm::EthEvmNetwork,
3370        fork::{CreateFork, ForkId, MultiFork},
3371        opts::EvmOpts,
3372    };
3373    use alloy_consensus::{Signed, TxEnvelope, TxLegacy, transaction::Recovered};
3374    use alloy_eips::BlockNumHash;
3375    use alloy_evm::EvmEnv;
3376    use alloy_network::{
3377        AnyHeader, AnyNetwork, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope,
3378        AnyTxType, TransactionBuilder, UnknownTxEnvelope, UnknownTypedTransaction,
3379    };
3380    use alloy_primitives::{
3381        Address, B256, Bytes, Signature, TxKind, U256, address, keccak256, map::AddressSet,
3382    };
3383    use alloy_provider::{Provider, ProviderBuilder, mock::Asserter};
3384    use alloy_rpc_types::{
3385        Block, BlockTransactions, Transaction as RpcTransaction, TransactionRequest,
3386    };
3387    use alloy_serde::WithOtherFields;
3388    use alloy_sol_types::SolValue;
3389    use anvil::{NodeConfig, spawn};
3390    use foundry_common::{SYSTEM_TRANSACTION_TYPE, provider::get_http_provider};
3391    use foundry_config::{Config, NamedChain};
3392    use foundry_evm_networks::{NetworkConfigs, celo::transfer::CELO_TRANSFER_ADDRESS};
3393    use foundry_fork_db::{
3394        SharedBackend,
3395        cache::{BlockchainDb, BlockchainDbMeta},
3396    };
3397    use revm::{
3398        context::{BlockEnv, JournalInner, TxEnv},
3399        database::{AccountState, CacheDB, DatabaseRef, DbAccount},
3400        primitives::{KECCAK_EMPTY, hardfork::SpecId},
3401        state::{Account, AccountInfo, EvmState, EvmStorageSlot, TransactionId},
3402    };
3403
3404    #[cfg(feature = "monad")]
3405    use super::ensure_block_identity;
3406    #[cfg(feature = "monad")]
3407    use crate::evm::monad::BlockContext;
3408    #[cfg(feature = "monad")]
3409    use monad_revm::{
3410        MonadHardfork,
3411        api::block::syscall_snapshot_calldata,
3412        staking::{STAKING_ADDRESS, constants::SYSTEM_ADDRESS},
3413    };
3414
3415    fn fork_with_closed_backend() -> Fork<AnyNetwork, BlockEnv> {
3416        let provider =
3417            ProviderBuilder::<_, _, AnyNetwork>::default().connect_mocked_client(Asserter::new());
3418        let db = BlockchainDb::new(
3419            BlockchainDbMeta::new(BlockEnv::default(), "http://localhost".to_string()),
3420            None,
3421        );
3422        let (backend, handler) = SharedBackend::new(provider, db, None);
3423        drop(handler);
3424        Fork {
3425            db: CacheDB::new(backend),
3426            journaled_state: JournalInner::new(),
3427            source_chain_id: 1,
3428            position: ForkPosition::AfterBlock { block: BlockNumHash::default() },
3429        }
3430    }
3431
3432    fn rpc_block(number: u64, hash: B256, parent_hash: B256) -> AnyRpcBlock {
3433        let header = AnyHeader { number, parent_hash, ..Default::default() };
3434        AnyRpcBlock::new(
3435            Block::new(
3436                AnyRpcHeader::from_sealed(header.seal(hash)),
3437                BlockTransactions::Full(Vec::new()),
3438            )
3439            .into(),
3440        )
3441    }
3442
3443    fn rpc_transaction(
3444        caller: Address,
3445        nonce: u64,
3446        value: u64,
3447        gas_limit: u64,
3448        recipient: Address,
3449        hash: B256,
3450    ) -> AnyRpcTransaction {
3451        let tx = TxLegacy {
3452            nonce,
3453            gas_limit,
3454            to: TxKind::Call(recipient),
3455            value: U256::from(value),
3456            ..Default::default()
3457        };
3458        let signed =
3459            Signed::new_unchecked(tx, Signature::new(U256::from(1), U256::from(1), false), hash);
3460        AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3461            inner: Recovered::new_unchecked(
3462                AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(signed)),
3463                caller,
3464            ),
3465            block_hash: None,
3466            block_number: None,
3467            transaction_index: None,
3468            effective_gas_price: None,
3469            block_timestamp: None,
3470        }))
3471    }
3472
3473    #[test]
3474    fn fork_replay_backend_reuses_manager_on_current_thread_runtime() {
3475        let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
3476
3477        let forks = runtime.block_on(async {
3478            let forks = MultiFork::<AnyNetwork, SpecId, BlockEnv>::spawn();
3479            let sender = Address::with_last_byte(0x42);
3480            let recipient = Address::with_last_byte(0x43);
3481            let target = B256::with_last_byte(2);
3482            let mut fork = fork_with_closed_backend();
3483            fork.db.insert_account_info(
3484                sender,
3485                AccountInfo { balance: U256::from(100), ..Default::default() },
3486            );
3487            fork.db.insert_account_info(recipient, AccountInfo::default());
3488            fork.db.insert_account_info(Address::ZERO, AccountInfo::default());
3489            let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3490            block.inner.transactions = BlockTransactions::Full(vec![
3491                rpc_transaction(sender, 0, 7, 21_000, recipient, B256::with_last_byte(1)),
3492                rpc_transaction(sender, 1, 0, 21_000, recipient, target),
3493            ]);
3494
3495            let result = Backend::<EthEvmNetwork>::replay_until(
3496                &mut fork,
3497                ReplayInputs {
3498                    fork_id: ForkId::new("http://localhost", Some(0)),
3499                    forks: forks.clone(),
3500                    evm_env: EvmEnv::default(),
3501                    networks: NetworkConfigs::default(),
3502                },
3503                &block,
3504                #[cfg(feature = "monad")]
3505                None,
3506                target,
3507                &mut JournalInner::new(),
3508                &AddressSet::default(),
3509            )
3510            .unwrap();
3511
3512            assert!(result.is_some());
3513            assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::from(7));
3514            assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 1);
3515            forks
3516        });
3517        drop(runtime);
3518        drop(forks);
3519    }
3520
3521    #[test]
3522    fn fork_replay_keeps_system_skips_and_prefix_atomicity() {
3523        let sender = Address::with_last_byte(0x42);
3524        let recipient = Address::with_last_byte(0x43);
3525        let system = address!("6f49a8f621353f12378d0046e7d7e4b9b249dc9e");
3526        let target = B256::with_last_byte(4);
3527
3528        #[cfg(not(feature = "monad"))]
3529        let contexts = [false];
3530        #[cfg(feature = "monad")]
3531        let contexts = [false, true];
3532        for _with_context in contexts {
3533            for invalid_nonce in [false, true] {
3534                let mut fork = fork_with_closed_backend();
3535                fork.db.insert_account_info(
3536                    sender,
3537                    AccountInfo { balance: U256::from(100), ..Default::default() },
3538                );
3539                fork.db.insert_account_info(recipient, AccountInfo::default());
3540                fork.db.insert_account_info(Address::ZERO, AccountInfo::default());
3541                let block = AnyRpcBlock::new(
3542                    Block::new(
3543                        AnyRpcHeader::from_sealed(
3544                            AnyHeader { number: 1, ..Default::default() }
3545                                .seal(B256::with_last_byte(1)),
3546                        ),
3547                        BlockTransactions::Full(vec![
3548                            rpc_transaction(
3549                                sender,
3550                                0,
3551                                7,
3552                                21_000,
3553                                recipient,
3554                                B256::with_last_byte(1),
3555                            ),
3556                            // Ethereum must skip this envelope, not validate it as an ordinary
3557                            // call.
3558                            rpc_transaction(system, 0, 0, 0, recipient, B256::with_last_byte(2)),
3559                            rpc_transaction(
3560                                sender,
3561                                if invalid_nonce { 0 } else { 1 },
3562                                5,
3563                                21_000,
3564                                recipient,
3565                                B256::with_last_byte(3),
3566                            ),
3567                            rpc_transaction(sender, 2, 2, 21_000, recipient, target),
3568                        ]),
3569                    )
3570                    .into(),
3571                );
3572                let networks = NetworkConfigs::default();
3573                #[cfg(feature = "monad")]
3574                let networks = if _with_context { NetworkConfigs::with_monad() } else { networks };
3575                #[cfg(feature = "monad")]
3576                let context = _with_context.then(|| {
3577                    BlockContext::<EthEvmNetwork>::new(Vec::new(), Vec::new(), Vec::new())
3578                });
3579                let result = Backend::<EthEvmNetwork>::replay_until(
3580                    &mut fork,
3581                    ReplayInputs {
3582                        fork_id: ForkId::new("http://localhost", Some(0)),
3583                        forks: MultiFork::spawn(),
3584                        evm_env: EvmEnv::default(),
3585                        networks,
3586                    },
3587                    &block,
3588                    #[cfg(feature = "monad")]
3589                    context.as_ref(),
3590                    target,
3591                    &mut JournalInner::new(),
3592                    &AddressSet::default(),
3593                );
3594                if invalid_nonce {
3595                    assert!(result.is_err());
3596                    assert_eq!(fork.db.basic_ref(recipient).unwrap().unwrap().balance, U256::ZERO);
3597                    assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 0);
3598                } else {
3599                    assert!(result.unwrap().is_some());
3600                    assert_eq!(
3601                        fork.db.basic_ref(recipient).unwrap().unwrap().balance,
3602                        U256::from(12)
3603                    );
3604                    assert_eq!(fork.db.basic_ref(sender).unwrap().unwrap().nonce, 2);
3605                }
3606            }
3607        }
3608    }
3609
3610    #[test]
3611    #[cfg(feature = "monad")]
3612    fn fork_replay_executes_monad_system_prefix_with_or_without_profile() {
3613        let transaction = |nonce, hash| {
3614            let tx = TxLegacy {
3615                nonce,
3616                to: TxKind::Call(STAKING_ADDRESS),
3617                input: syscall_snapshot_calldata(),
3618                ..Default::default()
3619            };
3620            let signed = Signed::new_unchecked(
3621                tx,
3622                Signature::new(U256::from(1), U256::from(1), false),
3623                hash,
3624            );
3625            AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3626                inner: Recovered::new_unchecked(
3627                    AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(signed)),
3628                    SYSTEM_ADDRESS,
3629                ),
3630                block_hash: None,
3631                block_number: None,
3632                transaction_index: None,
3633                effective_gas_price: None,
3634                block_timestamp: None,
3635            }))
3636        };
3637        let target = B256::with_last_byte(2);
3638        let mut block = rpc_block(1, B256::with_last_byte(1), B256::ZERO);
3639        block.inner.transactions = BlockTransactions::Full(vec![
3640            transaction(3, B256::with_last_byte(1)),
3641            transaction(4, target),
3642        ]);
3643        for with_context in [false, true] {
3644            let mut fork = fork_with_closed_backend();
3645            fork.db.insert_account_info(
3646                SYSTEM_ADDRESS,
3647                AccountInfo { nonce: 3, ..Default::default() },
3648            );
3649            fork.db.insert_account_info(STAKING_ADDRESS, AccountInfo::default());
3650            fork.db.replace_account_storage(STAKING_ADDRESS, Default::default()).unwrap();
3651            let context = with_context.then(|| {
3652                BlockContext::<crate::evm::MonadEvmNetwork>::new(Vec::new(), Vec::new(), Vec::new())
3653            });
3654            let result = Backend::<crate::evm::MonadEvmNetwork>::replay_until(
3655                &mut fork,
3656                ReplayInputs {
3657                    fork_id: ForkId::new("http://localhost", Some(0)),
3658                    forks: MultiFork::spawn(),
3659                    evm_env: EvmEnv::new(
3660                        revm::context::CfgEnv::new_with_spec(MonadHardfork::MonadNine),
3661                        BlockEnv::default(),
3662                    ),
3663                    networks: if with_context {
3664                        NetworkConfigs::with_monad()
3665                    } else {
3666                        NetworkConfigs::default()
3667                    },
3668                },
3669                &block,
3670                context.as_ref(),
3671                target,
3672                &mut JournalInner::new(),
3673                &AddressSet::default(),
3674            );
3675            assert!(result.unwrap().is_some());
3676            assert_eq!(fork.db.basic_ref(SYSTEM_ADDRESS).unwrap().unwrap().nonce, 4);
3677        }
3678    }
3679
3680    #[test]
3681    #[cfg(feature = "monad")]
3682    fn validates_block_identity() {
3683        let hash = B256::with_last_byte(2);
3684        let block = rpc_block(2, hash, B256::with_last_byte(1));
3685        assert!(ensure_block_identity(&block, BlockNumHash::new(2, hash), "parent").is_ok());
3686
3687        let err =
3688            ensure_block_identity(&block, BlockNumHash::new(2, B256::with_last_byte(3)), "parent")
3689                .unwrap_err();
3690        assert!(err.to_string().contains("parent block changed"));
3691
3692        let err =
3693            ensure_block_identity(&block, BlockNumHash::new(1, hash), "grandparent").unwrap_err();
3694        assert!(err.to_string().contains("grandparent block changed"));
3695    }
3696
3697    #[test]
3698    fn failed_fork_state_refresh_does_not_publish_transaction_changes() {
3699        let mut fork = fork_with_closed_backend();
3700        let externally_loaded = Address::with_last_byte(1);
3701        let fork_loaded = Address::with_last_byte(2);
3702        let committed = Address::with_last_byte(3);
3703        let missing_slot = U256::from(1);
3704
3705        let cached_external = AccountInfo { balance: U256::from(11), ..Default::default() };
3706        let cached_fork = AccountInfo { balance: U256::from(12), ..Default::default() };
3707        fork.db.insert_account_info(externally_loaded, cached_external);
3708        fork.db.insert_account_info(fork_loaded, cached_fork);
3709
3710        let mut journaled_state = JournalInner::new();
3711        let external_account = Account::default()
3712            .with_info(AccountInfo { balance: U256::from(1), ..Default::default() });
3713        journaled_state.state.insert(externally_loaded, external_account);
3714
3715        let mut fork_account = Account::default()
3716            .with_info(AccountInfo { balance: U256::from(2), ..Default::default() });
3717        fork_account
3718            .storage
3719            .insert(missing_slot, EvmStorageSlot::new(U256::ZERO, TransactionId::ZERO));
3720        fork.journaled_state.state.insert(fork_loaded, fork_account);
3721
3722        let mut committed_account = Account::default()
3723            .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3724        committed_account.mark_touch();
3725        let mut state = EvmState::default();
3726        state.insert(committed, committed_account);
3727
3728        let result =
3729            apply_state_changeset(state, &mut journaled_state, &mut fork, &AddressSet::default());
3730        assert!(result.is_err());
3731        assert!(!fork.db.cache.accounts.contains_key(&committed));
3732        assert_eq!(journaled_state.state[&externally_loaded].info.balance, U256::from(1));
3733        assert_eq!(fork.journaled_state.state[&fork_loaded].info.balance, U256::from(2));
3734    }
3735
3736    #[test]
3737    fn failed_fork_state_refresh_preserves_not_existing_account() {
3738        let mut fork = fork_with_closed_backend();
3739        let address = Address::with_last_byte(1);
3740        let missing_slot = U256::from(1);
3741        fork.db.cache.accounts.insert(address, DbAccount::new_not_existing());
3742
3743        let mut journaled_state = JournalInner::new();
3744        let mut journaled_account = Account::default()
3745            .with_info(AccountInfo { balance: U256::from(1), ..Default::default() });
3746        journaled_account
3747            .storage
3748            .insert(missing_slot, EvmStorageSlot::new(U256::from(7), TransactionId::ZERO));
3749        journaled_state.state.insert(address, journaled_account);
3750
3751        let mut touched_account = Account::default()
3752            .with_info(AccountInfo { balance: U256::from(13), ..Default::default() });
3753        touched_account.mark_touch();
3754        let mut state = EvmState::default();
3755        state.insert(address, touched_account);
3756
3757        let result =
3758            apply_state_changeset(state, &mut journaled_state, &mut fork, &AddressSet::default());
3759        assert!(result.is_err());
3760        assert_eq!(fork.db.cache.accounts[&address].account_state, AccountState::NotExisting);
3761        assert_eq!(journaled_state.state[&address].info.balance, U256::from(1));
3762        assert_eq!(
3763            journaled_state.state[&address].storage[&missing_slot].present_value(),
3764            U256::from(7)
3765        );
3766    }
3767
3768    #[tokio::test(flavor = "multi_thread")]
3769    async fn refresh_fork_account_updates_loaded_journals() {
3770        let (api, handle) = spawn(NodeConfig::test()).await;
3771        let target = address!("0x0000000000000000000000000000000000001331");
3772        let code =
3773            Bytes::from_static(&[0x60, 0x2a, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3]);
3774
3775        let provider = handle.http_provider();
3776        let block_number = provider.get_block_number().await.unwrap();
3777        let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
3778        evm_opts.fork_url = Some(handle.http_endpoint());
3779        evm_opts.fork_block_number = Some(block_number);
3780        let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
3781        let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
3782
3783        let mut journaled_state = JournalInner::new();
3784        journaled_state.load_account(&mut backend, target).unwrap();
3785        journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(1);
3786        let fork = backend.active_fork_mut().unwrap();
3787        fork.journaled_state.load_account(&mut fork.db, target).unwrap();
3788        fork.journaled_state.state.get_mut(&target).unwrap().info.balance = U256::from(2);
3789        let cached = fork.db.cache.accounts.get_mut(&target).unwrap();
3790        cached.info.balance = U256::from(3);
3791        cached.account_state = AccountState::Touched;
3792        assert_eq!(journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
3793        assert_eq!(fork.journaled_state.state[&target].info.code_hash, KECCAK_EMPTY);
3794
3795        api.anvil_set_code(target, code.clone()).await.unwrap();
3796        backend.refresh_fork_account(target, ForkAccountField::Code, &mut journaled_state).unwrap();
3797
3798        let expected_hash = keccak256(&code);
3799        let refreshed = &journaled_state.state[&target].info;
3800        assert_eq!(refreshed.code_hash, expected_hash);
3801        assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
3802        assert_eq!(refreshed.balance, U256::from(1));
3803        let refreshed = &backend.active_fork().unwrap().journaled_state.state[&target].info;
3804        assert_eq!(refreshed.code_hash, expected_hash);
3805        assert_eq!(refreshed.code.as_ref().unwrap().original_bytes(), code);
3806        assert_eq!(refreshed.balance, U256::from(2));
3807        let cached = &backend.active_fork().unwrap().db.cache.accounts[&target];
3808        assert_eq!(cached.info.code_hash, expected_hash);
3809        assert_eq!(cached.info.balance, U256::from(3));
3810    }
3811
3812    #[test]
3813    fn ethereum_replay_skips_unknown_system_envelopes_before_conversion() {
3814        let transaction = |ty| {
3815            let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
3816                hash: B256::ZERO,
3817                inner: UnknownTypedTransaction {
3818                    ty: AnyTxType(ty),
3819                    fields: Default::default(),
3820                    memo: Default::default(),
3821                },
3822            });
3823            AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
3824                inner: Recovered::new_unchecked(unknown, Address::with_last_byte(0x42)),
3825                block_hash: None,
3826                block_number: None,
3827                transaction_index: None,
3828                effective_gas_price: None,
3829                block_timestamp: None,
3830            }))
3831        };
3832
3833        assert!(
3834            Backend::<EthEvmNetwork>::replay_tx_env(&transaction(SYSTEM_TRANSACTION_TYPE))
3835                .unwrap()
3836                .is_none()
3837        );
3838        assert!(Backend::<EthEvmNetwork>::replay_tx_env(&transaction(0xff)).is_err());
3839    }
3840
3841    #[tokio::test(flavor = "multi_thread")]
3842    async fn celo_transaction_hash_fork_replays_transfer_precompile() {
3843        let networks = NetworkConfigs::with_celo();
3844        let (api, handle) = spawn(
3845            NodeConfig::test().with_chain_id(Some(NamedChain::Celo as u64)).with_networks(networks),
3846        )
3847        .await;
3848        let provider = handle.http_provider();
3849        let sender = provider.get_accounts().await.unwrap()[0];
3850        let recipient = Address::with_last_byte(0x99);
3851        let transfer_amount = U256::from(1_000);
3852        let target_amount = U256::from(1);
3853        let nonce = provider.get_transaction_count(sender).await.unwrap();
3854        let gas_price = provider.get_gas_price().await.unwrap();
3855
3856        api.anvil_set_auto_mine(false).await.unwrap();
3857        api.send_transaction(WithOtherFields::new(
3858            TransactionRequest::default()
3859                .with_from(sender)
3860                .with_to(CELO_TRANSFER_ADDRESS)
3861                .with_nonce(nonce)
3862                .with_gas_limit(100_000)
3863                .with_gas_price(gas_price)
3864                .with_input(Bytes::from((sender, recipient, transfer_amount).abi_encode())),
3865        ))
3866        .await
3867        .unwrap();
3868        let target_hash = api
3869            .send_transaction(WithOtherFields::new(
3870                TransactionRequest::default()
3871                    .with_from(sender)
3872                    .with_to(recipient)
3873                    .with_nonce(nonce + 1)
3874                    .with_gas_limit(21_000)
3875                    .with_gas_price(gas_price)
3876                    .with_value(target_amount),
3877            ))
3878            .await
3879            .unwrap();
3880        api.mine_one().await.unwrap();
3881
3882        assert_eq!(provider.get_balance(recipient).await.unwrap(), transfer_amount + target_amount);
3883
3884        let endpoint = handle.http_endpoint();
3885        let fork_block_number = provider.get_block_number().await.unwrap();
3886        let evm_opts = EvmOpts {
3887            fork_url: Some(endpoint.clone()),
3888            fork_block_number: Some(fork_block_number),
3889            networks,
3890            ..Default::default()
3891        };
3892        let fork = CreateFork { url: endpoint, enable_caching: false, evm_opts, resolved: None };
3893        let mut backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
3894        backend.set_networks(networks);
3895
3896        let fork_id = backend.create_fork_at_transaction(fork, target_hash).unwrap();
3897        let fork = backend.inner.get_fork_by_id(fork_id).unwrap();
3898        assert!(matches!(
3899            fork.position,
3900            ForkPosition::BeforeTransaction { transaction_index: 1, .. }
3901        ));
3902        assert_eq!(
3903            fork.db.basic_ref(recipient).unwrap().unwrap_or_default().balance,
3904            transfer_amount
3905        );
3906    }
3907
3908    #[test]
3909    fn fork_position_advances_from_exact_transaction_predecessor() {
3910        let parent_block = BlockNumHash::new(10, B256::with_last_byte(10));
3911        let block = BlockNumHash::new(11, B256::with_last_byte(11));
3912        let parent = ForkPosition::AfterBlock { block: parent_block };
3913        assert_eq!(
3914            parent.after_transaction(block, parent_block.hash, 0, 2),
3915            Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
3916        );
3917        assert_eq!(
3918            parent.after_transaction(block, parent_block.hash, 0, 1),
3919            Some(ForkPosition::AfterBlock { block })
3920        );
3921
3922        let before_first = ForkPosition::BeforeTransaction { block, transaction_index: 0 };
3923        assert_eq!(
3924            before_first.after_transaction(block, parent_block.hash, 0, 2),
3925            Some(ForkPosition::BeforeTransaction { block, transaction_index: 1 })
3926        );
3927
3928        let before_second = ForkPosition::BeforeTransaction { block, transaction_index: 1 };
3929        assert_eq!(
3930            before_second.after_transaction(block, parent_block.hash, 1, 3),
3931            Some(ForkPosition::BeforeTransaction { block, transaction_index: 2 })
3932        );
3933        assert_eq!(
3934            before_second.after_transaction(block, parent_block.hash, 1, 2),
3935            Some(ForkPosition::AfterBlock { block })
3936        );
3937
3938        assert_eq!(parent.after_transaction(block, B256::ZERO, 0, 1), None);
3939        assert_eq!(parent.after_transaction(block, parent_block.hash, 1, 2), None);
3940        assert_eq!(before_second.after_transaction(block, parent_block.hash, 0, 3), None);
3941        assert_eq!(before_second.after_transaction(block, parent_block.hash, 2, 3), None);
3942        assert_eq!(before_second.after_transaction(block, parent_block.hash, 1, 1), None);
3943        assert_eq!(parent.after_transaction(block, parent_block.hash, 0, 0), None);
3944    }
3945
3946    #[test]
3947    fn fork_block_env_updates_slot_number() {
3948        let mut evm_env =
3949            EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
3950        for slot_number in [Some(42), Some(u64::MAX), None, Some(0)] {
3951            let header = AnyHeader { slot_number, ..Default::default() };
3952            let block = AnyRpcBlock::new(
3953                Block::new(
3954                    AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
3955                    BlockTransactions::Full(Vec::new()),
3956                )
3957                .into(),
3958            );
3959            update_env_block::<AnyNetwork, _, _>(
3960                &mut evm_env,
3961                &block,
3962                NamedChain::Mainnet as u64,
3963                NetworkConfigs::default(),
3964            );
3965            assert_eq!(evm_env.block_env.slot_num, slot_number.unwrap_or_default());
3966        }
3967    }
3968
3969    #[test]
3970    fn fork_replay_block_env_preserves_arbitrum_l1_number() {
3971        let header = AnyHeader { number: 75_219_831, ..Default::default() };
3972        let mut block = AnyRpcBlock::new(
3973            Block::new(
3974                AnyRpcHeader::from_sealed(header.seal(B256::ZERO)),
3975                BlockTransactions::Full(Vec::new()),
3976            )
3977            .into(),
3978        );
3979        block.other.insert("l1BlockNumber".to_string(), serde_json::json!("0x10276d3"));
3980        let mut evm_env =
3981            EvmEnv::new(revm::context::CfgEnv::<SpecId>::default(), BlockEnv::default());
3982
3983        update_env_block::<AnyNetwork, _, _>(
3984            &mut evm_env,
3985            &block,
3986            NamedChain::Arbitrum as u64,
3987            NetworkConfigs::default(),
3988        );
3989
3990        assert_eq!(evm_env.block_env.number, U256::from(16_938_707));
3991    }
3992
3993    #[tokio::test(flavor = "multi_thread")]
3994    async fn temporary_backend_preserves_fork_position() {
3995        let (_api, handle) = spawn(NodeConfig::test()).await;
3996        let provider = handle.http_provider();
3997        let block_number = provider.get_block_number().await.unwrap();
3998
3999        let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4000        evm_opts.fork_url = Some(handle.http_endpoint());
4001        evm_opts.fork_block_number = Some(block_number);
4002        let fork = evm_opts.get_fork(&Config::default(), 31_337, Some(block_number)).unwrap();
4003        let mut backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
4004        let id = backend.active_fork_ids.unwrap().0;
4005        let fork_id = backend.inner.ensure_fork_id(id).unwrap().clone();
4006
4007        for position in [
4008            ForkPosition::BeforeTransaction {
4009                block: BlockNumHash::new(block_number + 1, B256::with_last_byte(1)),
4010                transaction_index: 2,
4011            },
4012            ForkPosition::AfterBlock {
4013                block: BlockNumHash::new(block_number + 2, B256::with_last_byte(2)),
4014            },
4015        ] {
4016            backend.inner.get_fork_by_id_mut(id).unwrap().position = position;
4017            let fork = backend.active_fork().unwrap().clone();
4018            let journaled_state = fork.journaled_state.clone();
4019            let mut temporary = Backend::<EthEvmNetwork>::new_with_fork(
4020                &fork_id,
4021                fork,
4022                journaled_state,
4023                NetworkConfigs::default(),
4024            )
4025            .unwrap();
4026
4027            assert_eq!(temporary.active_fork().unwrap().position, position);
4028            let expected = match position {
4029                ForkPosition::AfterBlock { block }
4030                | ForkPosition::BeforeTransaction { block, .. } => block.number,
4031            };
4032            assert_eq!(temporary.active_fork_block_number(), Some(expected));
4033            temporary.fork_block_number_override = Some(expected + 1);
4034            assert_eq!(temporary.active_fork_block_number(), Some(expected + 1));
4035        }
4036    }
4037
4038    #[tokio::test(flavor = "multi_thread")]
4039    #[cfg(feature = "monad")]
4040    async fn fork_factory_boundary_preserves_explicit_execution_overrides() {
4041        async fn pinned_opts(endpoint: String, networks: Option<NetworkConfigs>) -> EvmOpts {
4042            let mut opts = EvmOpts { fork_url: Some(endpoint), ..Default::default() };
4043            if let Some(networks) = networks {
4044                opts.networks = networks;
4045            }
4046            opts.infer_network_from_fork().await.unwrap();
4047            let identity = opts.fork_endpoint.clone().unwrap();
4048            let network_is_inferred = opts.fork_network_is_inferred;
4049            opts.expect_fork_endpoint(identity, network_is_inferred);
4050            opts.pin_fork_block().await.unwrap();
4051            opts
4052        }
4053
4054        fn target_fork(opts: EvmOpts, url: String) -> crate::fork::CreateFork {
4055            crate::fork::CreateFork { url, enable_caching: false, evm_opts: opts, resolved: None }
4056        }
4057
4058        let (ethereum_base_api, ethereum_base) = spawn(NodeConfig::test()).await;
4059        let (_ethereum_target_api, ethereum_target) = spawn(NodeConfig::test()).await;
4060        let (monad_base_api, monad_base) = spawn(NodeConfig::test_monad()).await;
4061        let (_monad_target_api, monad_target) = spawn(NodeConfig::test_monad()).await;
4062        ethereum_base_api.mine_one().await.unwrap();
4063        monad_base_api.mine_one().await.unwrap();
4064
4065        let inferred_ethereum = pinned_opts(ethereum_base.http_endpoint(), None).await;
4066        assert!(inferred_ethereum.fork_network_is_inferred);
4067        let error = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
4068            inferred_ethereum,
4069            monad_target.http_endpoint(),
4070        )))
4071        .unwrap_err();
4072        assert!(
4073            error
4074                .to_string()
4075                .contains("cannot create a `monad` fork with an EVM instantiated for `ethereum`"),
4076            "{error}"
4077        );
4078
4079        let inferred_monad = pinned_opts(monad_base.http_endpoint(), None).await;
4080        assert!(inferred_monad.fork_network_is_inferred);
4081        let error = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
4082            inferred_monad,
4083            ethereum_target.http_endpoint(),
4084        )))
4085        .unwrap_err();
4086        assert!(
4087            error
4088                .to_string()
4089                .contains("cannot create a `ethereum` fork with an EVM instantiated for `monad`"),
4090            "{error}"
4091        );
4092
4093        let explicit_ethereum =
4094            pinned_opts(ethereum_base.http_endpoint(), Some(NetworkConfigs::with_ethereum())).await;
4095        assert!(!explicit_ethereum.fork_network_is_inferred);
4096        let _backend = Backend::<EthEvmNetwork>::spawn(Some(target_fork(
4097            explicit_ethereum,
4098            monad_target.http_endpoint(),
4099        )))
4100        .unwrap();
4101
4102        let explicit_monad =
4103            pinned_opts(monad_base.http_endpoint(), Some(NetworkConfigs::with_monad())).await;
4104        assert!(!explicit_monad.fork_network_is_inferred);
4105        let _backend = Backend::<crate::evm::MonadEvmNetwork>::spawn(Some(target_fork(
4106            explicit_monad,
4107            ethereum_target.http_endpoint(),
4108        )))
4109        .unwrap();
4110    }
4111
4112    #[tokio::test(flavor = "multi_thread")]
4113    async fn can_read_write_cache() {
4114        let endpoint = &*foundry_test_utils::rpc::next_http_rpc_endpoint();
4115        let provider = get_http_provider(endpoint);
4116
4117        let block_num = provider.get_block_number().await.unwrap();
4118
4119        let mut evm_opts = Config::figment().extract::<EvmOpts>().unwrap();
4120        evm_opts.fork_url = Some(endpoint.to_string());
4121        evm_opts.fork_block_number = Some(block_num);
4122
4123        let (evm_env, _, resolved) =
4124            evm_opts.env_resolved::<SpecId, BlockEnv, TxEnv>().await.unwrap();
4125
4126        let fork = evm_opts
4127            .get_fork_resolved(&Config::default(), evm_env.cfg_env.chain_id, resolved.as_ref())
4128            .unwrap();
4129
4130        let resolved = resolved.unwrap();
4131        let fork_hash = resolved.hash();
4132        let source_id = resolved.source_id();
4133        let backend = Backend::<EthEvmNetwork>::spawn(Some(fork)).unwrap();
4134
4135        // some rng contract from etherscan
4136        let address = address!("0x63091244180ae240c87d1f528f5f269134cb07b3");
4137
4138        let num_slots = 5;
4139        let _account = backend.basic_ref(address);
4140        for idx in 0..num_slots {
4141            let _ = backend.storage_ref(address, U256::from(idx));
4142        }
4143        drop(backend);
4144
4145        let meta = BlockchainDbMeta::new(evm_env.block_env, endpoint.to_string())
4146            .with_fork_identity(fork_hash, source_id);
4147
4148        let db = BlockchainDb::new(
4149            meta,
4150            Some(Config::foundry_block_cache_dir(NamedChain::Mainnet, block_num).unwrap()),
4151        );
4152        assert!(db.accounts().read().contains_key(&address));
4153        assert!(db.storage().read().contains_key(&address));
4154        assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize);
4155    }
4156}