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