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