Skip to main content

foundry_cheatcodes/
inspector.rs

1//! Cheatcode EVM inspector.
2
3use crate::{
4    Cheatcode, CheatsConfig, CheatsCtxt, Error, Result,
5    Vm::{self, AccountAccess},
6    evm::{
7        DealRecord, GasRecord, RecordAccess, append_storage_access, journaled_account,
8        mark_account_accesses_reverted, merge_recorded_frame,
9        mock::{self, MockCallDataContext, MockCallReturnData},
10        prank::Prank,
11    },
12    expected_emit::{self, ExpectedEmitTracker},
13    inspector::utils::CommonCreateInput,
14    script::{Broadcast, Wallets},
15    test::{
16        assume::AssumeNoRevert,
17        expect::{self, ExpectedCallTracker, ExpectedCreate, ExpectedRevert, ExpectedRevertKind},
18        revert_handlers,
19    },
20    utils::IgnoredTraces,
21};
22use alloy_consensus::BlobTransactionSidecarVariant;
23use alloy_network::{Ethereum, Network, TransactionBuilder};
24use alloy_primitives::{
25    Address, B256, Bytes, Log, TxKind, U256, hex,
26    map::{AddressHashMap, HashMap, HashSet},
27};
28use alloy_rpc_types::AccessList;
29use alloy_signer_local::PrivateKeySigner;
30use alloy_sol_types::{SolCall, SolInterface};
31use foundry_common::{
32    FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
33    mapping_slots::{
34        MappingSlots, PendingMappingHash, capture_hash as capture_mapping_hash,
35        record_hash as record_mapping_hash, step as mapping_step,
36    },
37};
38use foundry_evm_core::{
39    Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
40    abi::Vm::stopExpectSafeMemoryCall,
41    backend::{
42        ContextUpdateFor, DatabaseError, DatabaseExt, JournaledState, LocalForkId, RevertDiagnostic,
43    },
44    constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
45    env::FoundryContextExt,
46    evm::{
47        BlockEnvFor, ChainFor, EthEvmNetwork, EvmFactoryFor, FoundryContextFor, FoundryEvmFactory,
48        FoundryEvmNetwork, NestedEvmClosureFor, SpecFor, TransactionRequestFor, TxEnvFor,
49        with_inherited_evm,
50    },
51};
52use foundry_evm_traces::{
53    TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
54};
55use foundry_wallets::wallet_multi::MultiWallet;
56use itertools::Itertools;
57use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
58use rand::Rng;
59use revm::{
60    Inspector, JournalEntry,
61    bytecode::opcode as op,
62    context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
63    context_interface::{CreateScheme, transaction::SignedAuthorization},
64    handler::FrameResult,
65    interpreter::{
66        CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome,
67        FrameInput, Gas, InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
68        interpreter_types::{Jumps, LoopControl, MemoryTr, ReturnData},
69    },
70};
71use serde_json::Value;
72use std::{
73    cmp::max,
74    collections::{BTreeMap, VecDeque},
75    fmt::Debug,
76    fs::File,
77    io::BufReader,
78    ops::Range,
79    path::PathBuf,
80    sync::{Arc, OnceLock},
81};
82
83mod env_overrides;
84pub use env_overrides::EnvOverrideState;
85
86mod utils;
87
88pub mod analysis;
89pub use analysis::CheatcodeAnalysis;
90
91/// Helper trait for running nested EVM operations from inside cheatcode implementations.
92pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
93    /// Runs a closure with a nested EVM built from the current context.
94    /// The inspector is assembled internally — never exposed to the caller.
95    fn with_nested_evm(
96        &mut self,
97        cheats: &mut Cheatcodes<FEN>,
98        ecx: &mut FoundryContextFor<'_, FEN>,
99        f: NestedEvmClosureFor<'_, FEN>,
100    ) -> Result<(), EVMError<DatabaseError>>;
101
102    /// Replays a historical transaction on the database. Inspector is assembled internally.
103    fn transact_on_db(
104        &mut self,
105        cheats: &mut Cheatcodes<FEN>,
106        ecx: &mut FoundryContextFor<'_, FEN>,
107        fork_id: Option<U256>,
108        transaction: B256,
109    ) -> eyre::Result<ContextUpdateFor<EvmFactoryFor<FEN>>>;
110
111    /// Executes a `TransactionRequest` on the database. Inspector is assembled internally.
112    fn transact_from_tx_on_db(
113        &mut self,
114        cheats: &mut Cheatcodes<FEN>,
115        ecx: &mut FoundryContextFor<'_, FEN>,
116        tx: TxEnvFor<FEN>,
117    ) -> eyre::Result<()>;
118
119    /// Runs a closure with a fresh nested EVM using the current environment and database.
120    /// Unlike `with_nested_evm`, this starts an independent journal and does not write back.
121    /// The caller is responsible for state merging. Used by `executeTransactionCall`.
122    /// Returns the final EVM environment after the closure runs (consumed without cloning).
123    #[allow(clippy::type_complexity)]
124    fn with_fresh_nested_evm(
125        &mut self,
126        cheats: &mut Cheatcodes<FEN>,
127        ecx: &mut FoundryContextFor<'_, FEN>,
128        chain_context: ChainFor<FEN>,
129        f: NestedEvmClosureFor<'_, FEN>,
130    ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
131
132    /// Simulates `console.log` invocation.
133    fn console_log(&mut self, msg: &str);
134
135    /// Returns a mutable reference to the tracing inspector if it is available.
136    fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
137        None
138    }
139}
140
141/// Builds a sub-EVM from the current context and executes the given CREATE frame.
142pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
143    executor: &mut dyn CheatcodesExecutor<FEN>,
144    inputs: CreateInputs,
145    ccx: &mut CheatsCtxt<'_, '_, FEN>,
146) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
147    let fee_token = ccx.tx_fee_token();
148    let tx_origin = ccx.tx_caller();
149    let mut inputs = Some(inputs);
150    let mut outcome = None;
151    executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
152        evm.tx_mut().set_fee_token(fee_token);
153        evm.tx_mut().set_caller(tx_origin);
154        let inputs = inputs.take().unwrap();
155        evm.journal_inner_mut().depth += 1;
156
157        let frame = FrameInput::Create(Box::new(inputs));
158
159        let result = match evm.run_execution(frame)? {
160            FrameResult::Call(_) => unreachable!(),
161            FrameResult::Create(create) => create,
162        };
163
164        evm.journal_inner_mut().depth -= 1;
165
166        outcome = Some(result);
167        Ok(())
168    })?;
169    Ok(outcome.unwrap())
170}
171
172/// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an
173/// inspector.
174#[derive(Debug, Default, Clone, Copy)]
175struct TransparentCheatcodesExecutor;
176
177impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
178    fn with_nested_evm(
179        &mut self,
180        cheats: &mut Cheatcodes<FEN>,
181        ecx: &mut FoundryContextFor<'_, FEN>,
182        f: NestedEvmClosureFor<'_, FEN>,
183    ) -> Result<(), EVMError<DatabaseError>> {
184        with_inherited_evm::<FEN::EvmFactory, _>(ecx, cheats, f)
185    }
186
187    fn with_fresh_nested_evm(
188        &mut self,
189        cheats: &mut Cheatcodes<FEN>,
190        ecx: &mut FoundryContextFor<'_, FEN>,
191        chain_context: ChainFor<FEN>,
192        f: NestedEvmClosureFor<'_, FEN>,
193    ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
194        let depth = ecx.journal().depth();
195        let evm_env = ecx.evm_clone();
196        let (db, _) = ecx.db_journal_inner_mut();
197        let mut evm =
198            FEN::EvmFactory::default().create_nested_evm_with_inspector(db, evm_env, cheats);
199        evm.journal_inner_mut().depth = depth;
200        *evm.chain_mut() = chain_context;
201        f(&mut *evm)?;
202        Ok(evm.to_evm_env())
203    }
204
205    fn transact_on_db(
206        &mut self,
207        cheats: &mut Cheatcodes<FEN>,
208        ecx: &mut FoundryContextFor<'_, FEN>,
209        fork_id: Option<U256>,
210        transaction: B256,
211    ) -> eyre::Result<ContextUpdateFor<EvmFactoryFor<FEN>>> {
212        let evm_env = ecx.evm_clone();
213        let outer_tx_env = ecx.tx_clone();
214        let (db, inner) = ecx.db_journal_inner_mut();
215        db.transact(fork_id, transaction, evm_env, &outer_tx_env, inner, cheats)
216    }
217
218    fn transact_from_tx_on_db(
219        &mut self,
220        cheats: &mut Cheatcodes<FEN>,
221        ecx: &mut FoundryContextFor<'_, FEN>,
222        tx: TxEnvFor<FEN>,
223    ) -> eyre::Result<()> {
224        let evm_env = ecx.evm_clone();
225        let (db, inner) = ecx.db_journal_inner_mut();
226        db.transact_from_tx(tx, evm_env, inner, cheats)
227    }
228
229    fn console_log(&mut self, _msg: &str) {}
230}
231
232macro_rules! try_or_return {
233    ($e:expr) => {
234        match $e {
235            Ok(v) => v,
236            Err(_) => return,
237        }
238    };
239}
240
241/// Contains additional, test specific resources that should be kept for the duration of the test
242#[derive(Debug, Default)]
243pub struct TestContext {
244    /// Buffered readers for files opened for reading (path => BufReader mapping)
245    pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
246}
247
248/// Every time we clone `Context`, we want it to be empty
249impl Clone for TestContext {
250    fn clone(&self) -> Self {
251        Default::default()
252    }
253}
254
255impl TestContext {
256    /// Clears the context.
257    pub fn clear(&mut self) {
258        self.opened_read_files.clear();
259    }
260}
261
262/// Helps collecting transactions from different forks.
263#[derive(Clone, Debug)]
264pub struct BroadcastableTransaction<N: Network = Ethereum> {
265    /// The optional RPC URL.
266    pub rpc: Option<String>,
267    /// The transaction to broadcast.
268    pub transaction: TransactionMaybeSigned<N>,
269}
270
271#[derive(Clone, Debug, Copy)]
272pub struct RecordDebugStepInfo {
273    /// The debug trace node index when the recording starts.
274    pub start_node_idx: usize,
275    /// The original tracer config when the recording starts.
276    pub original_tracer_config: TracingInspectorConfig,
277}
278
279/// A callback registered for a storage access hook.
280#[derive(Clone, Copy, Debug, PartialEq, Eq)]
281pub struct StorageHook {
282    /// Contract that receives the callback.
283    pub callback_target: Address,
284    /// Callback function selector.
285    pub callback_selector: [u8; 4],
286}
287
288#[derive(Clone, Debug)]
289enum PendingStorageHook {
290    Load {
291        account: Address,
292        slot: U256,
293        hook: StorageHook,
294    },
295    Store {
296        account: Address,
297        slot: U256,
298        old_value: U256,
299        mapping: Option<(B256, Vec<B256>)>,
300        hook: StorageHook,
301    },
302}
303
304#[derive(Clone, Debug)]
305struct ActiveStorageHook {
306    parent_depth: usize,
307    callback_target: Address,
308    callback_input: Bytes,
309    saved_gas: Gas,
310    saved_return_data: Bytes,
311    saved_stack_item: Option<U256>,
312    journal_start: usize,
313    inspector_state: StorageHookInspectorState,
314    outcome: Option<(InstructionResult, Bytes)>,
315}
316
317#[derive(Clone, Debug)]
318struct StorageHookInspectorState {
319    accesses: RecordAccess,
320    recording_accesses: bool,
321    mapping_slots: Option<AddressHashMap<MappingSlots>>,
322    recorded_logs: Option<Vec<Vm::Log>>,
323    mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
324    mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
325    expected_revert: Option<ExpectedRevert>,
326    assume_no_revert: Option<AssumeNoRevert>,
327    expected_calls: ExpectedCallTracker,
328    expected_emits: ExpectedEmitTracker,
329    expected_creates: Vec<ExpectedCreate>,
330}
331
332/// Holds gas metering state.
333#[derive(Clone, Debug, Default)]
334pub struct GasMetering {
335    /// True if gas metering is paused.
336    pub paused: bool,
337    /// True if gas metering was resumed or reset during the test.
338    /// Used to reconcile gas when frame ends (if spent less than refunded).
339    pub touched: bool,
340    /// True if gas metering should be reset to frame limit.
341    pub reset: bool,
342    /// Stores paused gas frames.
343    pub paused_frames: Vec<Gas>,
344
345    /// The group and name of the active snapshot.
346    pub active_gas_snapshot: Option<(String, String)>,
347
348    /// Cache of the amount of gas used in previous call.
349    /// This is used by the `lastCallGas` cheatcode.
350    pub last_call_gas: Option<crate::Vm::Gas>,
351    /// Gas used by `snapshotGasLastCall`.
352    pub(crate) last_call_snapshot_gas_used: u64,
353
354    /// Cache of the amount of gas used in previous call or create frame.
355    /// This is used by the `lastFrameGas` cheatcode.
356    pub last_frame_gas: Option<crate::Vm::Gas>,
357    /// Gas used by `snapshotGasLastFrame`.
358    pub(crate) last_frame_snapshot_gas_used: u64,
359
360    /// Post-refund gas used by the isolated transaction wrapping the current frame, and the
361    /// account-creation state gas in it that the outer opcode charged before entering the frame.
362    isolated_snapshot_gas_used: Option<(u64, u64)>,
363
364    /// Caller depth, gas charged to the last isolated frame, and the transaction gas that replaces
365    /// it in the next region sample at that depth.
366    pending_isolated_region_gas: Option<(usize, u64, u64)>,
367
368    /// True if gas recording is enabled.
369    pub recording: bool,
370    /// The gas used in the last frame.
371    pub last_gas_used: u64,
372    /// Gas records for the active snapshots.
373    pub gas_records: Vec<GasRecord>,
374}
375
376impl GasMetering {
377    /// Start the gas recording.
378    pub const fn start(&mut self) {
379        self.recording = true;
380        self.last_gas_used = 0;
381        self.pending_isolated_region_gas = None;
382    }
383
384    /// Stop the gas recording.
385    pub const fn stop(&mut self) {
386        self.recording = false;
387    }
388
389    /// Resume paused gas metering.
390    pub fn resume(&mut self) {
391        if self.paused {
392            self.paused = false;
393            self.touched = true;
394        }
395        self.paused_frames.clear();
396    }
397
398    /// Reset gas to limit.
399    pub fn reset(&mut self) {
400        self.paused = false;
401        self.touched = true;
402        self.reset = true;
403        self.paused_frames.clear();
404    }
405
406    /// Preserves the receipt gas of an isolated transaction for gas snapshots.
407    ///
408    /// `precharged_state` is the account-creation state gas in `gas_used` that the outer opcode
409    /// already charged before entering the isolated frame.
410    pub const fn set_isolated_snapshot_gas_used(&mut self, gas_used: u64, precharged_state: u64) {
411        self.isolated_snapshot_gas_used = Some((gas_used, precharged_state));
412    }
413
414    /// Takes the receipt gas of the isolated transaction that wrapped the ending frame.
415    ///
416    /// Region snapshots replace the gas charged to the frame with the transaction gas, without
417    /// changing the interpreter's gas. The transaction gas can be lower because of refunds, or
418    /// higher because of intrinsic gas that did not fit in the frame's budget.
419    const fn take_isolated_snapshot_gas_used(&mut self, depth: usize, gas: &Gas) -> Option<u64> {
420        let Some((gas_used, precharged_state)) = self.isolated_snapshot_gas_used.take() else {
421            return None;
422        };
423        if self.recording {
424            // The caller's sample already includes the precharged state gas.
425            self.pending_isolated_region_gas =
426                Some((depth, gas.total_gas_spent(), gas_used.saturating_sub(precharged_state)));
427        }
428        Some(gas_used)
429    }
430}
431
432/// Holds data about arbitrary storage.
433#[derive(Clone, Debug, Default)]
434pub struct ArbitraryStorage {
435    /// Mapping of arbitrary storage addresses to generated values (slot, arbitrary value).
436    /// (SLOADs return random value if storage slot wasn't accessed).
437    /// Changed values are recorded and used to copy storage to different addresses.
438    values: HashMap<Address, HashMap<U256, U256>>,
439    /// Mapping of address with storage copied to arbitrary storage address source.
440    copies: HashMap<Address, Address>,
441    /// Address with storage slots that should be overwritten even if previously set.
442    overwrites: HashSet<Address>,
443    /// Storage slots explicitly written with `vm.store`, grouped by address.
444    explicit_slots: HashMap<Address, HashSet<U256>>,
445}
446
447impl ArbitraryStorage {
448    /// Marks an address with arbitrary storage.
449    pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
450        self.values.insert(*address, HashMap::default());
451        self.explicit_slots.remove(address);
452        if overwrite {
453            self.overwrites.insert(*address);
454        } else {
455            self.overwrites.remove(address);
456        }
457    }
458
459    /// Maps an address that copies storage with the arbitrary storage address.
460    pub fn mark_copy(&mut self, from: &Address, to: &Address) {
461        if self.values.contains_key(from) {
462            self.copies.insert(*to, *from);
463            if let Some(slots) = self.explicit_slots.get(from).cloned() {
464                self.explicit_slots.insert(*to, slots);
465            } else {
466                self.explicit_slots.remove(to);
467            }
468        }
469    }
470
471    /// Marks a slot as explicitly written if the address has arbitrary or copied storage.
472    fn mark_explicit(&mut self, address: Address, slot: U256) {
473        if self.values.contains_key(&address) || self.copies.contains_key(&address) {
474            self.explicit_slots.entry(address).or_default().insert(slot);
475        }
476    }
477
478    /// Returns whether a slot was explicitly written for the given address.
479    fn is_explicit(&self, address: Address, slot: U256) -> bool {
480        self.explicit_slots.get(&address).is_some_and(|slots| slots.contains(&slot))
481    }
482
483    /// Returns addresses explicitly marked with arbitrary storage.
484    fn targets(&self) -> impl Iterator<Item = Address> + '_ {
485        self.values.keys().copied()
486    }
487
488    /// Returns addresses explicitly marked with arbitrary storage and whether nonzero slots are
489    /// overwritten.
490    fn target_overwrite_modes(&self) -> impl Iterator<Item = (Address, bool)> + '_ {
491        self.values.keys().map(|address| (*address, self.overwrites.contains(address)))
492    }
493
494    /// Returns addresses that copy storage from arbitrary-storage targets.
495    fn copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
496        self.copies.keys().copied()
497    }
498
499    /// Returns copied arbitrary-storage targets and their source address.
500    fn copied_target_sources(&self) -> impl Iterator<Item = (Address, Address)> + '_ {
501        self.copies.iter().map(|(target, source)| (*target, *source))
502    }
503
504    /// Caches a concrete value for a slot on an arbitrary-storage address or copied target.
505    fn cache_value(&mut self, address: Address, slot: U256, data: U256) {
506        if let Some(values) = self.values.get_mut(&address) {
507            values.insert(slot, data);
508            return;
509        }
510
511        let Some(source) = self.copies.get(&address).copied() else {
512            return;
513        };
514        if let Some(values) = self.values.get_mut(&source) {
515            values.insert(slot, data);
516        }
517    }
518
519    /// Returns a cached arbitrary value for a slot.
520    fn cached_value(&self, address: Address, slot: U256) -> Option<U256> {
521        self.values.get(&address).and_then(|values| values.get(&slot)).copied()
522    }
523
524    /// Saves arbitrary storage value for a given address:
525    /// - store value in changed values cache.
526    /// - update account's storage with given value.
527    pub fn save<CTX: ContextTr>(
528        &mut self,
529        ecx: &mut CTX,
530        address: Address,
531        slot: U256,
532        data: U256,
533    ) {
534        self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
535        if ecx.journal_mut().load_account(address).is_ok() {
536            ecx.journal_mut()
537                .sstore(address, slot, data)
538                .expect("could not set arbitrary storage value");
539        }
540    }
541
542    /// Copies arbitrary storage value from source address to the given target address:
543    /// - if a value is present in arbitrary values cache, then update target storage and return
544    ///   existing value.
545    /// - if no value was yet generated for given slot, then save new value in cache and update both
546    ///   source and target storages.
547    pub fn copy<CTX: ContextTr>(
548        &mut self,
549        ecx: &mut CTX,
550        target: Address,
551        slot: U256,
552        new_value: U256,
553    ) -> U256 {
554        let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
555        let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
556        let value = match storage_cache.get(&slot) {
557            Some(value) => *value,
558            None => {
559                storage_cache.insert(slot, new_value);
560                // Update source storage with new value.
561                if ecx.journal_mut().load_account(*source).is_ok() {
562                    ecx.journal_mut()
563                        .sstore(*source, slot, new_value)
564                        .expect("could not copy arbitrary storage value");
565                }
566                new_value
567            }
568        };
569        // Update target storage with new value.
570        if ecx.journal_mut().load_account(target).is_ok() {
571            ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
572        }
573        value
574    }
575}
576
577/// List of transactions that can be broadcasted.
578pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
579
580#[derive(Clone, Copy, Debug, PartialEq, Eq)]
581enum CreatedAccountsFrameKind {
582    Call,
583    Create,
584}
585
586#[derive(Clone, Copy, Debug)]
587struct CreatedAccountsFrame {
588    kind: CreatedAccountsFrameKind,
589    depth: usize,
590    checkpoint: usize,
591}
592
593#[derive(Clone, Copy, Debug)]
594struct CreatedAccountChange {
595    fork_id: Option<LocalForkId>,
596    address: Address,
597    creation: usize,
598    previous: Option<usize>,
599    committed: bool,
600}
601
602#[derive(Clone, Debug)]
603struct CreatedAccountsSnapshot {
604    fork_id: Option<LocalForkId>,
605    bindings: AddressHashMap<usize>,
606}
607
608/// An EVM inspector that handles calls to various cheatcodes, each with their own behavior.
609///
610/// Cheatcodes can be called by contracts during execution to modify the VM environment, such as
611/// mocking addresses, signatures and altering call reverts.
612///
613/// Executing cheatcodes can be very powerful. Most cheatcodes are limited to evm internals, but
614/// there are also cheatcodes like `ffi` which can execute arbitrary commands or `writeFile` and
615/// `readFile` which can manipulate files of the filesystem. Therefore, several restrictions are
616/// implemented for these cheatcodes:
617/// - `ffi`, and file cheatcodes are _always_ opt-in (via foundry config) and never enabled by
618///   default: all respective cheatcode handlers implement the appropriate checks
619/// - File cheatcodes require explicit permissions which paths are allowed for which operation, see
620///   `Config.fs_permission`
621/// - Only permitted accounts are allowed to execute cheatcodes in forking mode, this ensures no
622///   contract deployed on the live network is able to execute cheatcodes by simply calling the
623///   cheatcode address: by default, the caller, test contract and newly deployed contracts are
624///   allowed to execute cheatcodes
625#[derive(Clone, Debug)]
626pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
627    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities
628    pub analysis: Option<CheatcodeAnalysis>,
629
630    /// The block environment
631    ///
632    /// Used in the cheatcode handler to overwrite the block environment separately from the
633    /// execution block environment.
634    pub block: Option<BlockEnvFor<FEN>>,
635
636    /// The active fork block override updated by a fork-switching cheatcode.
637    ///
638    /// This persists fork changes made through a copy-on-write backend between invariant calls.
639    pub fork_block_number_override: Option<u64>,
640
641    /// Currently active EIP-7702 delegations that will be consumed when building the next
642    /// transaction. Set by `vm.attachDelegation()` and consumed via `.take()` during
643    /// transaction construction.
644    pub active_delegations: Vec<SignedAuthorization>,
645
646    /// The active EIP-4844 blob that will be attached to the next call.
647    pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
648
649    /// The gas price.
650    ///
651    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
652    /// in the execution environment.
653    pub gas_price: Option<u128>,
654
655    /// Address labels
656    pub labels: AddressHashMap<String>,
657
658    /// Prank information, mapped to the call depth where pranks were added.
659    pub pranks: BTreeMap<usize, Prank>,
660
661    /// Expected revert information
662    pub expected_revert: Option<ExpectedRevert>,
663
664    /// Assume next call can revert and discard fuzz run if it does.
665    pub assume_no_revert: Option<AssumeNoRevert>,
666
667    /// Additional diagnostic for reverts
668    pub fork_revert_diagnostic: Option<RevertDiagnostic>,
669
670    /// Recorded storage reads and writes
671    pub accesses: RecordAccess,
672
673    /// Whether storage access recording is currently active
674    pub recording_accesses: bool,
675
676    /// Recorded account accesses (calls, creates) organized by relative call depth, where the
677    /// topmost vector corresponds to accesses at the depth at which account access recording
678    /// began. Each vector in the matrix represents a list of accesses at a specific call
679    /// depth. Once that call context has ended, the last vector is removed from the matrix and
680    /// merged into the previous vector.
681    pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
682
683    /// Account accesses performed by the test runner before user code can start recording.
684    pending_account_diffs: Option<Arc<[AccountAccess]>>,
685
686    /// Completed account accesses prepended to the active user recording session.
687    recorded_account_diffs_prefix: Option<Arc<[AccountAccess]>>,
688
689    /// Successfully created accounts in execution order.
690    created_accounts: Vec<Address>,
691
692    /// The creation currently represented by each address on each fork.
693    created_account_bindings: HashMap<(Option<LocalForkId>, Address), usize>,
694
695    /// Revertible changes to creation bindings made by EVM create frames.
696    created_account_changes: Vec<CreatedAccountChange>,
697
698    /// Creation-list checkpoints for frames observed by this inspector.
699    created_accounts_frames: Vec<CreatedAccountsFrame>,
700
701    /// Creation lists captured by state snapshots.
702    created_accounts_snapshots: HashMap<U256, CreatedAccountsSnapshot>,
703
704    /// The information of the debug step recording.
705    pub record_debug_steps_info: Option<RecordDebugStepInfo>,
706
707    /// Recorded logs
708    pub recorded_logs: Option<Vec<crate::Vm::Log>>,
709
710    /// Mocked calls
711    // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext`
712    pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
713
714    /// Mocked functions. Maps target address to be mocked to pair of (calldata, mock address).
715    pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
716
717    /// Expected calls
718    pub expected_calls: ExpectedCallTracker,
719    /// Expected emits
720    pub expected_emits: ExpectedEmitTracker,
721    /// Expected creates
722    pub expected_creates: Vec<ExpectedCreate>,
723
724    /// Map of context depths to memory offset ranges that may be written to within the call depth.
725    pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
726
727    /// Current broadcasting information
728    pub broadcast: Option<Broadcast>,
729
730    /// Scripting based transactions
731    pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
732
733    /// Current EIP-2930 access lists.
734    pub access_list: Option<AccessList>,
735
736    /// Additional, user configurable context this Inspector has access to when inspecting a call.
737    pub config: Arc<CheatsConfig>,
738
739    /// Additional addresses recognized as cheatcode contracts by this executor.
740    pub extra_cheatcode_addresses: &'static [Address],
741
742    /// Test-scoped context holding data that needs to be reset every test run
743    pub test_context: TestContext,
744
745    /// Revert payloads minted by the `skip` cheatcode during the current test call.
746    ///
747    /// A top-level revert is only classified as a skip when its data byte-equals one of these
748    /// payloads, so user-crafted `FOUNDRY::SKIP` revert data never skips a test on its own.
749    pub skip_payloads: Vec<Bytes>,
750
751    /// Whether to commit FS changes such as file creations, writes and deletes.
752    /// Used to prevent duplicate changes file executing non-committing calls.
753    pub fs_commit: bool,
754
755    /// Serialized JSON values.
756    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
757    pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
758
759    /// All recorded ETH `deal`s.
760    pub eth_deals: Vec<DealRecord>,
761
762    /// Gas metering state.
763    pub gas_metering: GasMetering,
764
765    /// Contains gas snapshots made over the course of a test suite.
766    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
767    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
768
769    /// Mapping slots.
770    pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
771
772    /// The current program counter.
773    pub pc: usize,
774    /// Breakpoints supplied by the `breakpoint` cheatcode.
775    /// `char -> (address, pc)`
776    pub breakpoints: Breakpoints,
777
778    /// Whether the next contract creation should be intercepted to return its initcode.
779    pub intercept_next_create_call: bool,
780
781    /// Optional cheatcodes `TestRunner`. Used for generating random values from uint and int
782    /// strategies.
783    test_runner: Option<TestRunner>,
784
785    /// Ignored traces.
786    pub ignored_traces: IgnoredTraces,
787
788    /// Addresses with arbitrary storage.
789    pub arbitrary_storage: Option<ArbitraryStorage>,
790
791    /// SLOAD callbacks keyed by effective storage address.
792    storage_load_hooks: AddressHashMap<StorageHook>,
793    /// SSTORE callbacks keyed by effective storage address.
794    storage_store_hooks: AddressHashMap<StorageHook>,
795    /// Mapping SSTORE callbacks keyed by effective storage address and root slot.
796    mapping_storage_store_hooks: AddressHashMap<HashMap<B256, StorageHook>>,
797    /// Execution-local provenance used only by mapping storage hooks.
798    storage_hook_mapping_slots: AddressHashMap<MappingSlots>,
799    /// A 64-byte Keccak operation awaiting successful completion.
800    pending_mapping_hash: Option<PendingMappingHash>,
801    /// Whether any storage hook map contains a callback.
802    storage_hooks_registered: bool,
803    /// Matching storage access captured before the opcode executes.
804    pending_storage_hook: Option<PendingStorageHook>,
805    /// Synthetic callback frame currently executing or awaiting parent cleanup.
806    active_storage_hook: Option<ActiveStorageHook>,
807
808    /// Deprecated cheatcodes mapped to the reason. Used to report warnings on test results.
809    pub deprecated: HashMap<&'static str, Option<&'static str>>,
810    /// Main script contract, when script execution protection is enabled.
811    pub script_address: Option<Address>,
812    /// Unlocked wallets used in scripts and testing of scripts.
813    pub wallets: Option<Wallets>,
814    /// Parsed secp256k1 private-key signers for repeated `vm.addr` / `vm.sign` calls.
815    pub private_key_signers: HashMap<U256, PrivateKeySigner>,
816    /// Signatures identifier for decoding events and functions
817    signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
818    /// Used to determine whether the broadcasted call has dynamic gas limit.
819    pub dynamic_gas_limit: bool,
820    // Custom execution evm version.
821    pub execution_evm_version: Option<SpecFor<FEN>>,
822
823    /// Per-fork opcode environment overrides and their state-snapshot copies.
824    pub env_overrides: EnvOverrideState,
825
826    /// Per-state-snapshot copies of [`Self::fork_block_number_override`].
827    pub fork_block_number_override_snapshots: HashMap<U256, Option<u64>>,
828
829    /// Transaction-position context and Monad's reserve-balance-tracker state captured atomically
830    /// alongside state snapshots.
831    #[cfg(feature = "monad")]
832    pub context_snapshots:
833        HashMap<U256, (ChainFor<FEN>, monad_revm::reserve_balance::tracker::ReserveBalanceTracker)>,
834
835    /// Whether we are currently executing inside an isolation context, i.e.
836    /// the synthetic inner transaction wrapped by
837    /// `InspectorStackRefMut::transact_inner` (used by `--gas-report` and
838    /// `--isolate`).
839    ///
840    /// Toggled by the inspector stack around the inner `transact_raw`
841    /// call. Cheatcodes that mutate the tx/block env consult this flag and
842    /// route the change through `EnvOverrides` instead of the actual env
843    /// when `true`, so they don't fight with the fee-accounting zeroing.
844    pub in_isolation_context: bool,
845
846    /// Journal restored by a state snapshot inside an isolated transaction, to be applied to its
847    /// suspended parent alongside the returned state.
848    pub pending_isolated_snapshot_journal: Option<Vec<JournalEntry>>,
849
850    /// Whether snapshot restorations belong to the tracked transaction (an isolated call, or the
851    /// top-level transaction when isolation is disabled) and must be unwound by failing frames.
852    pub track_isolated_snapshots: bool,
853
854    /// Journals replaced by snapshot restorations that may need to be reinstated when an
855    /// enclosing frame of the tracked transaction fails.
856    pub isolated_snapshot_restores: Vec<JournaledState>,
857
858    /// Whether the next snapshot restoration is the first one since its calling frame started,
859    /// and so must be recorded in `isolated_snapshot_restores`.
860    pub capture_isolated_snapshot_restore: bool,
861
862    /// Depth of the in-flight `deployCode` call, whose create frame runs one level deeper.
863    pub deploy_code_depth: Option<usize>,
864}
865
866// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
867// `CheatsConfig` which is unused, and inside it `ProjectPathsConfig` is relatively expensive to
868// create.
869impl Default for Cheatcodes {
870    fn default() -> Self {
871        Self::new(Arc::default())
872    }
873}
874
875impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
876    /// Creates a new `Cheatcodes` with the given settings.
877    pub fn new(config: Arc<CheatsConfig>) -> Self {
878        Self {
879            analysis: None,
880            fs_commit: true,
881            labels: config.labels.clone(),
882            config,
883            extra_cheatcode_addresses: &[],
884            block: Default::default(),
885            fork_block_number_override: Default::default(),
886            active_delegations: Default::default(),
887            active_blob_sidecar: Default::default(),
888            gas_price: Default::default(),
889            pranks: Default::default(),
890            expected_revert: Default::default(),
891            assume_no_revert: Default::default(),
892            fork_revert_diagnostic: Default::default(),
893            accesses: Default::default(),
894            recording_accesses: Default::default(),
895            recorded_account_diffs_stack: Default::default(),
896            pending_account_diffs: Default::default(),
897            recorded_account_diffs_prefix: Default::default(),
898            created_accounts: Default::default(),
899            created_account_bindings: Default::default(),
900            created_account_changes: Default::default(),
901            created_accounts_frames: Default::default(),
902            created_accounts_snapshots: Default::default(),
903            recorded_logs: Default::default(),
904            record_debug_steps_info: Default::default(),
905            mocked_calls: Default::default(),
906            mocked_functions: Default::default(),
907            expected_calls: Default::default(),
908            expected_emits: Default::default(),
909            expected_creates: Default::default(),
910            allowed_mem_writes: Default::default(),
911            broadcast: Default::default(),
912            broadcastable_transactions: Default::default(),
913            access_list: Default::default(),
914            test_context: Default::default(),
915            skip_payloads: Default::default(),
916            serialized_jsons: Default::default(),
917            eth_deals: Default::default(),
918            gas_metering: Default::default(),
919            gas_snapshots: Default::default(),
920            mapping_slots: Default::default(),
921            pc: Default::default(),
922            breakpoints: Default::default(),
923            intercept_next_create_call: Default::default(),
924            test_runner: Default::default(),
925            ignored_traces: Default::default(),
926            arbitrary_storage: Default::default(),
927            storage_load_hooks: Default::default(),
928            storage_store_hooks: Default::default(),
929            mapping_storage_store_hooks: Default::default(),
930            storage_hook_mapping_slots: Default::default(),
931            pending_mapping_hash: Default::default(),
932            storage_hooks_registered: Default::default(),
933            pending_storage_hook: Default::default(),
934            active_storage_hook: Default::default(),
935            deprecated: Default::default(),
936            script_address: Default::default(),
937            wallets: Default::default(),
938            private_key_signers: Default::default(),
939            signatures_identifier: Default::default(),
940            dynamic_gas_limit: Default::default(),
941            execution_evm_version: None,
942            env_overrides: Default::default(),
943            fork_block_number_override_snapshots: Default::default(),
944            #[cfg(feature = "monad")]
945            context_snapshots: Default::default(),
946            in_isolation_context: false,
947            pending_isolated_snapshot_journal: None,
948            track_isolated_snapshots: false,
949            isolated_snapshot_restores: Vec::new(),
950            capture_isolated_snapshot_restore: false,
951            deploy_code_depth: None,
952        }
953    }
954
955    /// Sets additional addresses recognized as cheatcode contracts.
956    #[inline]
957    pub const fn set_extra_cheatcode_addresses(&mut self, addresses: &'static [Address]) {
958        self.extra_cheatcode_addresses = addresses;
959    }
960
961    /// Enables cheatcode analysis capabilities by providing a solar compiler instance.
962    pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
963        self.analysis = Some(analysis);
964    }
965
966    /// Starts an internal account diff recording session for test runner setup.
967    pub fn start_internal_state_diff_recording(&mut self) -> bool {
968        if self.recorded_account_diffs_stack.is_some()
969            || self.recorded_account_diffs_prefix.is_some()
970        {
971            return false;
972        }
973        self.recorded_account_diffs_stack = Some(Default::default());
974        true
975    }
976
977    /// Stops an internal account diff recording session without leaving recording enabled.
978    pub fn stop_internal_state_diff_recording(&mut self) -> Vec<AccountAccess> {
979        self.recorded_account_diffs_stack.take().unwrap_or_default().into_iter().flatten().collect()
980    }
981
982    /// Makes account accesses captured by the test runner available to the next recording session.
983    pub fn set_pending_account_diffs(&mut self, accesses: Vec<AccountAccess>) {
984        self.pending_account_diffs = (!accesses.is_empty()).then(|| Arc::from(accesses));
985    }
986
987    /// Starts a user account diff recording session, including pending test runner accesses.
988    pub fn start_state_diff_recording(&mut self) {
989        self.recorded_account_diffs_prefix = self.pending_account_diffs.take();
990        self.recorded_account_diffs_stack = Some(Default::default());
991    }
992
993    /// Returns completed and active account accesses in execution order.
994    pub fn recorded_account_diffs(&self) -> impl Iterator<Item = &AccountAccess> {
995        self.recorded_account_diffs_prefix
996            .iter()
997            .flat_map(|prefix| prefix.iter())
998            .chain(self.recorded_account_diffs_stack.iter().flatten().flatten())
999    }
1000
1001    /// Takes completed account accesses from the active user recording session.
1002    pub fn take_recorded_account_diffs_prefix(&mut self) -> Vec<AccountAccess> {
1003        self.recorded_account_diffs_prefix
1004            .take()
1005            .map(|prefix| prefix.as_ref().to_vec())
1006            .unwrap_or_default()
1007    }
1008
1009    /// Returns the current creation bound to each address on the given fork.
1010    pub(crate) fn created_account_bindings(
1011        &self,
1012        fork_id: Option<LocalForkId>,
1013    ) -> AddressHashMap<usize> {
1014        self.created_account_bindings
1015            .iter()
1016            .filter_map(|(&(event_fork_id, address), &creation)| {
1017                (event_fork_id == fork_id).then_some((address, creation))
1018            })
1019            .collect()
1020    }
1021
1022    /// Returns successfully created accounts bound to the given fork in creation order.
1023    pub(crate) fn created_accounts(&self, fork_id: Option<LocalForkId>) -> Vec<Address> {
1024        let bindings = self.created_account_bindings(fork_id);
1025        self.created_accounts
1026            .iter()
1027            .enumerate()
1028            .filter_map(|(index, &address)| {
1029                (bindings.get(&address) == Some(&index)).then_some(address)
1030            })
1031            .collect()
1032    }
1033
1034    /// Records a successfully created account.
1035    pub(crate) fn record_created_account(
1036        &mut self,
1037        fork_id: Option<LocalForkId>,
1038        address: Address,
1039    ) {
1040        let creation = self.created_accounts.len();
1041        self.created_accounts.push(address);
1042        let previous = self.created_account_bindings.insert((fork_id, address), creation);
1043        self.created_account_changes.push(CreatedAccountChange {
1044            fork_id,
1045            address,
1046            creation,
1047            previous,
1048            committed: false,
1049        });
1050    }
1051
1052    /// Keeps creation bindings saved with an outgoing fork across later frame reverts.
1053    pub(crate) fn commit_created_account_changes(&mut self, fork_id: Option<LocalForkId>) {
1054        for change in &mut self.created_account_changes {
1055            if change.fork_id == fork_id {
1056                change.committed = true;
1057            }
1058        }
1059    }
1060
1061    /// Records shared pre-fork creations on a newly selected fork without replacing local ones.
1062    pub(crate) fn record_initial_created_accounts(
1063        &mut self,
1064        fork_id: Option<LocalForkId>,
1065        accounts: impl IntoIterator<Item = (Address, usize)>,
1066    ) {
1067        for (address, creation) in accounts {
1068            self.created_account_bindings.entry((fork_id, address)).or_insert(creation);
1069        }
1070    }
1071
1072    /// Records creations propagated to a fork with persistent account state.
1073    pub(crate) fn record_propagated_accounts(
1074        &mut self,
1075        fork_id: Option<LocalForkId>,
1076        accounts: impl IntoIterator<Item = (Address, usize)>,
1077    ) {
1078        self.created_account_bindings
1079            .extend(accounts.into_iter().map(|(address, creation)| ((fork_id, address), creation)));
1080    }
1081
1082    /// Captures creation ordering alongside a state snapshot.
1083    pub(crate) fn snapshot_created_accounts(
1084        &mut self,
1085        snapshot_id: U256,
1086        fork_id: Option<LocalForkId>,
1087    ) {
1088        let bindings = self.created_account_bindings(fork_id);
1089        self.created_accounts_snapshots
1090            .insert(snapshot_id, CreatedAccountsSnapshot { fork_id, bindings });
1091    }
1092
1093    /// Restores creation ordering from a state snapshot.
1094    pub(crate) fn revert_created_accounts(&mut self, snapshot_id: U256, remove: bool) {
1095        let snapshot = if remove {
1096            self.created_accounts_snapshots.remove(&snapshot_id)
1097        } else {
1098            self.created_accounts_snapshots.get(&snapshot_id).cloned()
1099        };
1100        if let Some(snapshot) = snapshot {
1101            self.created_account_bindings.retain(|(fork_id, _), _| *fork_id != snapshot.fork_id);
1102            self.created_account_bindings.extend(
1103                snapshot
1104                    .bindings
1105                    .into_iter()
1106                    .map(|(address, creation)| ((snapshot.fork_id, address), creation)),
1107            );
1108        }
1109    }
1110
1111    /// Deletes one captured creation-order snapshot.
1112    pub(crate) fn delete_created_accounts_snapshot(&mut self, snapshot_id: U256) {
1113        self.created_accounts_snapshots.remove(&snapshot_id);
1114    }
1115
1116    /// Deletes all captured creation-order snapshots.
1117    pub(crate) fn clear_created_accounts_snapshots(&mut self) {
1118        self.created_accounts_snapshots.clear();
1119    }
1120
1121    fn start_created_accounts_frame(
1122        &mut self,
1123        reset: bool,
1124        kind: CreatedAccountsFrameKind,
1125        depth: usize,
1126    ) {
1127        if reset {
1128            self.created_accounts.clear();
1129            self.created_account_bindings.clear();
1130            self.created_account_changes.clear();
1131            self.created_accounts_frames.clear();
1132            // Earlier snapshots contain no creations from the new root transaction.
1133            for snapshot in self.created_accounts_snapshots.values_mut() {
1134                snapshot.bindings.clear();
1135            }
1136        }
1137        self.created_accounts_frames.push(CreatedAccountsFrame {
1138            kind,
1139            depth,
1140            checkpoint: self.created_account_changes.len(),
1141        });
1142    }
1143
1144    fn finish_created_accounts_frame(
1145        &mut self,
1146        success: bool,
1147        kind: CreatedAccountsFrameKind,
1148        depth: usize,
1149    ) {
1150        let Some(frame) = self
1151            .created_accounts_frames
1152            .last()
1153            .copied()
1154            .filter(|frame| frame.kind == kind && frame.depth == depth)
1155        else {
1156            return;
1157        };
1158        let checkpoint = frame.checkpoint;
1159        self.created_accounts_frames.pop();
1160        if !success {
1161            while self.created_account_changes.len() > checkpoint {
1162                let change = self.created_account_changes.pop().expect("length checked");
1163                if change.committed {
1164                    continue;
1165                }
1166                let key = (change.fork_id, change.address);
1167                if self.created_account_bindings.get(&key) != Some(&change.creation) {
1168                    continue;
1169                }
1170                if let Some(previous) = change.previous {
1171                    self.created_account_bindings.insert(key, previous);
1172                } else {
1173                    self.created_account_bindings.remove(&key);
1174                }
1175            }
1176        }
1177    }
1178
1179    /// Returns the configured prank at given depth or the first prank configured at a lower depth.
1180    /// For example, if pranks configured for depth 1, 3 and 5, the prank for depth 4 is the one
1181    /// configured at depth 3.
1182    pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
1183        self.pranks.range(..=depth).last().map(|(_, prank)| prank)
1184    }
1185
1186    /// Returns the configured wallets if available, else creates a new instance.
1187    pub fn wallets(&mut self) -> &Wallets {
1188        self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
1189    }
1190
1191    /// Sets the unlocked wallets.
1192    pub fn set_wallets(&mut self, wallets: Wallets) {
1193        self.wallets = Some(wallets);
1194    }
1195
1196    /// Adds a delegation to the active delegations list.
1197    pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
1198        self.active_delegations.push(authorization);
1199    }
1200
1201    /// Returns the signatures identifier.
1202    pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
1203        self.signatures_identifier
1204            .get_or_init(|| {
1205                if let Some(artifacts) = &self.config.available_artifacts {
1206                    return SignaturesIdentifier::new_offline_with_abis(
1207                        artifacts.values().map(|contract| &contract.abi),
1208                    )
1209                    .ok();
1210                }
1211                SignaturesIdentifier::new(true).ok()
1212            })
1213            .as_ref()
1214    }
1215
1216    /// Decodes the input data and applies the cheatcode.
1217    fn apply_cheatcode(
1218        &mut self,
1219        ecx: &mut FoundryContextFor<'_, FEN>,
1220        call: &CallInputs,
1221        executor: &mut dyn CheatcodesExecutor<FEN>,
1222    ) -> Result {
1223        // decode the cheatcode call
1224        let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
1225            if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
1226                let msg = format!(
1227                    "unknown cheatcode with selector {selector}; \
1228                     you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
1229                     and the `forge` version"
1230                );
1231                return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
1232            }
1233            e
1234        })?;
1235
1236        let caller = call.transfer_from();
1237
1238        // ensure the caller is allowed to execute cheatcodes,
1239        // but only if the backend is in forking mode
1240        ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1241
1242        apply_dispatch(
1243            &decoded,
1244            &mut CheatsCtxt {
1245                state: self,
1246                ecx,
1247                gas_limit: call.gas_limit,
1248                caller,
1249                is_static: call.is_static,
1250            },
1251            executor,
1252        )
1253    }
1254
1255    /// Decodes the input data and applies Monad-specific cheatcodes.
1256    #[cfg(feature = "monad")]
1257    fn apply_monad_cheatcode(
1258        &mut self,
1259        ecx: &mut FoundryContextFor<'_, FEN>,
1260        call: &CallInputs,
1261    ) -> Result {
1262        let input = call.input.bytes(ecx);
1263        let caller = call.transfer_from();
1264
1265        // ensure the caller is allowed to execute cheatcodes,
1266        // but only if the backend is in forking mode
1267        ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
1268
1269        crate::monad::apply_monad_cheatcode(
1270            &mut CheatsCtxt {
1271                state: self,
1272                ecx,
1273                gas_limit: call.gas_limit,
1274                caller,
1275                is_static: call.is_static,
1276            },
1277            &input,
1278        )
1279    }
1280
1281    /// Grants cheat code access for new contracts if the caller also has
1282    /// cheatcode access or the new contract is created in top most call.
1283    ///
1284    /// There may be cheatcodes in the constructor of the new contract, in order to allow them
1285    /// automatically we need to determine the new address.
1286    fn allow_cheatcodes_on_create(
1287        &self,
1288        ecx: &mut FoundryContextFor<FEN>,
1289        caller: Address,
1290        created_address: Address,
1291    ) {
1292        if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
1293            ecx.db_mut().allow_cheatcode_access(created_address);
1294        }
1295    }
1296
1297    /// Apply EIP-2930 access list.
1298    ///
1299    /// If the transaction type is [TransactionType::Legacy] we need to upgrade it to
1300    /// [TransactionType::Eip2930] in order to use access lists. Other transaction types support
1301    /// access lists themselves.
1302    fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1303        if let Some(access_list) = &self.access_list {
1304            ecx.tx_mut().set_access_list(access_list.clone());
1305
1306            if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
1307                ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
1308            }
1309        }
1310    }
1311
1312    /// Called when there was a revert.
1313    ///
1314    /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's
1315    /// revert would run into issues.
1316    pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
1317        trace!(deals=?self.eth_deals.len(), "rolling back deals");
1318
1319        // Delay revert clean up until expected revert is handled, if set.
1320        if self.expected_revert.is_some() {
1321            return;
1322        }
1323
1324        // we only want to apply cleanup top level
1325        if ecx.journal().depth() > 0 {
1326            return;
1327        }
1328
1329        // Roll back all previously applied deals
1330        // This will prevent overflow issues in revm's [`JournaledState::journal_revert`] routine
1331        // which rolls back any transfers.
1332        while let Some(record) = self.eth_deals.pop() {
1333            if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
1334                acc.info.balance = record.old_balance;
1335            }
1336        }
1337    }
1338
1339    /// Handles a call, accounting for whether the executor will isolate it as a transaction.
1340    ///
1341    /// If `isolate_call` is true, the executor owns the transaction nonce increment when the call
1342    /// proceeds to execution.
1343    pub fn call_with_executor(
1344        &mut self,
1345        ecx: &mut FoundryContextFor<'_, FEN>,
1346        call: &mut CallInputs,
1347        executor: &mut dyn CheatcodesExecutor<FEN>,
1348        isolate_call: bool,
1349    ) -> Option<CallOutcome> {
1350        // Apply custom execution evm version.
1351        if let Some(spec_id) = self.execution_evm_version {
1352            EvmFactoryFor::<FEN>::set_execution_spec(ecx, spec_id);
1353        }
1354
1355        let gas = Gas::new(call.gas_limit);
1356        let curr_depth = ecx.journal().depth();
1357        self.start_created_accounts_frame(
1358            curr_depth == 0,
1359            CreatedAccountsFrameKind::Call,
1360            curr_depth,
1361        );
1362
1363        // At the root call to test function or script `run()`/`setUp()` functions, we are
1364        // decreasing sender nonce to ensure that it matches on-chain nonce once we start
1365        // broadcasting.
1366        if curr_depth == 0 {
1367            let sender = ecx.tx().caller();
1368            let account = match super::evm::journaled_account(ecx, sender) {
1369                Ok(account) => account,
1370                Err(err) => {
1371                    return Some(CallOutcome {
1372                        result: InterpreterResult {
1373                            result: InstructionResult::Revert,
1374                            output: err.abi_encode().into(),
1375                            gas,
1376                        },
1377                        memory_offset: call.return_memory_offset.clone(),
1378                        was_precompile_called: false,
1379                        precompile_call_logs: vec![],
1380                        charged_new_account_state_gas: call.charged_new_account_state_gas,
1381                    });
1382                }
1383            };
1384            let prev = account.info.nonce;
1385            account.info.nonce = prev.saturating_sub(1);
1386
1387            trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
1388        }
1389
1390        if call.transfer_to() == CHEATCODE_ADDRESS {
1391            return match self.apply_cheatcode(ecx, call, executor) {
1392                Ok(retdata) => Some(CallOutcome {
1393                    result: InterpreterResult {
1394                        result: InstructionResult::Return,
1395                        output: retdata.into(),
1396                        gas,
1397                    },
1398                    memory_offset: call.return_memory_offset.clone(),
1399                    was_precompile_called: true,
1400                    precompile_call_logs: vec![],
1401                    charged_new_account_state_gas: call.charged_new_account_state_gas,
1402                }),
1403                Err(err) => Some(CallOutcome {
1404                    result: InterpreterResult {
1405                        result: InstructionResult::Revert,
1406                        output: err.abi_encode().into(),
1407                        gas,
1408                    },
1409                    memory_offset: call.return_memory_offset.clone(),
1410                    was_precompile_called: false,
1411                    precompile_call_logs: vec![],
1412                    charged_new_account_state_gas: call.charged_new_account_state_gas,
1413                }),
1414            };
1415        }
1416
1417        #[cfg(feature = "monad")]
1418        if crate::monad::is_monad_cheatcode_call(self.extra_cheatcode_addresses, call.transfer_to())
1419        {
1420            let checkpoint = ecx.journal_mut().checkpoint();
1421            return match self.apply_monad_cheatcode(ecx, call) {
1422                Ok(retdata) => {
1423                    ecx.journal_mut().checkpoint_commit();
1424                    Some(CallOutcome {
1425                        result: InterpreterResult {
1426                            result: InstructionResult::Return,
1427                            output: retdata.into(),
1428                            gas,
1429                        },
1430                        memory_offset: call.return_memory_offset.clone(),
1431                        was_precompile_called: true,
1432                        precompile_call_logs: vec![],
1433                        charged_new_account_state_gas: call.charged_new_account_state_gas,
1434                    })
1435                }
1436                Err(err) => {
1437                    ecx.journal_mut().checkpoint_revert(checkpoint);
1438                    Some(CallOutcome {
1439                        result: InterpreterResult {
1440                            result: InstructionResult::Revert,
1441                            output: err.abi_encode().into(),
1442                            gas,
1443                        },
1444                        memory_offset: call.return_memory_offset.clone(),
1445                        was_precompile_called: false,
1446                        precompile_call_logs: vec![],
1447                        charged_new_account_state_gas: call.charged_new_account_state_gas,
1448                    })
1449                }
1450            };
1451        }
1452
1453        if call.transfer_to() == HARDHAT_CONSOLE_ADDRESS {
1454            return None;
1455        }
1456
1457        // `expectRevert`: track max call depth. This is also done in `initialize_interp`, but
1458        // precompile calls don't create an interpreter frame so we must also track it here.
1459        // The callee executes at `curr_depth + 1`.
1460        if let Some(expected) = &mut self.expected_revert {
1461            expected.max_depth = max(curr_depth + 1, expected.max_depth);
1462        }
1463
1464        // Handle expected calls
1465        if let Some(expected) = self.expected_calls.get_mut(&call.bytecode_address) {
1466            let input = call.input.as_bytes(ecx);
1467            expect::observe_call(
1468                expected,
1469                &input,
1470                call.transfer_value(),
1471                call.gas_limit,
1472                call.scheme,
1473            );
1474        }
1475
1476        // Apply our prank
1477        if let Some(prank) = self.get_prank(curr_depth).copied() {
1478            // Apply delegate call, `call.caller`` will not equal `prank.prank_caller`
1479            if prank.delegate_call && curr_depth == prank.depth && call.scheme.is_delegate_call() {
1480                call.target_address = prank.new_caller;
1481                call.caller = prank.new_caller;
1482                if let Some(new_origin) = prank.new_origin {
1483                    ecx.tx_mut().set_caller(new_origin);
1484                }
1485                if let Some(used) = prank.first_time_applied() {
1486                    self.pranks.insert(curr_depth, used);
1487                }
1488            }
1489
1490            if let Some(changes) = prank.changes_for(curr_depth, call.transfer_from()) {
1491                if let Some(new_caller) = changes.caller {
1492                    // Ensure new caller is loaded and touched
1493                    let _ = journaled_account(ecx, new_caller);
1494                    call.caller = new_caller;
1495                }
1496                if let Some(new_origin) = changes.origin {
1497                    ecx.tx_mut().set_caller(new_origin);
1498                }
1499                if let Some(used) = changes.used {
1500                    self.pranks.insert(curr_depth, used);
1501                }
1502            }
1503        }
1504
1505        // Handle mocked calls
1506        if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1507            let input = call.input.bytes(ecx);
1508            if let Some(return_data_queue) =
1509                mock::find_mock_returns(mocks, &input, call.transfer_value())
1510                && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1511            {
1512                if let Some(value) = call.transfer_value() {
1513                    let checkpoint = ecx.journal_mut().checkpoint();
1514                    match ecx.journal_mut().transfer_loaded(
1515                        call.transfer_from(),
1516                        call.transfer_to(),
1517                        value,
1518                    ) {
1519                        None => {
1520                            if return_data.ret_type.is_ok() {
1521                                ecx.journal_mut().checkpoint_commit();
1522                            } else {
1523                                ecx.journal_mut().checkpoint_revert(checkpoint);
1524                            }
1525                        }
1526                        Some(err) => {
1527                            ecx.journal_mut().checkpoint_revert(checkpoint);
1528                            return Some(CallOutcome {
1529                                result: InterpreterResult {
1530                                    result: err.into(),
1531                                    output: Bytes::new(),
1532                                    gas,
1533                                },
1534                                memory_offset: call.return_memory_offset.clone(),
1535                                was_precompile_called: false,
1536                                precompile_call_logs: vec![],
1537                                charged_new_account_state_gas: call.charged_new_account_state_gas,
1538                            });
1539                        }
1540                    }
1541                }
1542
1543                mock::advance_mock_returns(return_data_queue);
1544
1545                return Some(CallOutcome {
1546                    result: InterpreterResult {
1547                        result: return_data.ret_type,
1548                        output: return_data.data,
1549                        gas,
1550                    },
1551                    memory_offset: call.return_memory_offset.clone(),
1552                    was_precompile_called: true,
1553                    precompile_call_logs: vec![],
1554                    charged_new_account_state_gas: call.charged_new_account_state_gas,
1555                });
1556            }
1557        }
1558
1559        // Apply EIP-2930 access list
1560        self.apply_accesslist(ecx);
1561
1562        // Apply our broadcast
1563        if let Some(broadcast) = &mut self.broadcast {
1564            // Additional check as transfers in forge scripts seem to be estimated at 2300
1565            // by revm leading to "Intrinsic gas too low" failure when simulated on chain.
1566            let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1567            self.dynamic_gas_limit = false;
1568
1569            // We only apply a broadcast *to a specific depth*.
1570            //
1571            // We do this because any subsequent contract calls *must* exist on chain and
1572            // we only want to grab *this* call, not internal ones. `deployCode` routed through
1573            // the CREATE2 factory runs one level deeper in a nested EVM.
1574            if (curr_depth == broadcast.depth || broadcast.deploy_from_code)
1575                && call.transfer_from() == broadcast.original_caller
1576            {
1577                // Reset deploy from code flag for upcoming calls.
1578                broadcast.deploy_from_code = false;
1579
1580                // At the target depth we set `msg.sender` & tx.origin.
1581                // We are simulating the caller as being an EOA, so *both* must be set to the
1582                // broadcast.origin.
1583                ecx.tx_mut().set_caller(broadcast.new_origin);
1584
1585                call.caller = broadcast.new_origin;
1586                // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here
1587                // because we only need the from, to, value, and data. We can later change this
1588                // into 1559, in the cli package, relatively easily once we
1589                // know the target chain supports EIP-1559.
1590                if !call.is_static {
1591                    if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1592                        return Some(CallOutcome {
1593                            result: InterpreterResult {
1594                                result: InstructionResult::Revert,
1595                                output: Error::encode(err),
1596                                gas,
1597                            },
1598                            memory_offset: call.return_memory_offset.clone(),
1599                            was_precompile_called: false,
1600                            precompile_call_logs: vec![],
1601                            charged_new_account_state_gas: call.charged_new_account_state_gas,
1602                        });
1603                    }
1604
1605                    let input = call.input.bytes(ecx);
1606                    let chain_id = ecx.cfg().chain_id();
1607                    let rpc = ecx.db().active_fork_url();
1608                    let fee_token = ecx.tx().fee_token();
1609                    let nonce =
1610                        ecx.journal().evm_state().get(&broadcast.new_origin).unwrap().info.nonce;
1611
1612                    let mut tx_req = TransactionRequestFor::<FEN>::default()
1613                        .with_from(broadcast.new_origin)
1614                        .with_to(call.transfer_to())
1615                        .with_value(call.transfer_value().unwrap_or_default())
1616                        .with_input(input)
1617                        .with_nonce(nonce)
1618                        .with_chain_id(chain_id);
1619                    if is_fixed_gas_limit {
1620                        tx_req.set_gas_limit(call.gas_limit)
1621                    }
1622
1623                    let active_delegations = std::mem::take(&mut self.active_delegations);
1624                    // Set active blob sidecar, if any.
1625                    if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1626                        // Ensure blob and delegation are not set for the same tx.
1627                        if !active_delegations.is_empty() {
1628                            let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1629                            return Some(CallOutcome {
1630                                result: InterpreterResult {
1631                                    result: InstructionResult::Revert,
1632                                    output: Error::encode(msg),
1633                                    gas,
1634                                },
1635                                memory_offset: call.return_memory_offset.clone(),
1636                                was_precompile_called: false,
1637                                precompile_call_logs: vec![],
1638                                charged_new_account_state_gas: call.charged_new_account_state_gas,
1639                            });
1640                        }
1641                        tx_req.set_blob_sidecar(blob_sidecar);
1642                    }
1643
1644                    // Apply active EIP-7702 delegations, if any.
1645                    if !active_delegations.is_empty() {
1646                        if let Err(err) = apply_authorization_nonces::<FEN>(
1647                            ecx,
1648                            &active_delegations,
1649                            broadcast.new_origin,
1650                            chain_id,
1651                        ) {
1652                            return Some(CallOutcome {
1653                                result: InterpreterResult {
1654                                    result: InstructionResult::Revert,
1655                                    output: err.abi_encode().into(),
1656                                    gas,
1657                                },
1658                                memory_offset: call.return_memory_offset.clone(),
1659                                was_precompile_called: false,
1660                                precompile_call_logs: vec![],
1661                                charged_new_account_state_gas: call.charged_new_account_state_gas,
1662                            });
1663                        }
1664                        tx_req.set_authorization_list(active_delegations);
1665                    }
1666                    if let Some(fee_token) = fee_token {
1667                        tx_req.set_fee_token(fee_token);
1668                    }
1669                    self.broadcastable_transactions.push_back(BroadcastableTransaction {
1670                        rpc,
1671                        transaction: TransactionMaybeSigned::new(tx_req),
1672                    });
1673                    debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1674
1675                    // Isolated transactions increment the nonce during execution. Nested
1676                    // broadcasts do not start a separate transaction and need this increment.
1677                    if !isolate_call {
1678                        let account = ecx
1679                            .journal_mut()
1680                            .evm_state_mut()
1681                            .get_mut(&broadcast.new_origin)
1682                            .unwrap();
1683                        let prev = account.info.nonce;
1684                        account.info.nonce += 1;
1685                        debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1686                    }
1687                } else if broadcast.single_call {
1688                    let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1689                    return Some(CallOutcome {
1690                        result: InterpreterResult {
1691                            result: InstructionResult::Revert,
1692                            output: Error::encode(msg),
1693                            gas,
1694                        },
1695                        memory_offset: call.return_memory_offset.clone(),
1696                        was_precompile_called: false,
1697                        precompile_call_logs: vec![],
1698                        charged_new_account_state_gas: call.charged_new_account_state_gas,
1699                    });
1700                }
1701            }
1702        }
1703
1704        // Record called accounts if `startStateDiffRecording` has been called
1705        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1706            // Determine if account is "initialized," ie, it has a non-zero balance, a non-zero
1707            // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code
1708            let (initialized, old_balance, old_nonce) =
1709                if let Ok(acc) = ecx.journal_mut().load_account(call.transfer_to()) {
1710                    (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1711                } else {
1712                    (false, U256::ZERO, 0)
1713                };
1714
1715            let kind = match call.scheme {
1716                CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1717                CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1718                CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1719                CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1720            };
1721
1722            // Record this call by pushing it to a new pending vector; all subsequent calls at
1723            // that depth will be pushed to the same vector. When the call ends, the
1724            // RecordedAccountAccess (and all subsequent RecordedAccountAccesses) will be
1725            // updated with the revert status of this call, since the EVM does not mark accounts
1726            // as "warm" if the call from which they were accessed is reverted
1727            recorded_account_diffs_stack.push(vec![AccountAccess {
1728                chainInfo: crate::Vm::ChainInfo {
1729                    forkId: ecx.db().active_fork_id().unwrap_or_default(),
1730                    chainId: U256::from(ecx.cfg().chain_id()),
1731                },
1732                accessor: call.transfer_from(),
1733                account: call.bytecode_address,
1734                kind,
1735                initialized,
1736                oldBalance: old_balance,
1737                newBalance: U256::ZERO, // updated on call_end
1738                oldNonce: old_nonce,
1739                newNonce: 0, // updated on call_end
1740                value: call.call_value(),
1741                data: call.input.bytes(ecx),
1742                reverted: false,
1743                deployedCode: Bytes::new(),
1744                storageAccesses: vec![], // updated on step
1745                depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1746            }]);
1747        }
1748
1749        None
1750    }
1751
1752    pub fn rng(&mut self) -> &mut impl Rng {
1753        self.test_runner().rng()
1754    }
1755
1756    pub fn test_runner(&mut self) -> &mut TestRunner {
1757        self.test_runner.get_or_insert_with(|| match self.config.seed {
1758            Some(seed) => TestRunner::new_with_rng(
1759                proptest::test_runner::Config::default(),
1760                TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1761            ),
1762            None => TestRunner::new(proptest::test_runner::Config::default()),
1763        })
1764    }
1765
1766    pub fn set_seed(&mut self, seed: U256) {
1767        self.test_runner = Some(TestRunner::new_with_rng(
1768            proptest::test_runner::Config::default(),
1769            TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1770        ));
1771    }
1772
1773    /// Returns existing or set a default `ArbitraryStorage` option.
1774    /// Used by `setArbitraryStorage` cheatcode to track addresses with arbitrary storage.
1775    pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1776        self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1777    }
1778
1779    /// Returns addresses explicitly marked with arbitrary storage.
1780    pub fn arbitrary_storage_targets(&self) -> impl Iterator<Item = Address> + '_ {
1781        self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::targets)
1782    }
1783
1784    /// Returns addresses explicitly marked with arbitrary storage and whether nonzero slots are
1785    /// overwritten.
1786    pub fn arbitrary_storage_target_overwrite_modes(
1787        &self,
1788    ) -> impl Iterator<Item = (Address, bool)> + '_ {
1789        self.arbitrary_storage
1790            .as_ref()
1791            .into_iter()
1792            .flat_map(ArbitraryStorage::target_overwrite_modes)
1793    }
1794
1795    /// Returns addresses that copy storage from arbitrary-storage targets.
1796    pub fn arbitrary_storage_copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
1797        self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::copied_targets)
1798    }
1799
1800    /// Returns copied arbitrary-storage targets and their source address.
1801    pub fn arbitrary_storage_copied_target_sources(
1802        &self,
1803    ) -> impl Iterator<Item = (Address, Address)> + '_ {
1804        self.arbitrary_storage
1805            .as_ref()
1806            .into_iter()
1807            .flat_map(ArbitraryStorage::copied_target_sources)
1808    }
1809
1810    /// Caches a concrete replay value for a slot on an arbitrary-storage address or copied target.
1811    pub fn cache_arbitrary_storage_value(&mut self, address: Address, slot: U256, value: U256) {
1812        if let Some(storage) = &mut self.arbitrary_storage {
1813            storage.cache_value(address, slot, value);
1814        }
1815    }
1816
1817    /// Marks a slot as explicitly written with `vm.store`.
1818    pub fn mark_arbitrary_storage_slot_explicit(&mut self, address: Address, slot: U256) {
1819        if let Some(storage) = &mut self.arbitrary_storage {
1820            storage.mark_explicit(address, slot);
1821        }
1822    }
1823
1824    /// Returns whether a slot was explicitly written with `vm.store`.
1825    pub fn is_arbitrary_storage_slot_explicit(&self, address: Address, slot: U256) -> bool {
1826        self.arbitrary_storage.as_ref().is_some_and(|storage| storage.is_explicit(address, slot))
1827    }
1828
1829    /// Returns a cached arbitrary-storage replay value for a slot.
1830    pub fn cached_arbitrary_storage_value(&self, address: Address, slot: U256) -> Option<U256> {
1831        self.arbitrary_storage.as_ref().and_then(|storage| storage.cached_value(address, slot))
1832    }
1833
1834    /// Whether the given address has arbitrary storage.
1835    pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1836        match &self.arbitrary_storage {
1837            Some(storage) => storage.values.contains_key(address),
1838            None => false,
1839        }
1840    }
1841
1842    /// Whether the given slot of address with arbitrary storage should be overwritten.
1843    /// True if address is marked as and overwrite and if no value was previously generated for
1844    /// given slot.
1845    pub fn should_overwrite_arbitrary_storage(
1846        &self,
1847        address: &Address,
1848        storage_slot: U256,
1849    ) -> bool {
1850        match &self.arbitrary_storage {
1851            Some(storage) => {
1852                storage.overwrites.contains(address)
1853                    && storage
1854                        .values
1855                        .get(address)
1856                        .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1857                        .is_none()
1858            }
1859            None => false,
1860        }
1861    }
1862
1863    /// Whether the given address is a copy of an address with arbitrary storage.
1864    pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1865        match &self.arbitrary_storage {
1866            Some(storage) => storage.copies.contains_key(address),
1867            None => false,
1868        }
1869    }
1870
1871    /// Registers an SLOAD callback, replacing the existing callback for `target`.
1872    pub fn register_storage_load_hook(
1873        &mut self,
1874        target: Address,
1875        callback_target: Address,
1876        callback_selector: [u8; 4],
1877    ) {
1878        self.storage_load_hooks.insert(target, StorageHook { callback_target, callback_selector });
1879        self.storage_hooks_registered = true;
1880    }
1881
1882    /// Registers an SSTORE callback, replacing the existing callback for `target`.
1883    pub fn register_storage_store_hook(
1884        &mut self,
1885        target: Address,
1886        callback_target: Address,
1887        callback_selector: [u8; 4],
1888    ) {
1889        self.storage_store_hooks.insert(target, StorageHook { callback_target, callback_selector });
1890        self.storage_hooks_registered = true;
1891    }
1892
1893    /// Registers a mapping SSTORE callback. Returns false when a raw hook conflicts.
1894    pub fn register_mapping_storage_store_hook(
1895        &mut self,
1896        target: Address,
1897        root_slot: B256,
1898        callback_target: Address,
1899        callback_selector: [u8; 4],
1900    ) -> bool {
1901        if self.storage_store_hooks.contains_key(&target) {
1902            return false;
1903        }
1904        self.storage_hook_mapping_slots.remove(&target);
1905        self.mapping_storage_store_hooks
1906            .entry(target)
1907            .or_default()
1908            .insert(root_slot, StorageHook { callback_target, callback_selector });
1909        self.storage_hooks_registered = true;
1910        true
1911    }
1912
1913    /// Returns registered mapping SSTORE callbacks.
1914    pub fn mapping_storage_store_hooks(
1915        &self,
1916    ) -> impl Iterator<Item = (Address, B256, StorageHook)> + '_ {
1917        self.mapping_storage_store_hooks
1918            .iter()
1919            .flat_map(|(target, hooks)| hooks.iter().map(|(root, hook)| (*target, *root, *hook)))
1920    }
1921
1922    /// Returns whether mapping hooks conflict with a raw store hook.
1923    pub fn has_mapping_storage_store_hooks(&self, target: Address) -> bool {
1924        self.mapping_storage_store_hooks.get(&target).is_some_and(|hooks| !hooks.is_empty())
1925    }
1926
1927    /// Returns registered SLOAD callbacks.
1928    pub fn storage_load_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
1929        self.storage_load_hooks.iter().map(|(target, hook)| (*target, *hook))
1930    }
1931
1932    /// Returns registered SSTORE callbacks.
1933    pub fn storage_store_hooks(&self) -> impl Iterator<Item = (Address, StorageHook)> + '_ {
1934        self.storage_store_hooks.iter().map(|(target, hook)| (*target, *hook))
1935    }
1936
1937    /// Returns whether any storage callback is registered.
1938    #[inline]
1939    pub const fn has_storage_hooks(&self) -> bool {
1940        self.storage_hooks_registered
1941    }
1942
1943    /// Clears execution-local mapping provenance while preserving hook registrations.
1944    pub fn clear_storage_hook_mapping_slots(&mut self) {
1945        self.storage_hook_mapping_slots.clear();
1946    }
1947
1948    /// Returns whether a synthetic storage-hook callback or one of its child calls is executing.
1949    #[inline]
1950    pub const fn is_storage_hook_active(&self) -> bool {
1951        self.active_storage_hook.is_some()
1952    }
1953
1954    /// Returns whether `call` is the synthetic callback for the active storage hook.
1955    pub fn is_storage_hook_callback(
1956        &self,
1957        ecx: &FoundryContextFor<'_, FEN>,
1958        call: &CallInputs,
1959    ) -> bool {
1960        self.active_storage_hook.as_ref().is_some_and(|active| {
1961            active.outcome.is_none()
1962                && ecx.journal().depth() == active.parent_depth
1963                && call.transfer_from() == CHEATCODE_ADDRESS
1964                && call.transfer_to() == active.callback_target
1965                && call.input.bytes(ecx) == active.callback_input
1966        })
1967    }
1968
1969    fn finish_storage_hook_call(
1970        &mut self,
1971        ecx: &FoundryContextFor<'_, FEN>,
1972        call: &CallInputs,
1973        outcome: &CallOutcome,
1974    ) -> bool {
1975        let Some(active) = self.active_storage_hook.as_mut() else { return false };
1976        if active.outcome.is_some()
1977            || ecx.journal().depth() != active.parent_depth
1978            || call.transfer_from() != CHEATCODE_ADDRESS
1979            || call.transfer_to() != active.callback_target
1980            || call.input.bytes(ecx) != active.callback_input
1981        {
1982            return false;
1983        }
1984        active.outcome = Some((outcome.result.result, outcome.result.output.clone()));
1985        true
1986    }
1987
1988    #[inline(always)]
1989    pub fn has_step_hooks(&self) -> bool {
1990        self.broadcast.is_some()
1991            || self.gas_metering.paused
1992            || self.gas_metering.reset
1993            || self.recording_accesses
1994            || self.recorded_account_diffs_stack.is_some()
1995            || !self.allowed_mem_writes.is_empty()
1996            || self.mapping_slots.is_some()
1997            || self.gas_metering.recording
1998            || self.has_active_env_overrides()
1999            || self.has_storage_hooks()
2000    }
2001
2002    #[inline(always)]
2003    pub fn has_step_end_hooks(&self) -> bool {
2004        self.gas_metering.paused
2005            || self.gas_metering.touched
2006            || self.arbitrary_storage.is_some()
2007            || self.mapping_slots.is_some()
2008            || self.has_active_env_overrides()
2009            || self.has_storage_hooks()
2010    }
2011
2012    #[inline(always)]
2013    pub fn has_log_hooks(&self) -> bool {
2014        !self.expected_emits.is_empty() || self.recorded_logs.is_some()
2015    }
2016
2017    #[inline(always)]
2018    pub fn has_recording_accesses_only_step_hook(&self) -> bool {
2019        self.recording_accesses
2020            && self.broadcast.is_none()
2021            && !self.gas_metering.paused
2022            && !self.gas_metering.reset
2023            && self.recorded_account_diffs_stack.is_none()
2024            && self.allowed_mem_writes.is_empty()
2025            && self.mapping_slots.is_none()
2026            && !self.has_storage_hooks()
2027            && !self.gas_metering.recording
2028            && !self.has_active_env_overrides()
2029    }
2030
2031    #[inline(always)]
2032    fn has_active_env_overrides(&self) -> bool {
2033        self.env_overrides.is_any_set()
2034    }
2035
2036    /// Returns struct definitions from the analysis, if available.
2037    pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
2038        self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
2039    }
2040}
2041
2042const fn frame_gas(result: &InterpreterResult) -> Vm::Gas {
2043    let gas = &result.gas;
2044    // A halt consumes the regular gas restored while rolling back state gas.
2045    let regular_gas_spent = if result.is_halt() {
2046        gas.total_gas_spent()
2047    } else {
2048        gas.total_gas_spent().saturating_sub(gas.state_gas_spilled())
2049    };
2050    Vm::Gas {
2051        gasLimit: gas.limit(),
2052        gasTotalUsed: regular_gas_spent,
2053        gasMemoryUsed: 0,
2054        gasRefunded: gas.refunded(),
2055        gasRemaining: gas.remaining(),
2056        gasStateUsed: if result.is_ok() { gas.state_gas_spent() } else { 0 },
2057    }
2058}
2059
2060impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
2061    fn initialize_interp(
2062        &mut self,
2063        interpreter: &mut Interpreter,
2064        ecx: &mut FoundryContextFor<'_, FEN>,
2065    ) {
2066        // When the first interpreter is initialized we've circumvented the balance and gas checks,
2067        // so we apply our actual block data with the correct fees and all.
2068        if let Some(block) = self.block.take() {
2069            ecx.set_block(block);
2070        }
2071        if let Some(gas_price) = self.gas_price.take() {
2072            ecx.tx_mut().set_gas_price(gas_price);
2073        }
2074
2075        // Record gas for current frame.
2076        if self.gas_metering.paused {
2077            self.gas_metering.paused_frames.push(interpreter.gas);
2078        }
2079
2080        // `expectRevert`: track the max call depth during `expectRevert`
2081        if let Some(expected) = &mut self.expected_revert {
2082            expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
2083        }
2084    }
2085
2086    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2087        self.pc = interpreter.bytecode.pc();
2088
2089        if !self.has_step_hooks() {
2090            return;
2091        }
2092
2093        if self.finish_storage_hook_callback(interpreter, ecx) {
2094            return;
2095        }
2096
2097        if self.broadcast.is_some() {
2098            self.set_gas_limit_type(interpreter);
2099        }
2100
2101        // Broadcasting changes outgoing calls, not the caller of the script's current frame.
2102        // Only protect the broadcasting frame; callbacks into the script have their own caller.
2103        if interpreter.bytecode.opcode() == op::CALLER
2104            && let Some(broadcast) = &self.broadcast
2105            && let Some(script_address) = self.script_address
2106            && ecx.journal().depth() == broadcast.depth
2107            && interpreter.input.target_address == script_address
2108            && interpreter.input.bytecode_address == Some(script_address)
2109            && interpreter.input.caller_address != broadcast.new_origin
2110        {
2111            interpreter.bytecode.set_action(InterpreterAction::new_return(
2112                InstructionResult::Revert,
2113                Bytes::from(
2114                    format!(
2115                        "Usage of `msg.sender` inside a `broadcast` in script contract detected. \
2116                         `msg.sender` is `{:#x}`, not the broadcast sender `{:#x}`. \
2117                         Use the `--sender` flag or pass the deployer address directly instead.",
2118                        interpreter.input.caller_address, broadcast.new_origin,
2119                    )
2120                    .into_bytes(),
2121                ),
2122                interpreter.gas,
2123            ));
2124            return;
2125        }
2126
2127        // `pauseGasMetering`: pause / resume interpreter gas.
2128        if self.gas_metering.paused {
2129            self.meter_gas(interpreter);
2130        }
2131
2132        // `resetGasMetering`: reset interpreter gas.
2133        if self.gas_metering.reset {
2134            self.meter_gas_reset(interpreter);
2135        }
2136
2137        // `record`: record storage reads and writes.
2138        if self.recording_accesses {
2139            self.record_accesses(interpreter);
2140        }
2141
2142        // `startStateDiffRecording`: record granular ordered storage accesses.
2143        if self.recorded_account_diffs_stack.is_some() {
2144            self.record_state_diffs(interpreter, ecx);
2145        }
2146
2147        // `expectSafeMemory`: check if the current opcode is allowed to interact with memory.
2148        if !self.allowed_mem_writes.is_empty() {
2149            self.check_mem_opcodes(
2150                interpreter,
2151                ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
2152            );
2153        }
2154
2155        if self.mapping_slots.is_some() || !self.mapping_storage_store_hooks.is_empty() {
2156            // `startMappingRecording`: record SSTORE.
2157            if let Some(mapping_slots) = &mut self.mapping_slots {
2158                mapping_step(mapping_slots, interpreter);
2159            }
2160
2161            let account = interpreter.input.target_address;
2162            let mapping_hook_active = self.active_storage_hook.is_none()
2163                && self
2164                    .mapping_storage_store_hooks
2165                    .get(&account)
2166                    .is_some_and(|hooks| !hooks.is_empty());
2167            if mapping_hook_active {
2168                mapping_step(&mut self.storage_hook_mapping_slots, interpreter);
2169            }
2170            self.pending_mapping_hash = if self.mapping_slots.is_some() || mapping_hook_active {
2171                capture_mapping_hash(interpreter)
2172            } else {
2173                None
2174            };
2175        }
2176
2177        // `snapshotGas*`: take a snapshot of the current gas.
2178        if self.gas_metering.recording {
2179            self.meter_gas_record(interpreter, ecx);
2180        }
2181
2182        // Capture the opcode for `step_end` to use, since by the time
2183        // `step_end` runs the PC has already advanced past it. Also peek the
2184        // BLOBHASH index now (still on top of stack before execution) so we
2185        // can look up the override later.
2186        if !self.env_overrides.is_empty() {
2187            let fork_id = ecx.db().active_fork_id();
2188            if let Some(env_overrides) =
2189                self.env_overrides.get_mut(fork_id).filter(|o| o.is_any_set())
2190            {
2191                // Always clear stale pending state first so a leftover value from
2192                // a prior step (e.g. when `peek` failed, or when an override
2193                // wasn't actually used) cannot leak into the next opcode.
2194                env_overrides.pending_opcode = None;
2195                env_overrides.pending_blobhash_index = None;
2196
2197                let opcode = interpreter.bytecode.opcode();
2198                match opcode {
2199                    op::BASEFEE | op::GASPRICE => {
2200                        env_overrides.pending_opcode = Some(opcode);
2201                    }
2202                    op::BLOBHASH => {
2203                        env_overrides.pending_opcode = Some(opcode);
2204                        env_overrides.pending_blobhash_index =
2205                            interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
2206                    }
2207                    _ => {}
2208                }
2209            }
2210        }
2211
2212        if self.active_storage_hook.is_none() {
2213            self.capture_storage_hook(interpreter, ecx);
2214        }
2215    }
2216
2217    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
2218        if !self.has_step_end_hooks() {
2219            return;
2220        }
2221
2222        if self.gas_metering.paused {
2223            self.meter_gas_end(interpreter);
2224        }
2225
2226        if self.gas_metering.touched {
2227            self.meter_gas_check(interpreter);
2228        }
2229
2230        // `setArbitraryStorage` and `copyStorage`: add arbitrary values to storage.
2231        if self.arbitrary_storage.is_some() {
2232            self.arbitrary_storage_end(interpreter, ecx);
2233        }
2234
2235        if let Some(pending) = self.pending_mapping_hash.take()
2236            && interpreter
2237                .bytecode
2238                .action
2239                .as_ref()
2240                .and_then(InterpreterAction::instruction_result)
2241                .is_none()
2242        {
2243            if let Some(mapping_slots) = &mut self.mapping_slots {
2244                record_mapping_hash(mapping_slots, interpreter, pending);
2245            }
2246            if self
2247                .mapping_storage_store_hooks
2248                .get(&pending.address)
2249                .is_some_and(|hooks| !hooks.is_empty())
2250                && self.active_storage_hook.is_none()
2251            {
2252                record_mapping_hash(&mut self.storage_hook_mapping_slots, interpreter, pending);
2253            }
2254        }
2255
2256        if self.active_storage_hook.is_none() {
2257            self.invoke_pending_storage_hook(interpreter, ecx);
2258        }
2259
2260        // Apply opcode-level env overrides (basefee/gasprice/blobhash). Needed
2261        // in isolation mode where the actual tx/block env is zeroed for
2262        // fee-accounting; in non-isolation mode the override and the real env
2263        // are kept in sync by the cheatcode handlers, so this is a no-op fixup.
2264        //
2265        // We must only rewrite the stack if the opcode actually completed
2266        // successfully and pushed its result; otherwise (stack underflow on
2267        // BLOBHASH, OOG before push, etc.) the stack is in an error state and
2268        // a blind `pop()+push()` would corrupt the failing frame.
2269        if !self.env_overrides.is_empty() {
2270            let fork_id = ecx.db().active_fork_id();
2271            if self.env_overrides.get(fork_id).is_some_and(|o| o.is_any_set()) {
2272                // Mirrors the pattern used by `meter_gas_record`: when `action` is
2273                // `Some` with an `instruction_result`, the opcode has set a
2274                // non-continue result (halt/revert/error) — i.e. it didn't push
2275                // its normal result. `None` means "still running", which is the
2276                // success path for a stack-only opcode in `step_end`.
2277                let opcode_failed = interpreter
2278                    .bytecode
2279                    .action
2280                    .as_ref()
2281                    .and_then(|a| a.instruction_result())
2282                    .is_some();
2283                if opcode_failed {
2284                    if let Some(env_overrides) = self.env_overrides.get_mut(fork_id) {
2285                        env_overrides.pending_opcode = None;
2286                        env_overrides.pending_blobhash_index = None;
2287                    }
2288                } else {
2289                    self.apply_env_overrides(interpreter, fork_id);
2290                }
2291            }
2292        }
2293    }
2294
2295    fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
2296        if !self.expected_emits.is_empty()
2297            && let Some(err) = expect::handle_expect_emit(self, &log, None)
2298        {
2299            // Because we do not have access to the interpreter here, we cannot fail the test
2300            // immediately. In most cases the failure will still be caught on `call_end`.
2301            // In the rare case it is not, we log the error here.
2302            let _ = sh_err!("{err:?}");
2303        }
2304
2305        // `recordLogs`
2306        record_logs(&mut self.recorded_logs, &log);
2307    }
2308
2309    fn log_full(
2310        &mut self,
2311        interpreter: &mut Interpreter,
2312        _ecx: &mut FoundryContextFor<'_, FEN>,
2313        log: Log,
2314    ) {
2315        if !self.expected_emits.is_empty() {
2316            expect::handle_expect_emit(self, &log, Some(interpreter));
2317        }
2318
2319        // `recordLogs`
2320        record_logs(&mut self.recorded_logs, &log);
2321    }
2322
2323    fn call(
2324        &mut self,
2325        ecx: &mut FoundryContextFor<'_, FEN>,
2326        inputs: &mut CallInputs,
2327    ) -> Option<CallOutcome> {
2328        if self.is_storage_hook_callback(ecx, inputs) {
2329            return None;
2330        }
2331        Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor, false)
2332    }
2333
2334    fn call_end(
2335        &mut self,
2336        ecx: &mut FoundryContextFor<'_, FEN>,
2337        call: &CallInputs,
2338        outcome: &mut CallOutcome,
2339    ) {
2340        let isolated_snapshot_gas_used = self
2341            .gas_metering
2342            .take_isolated_snapshot_gas_used(ecx.journal().depth(), &outcome.result.gas);
2343        if self.finish_storage_hook_call(ecx, call, outcome) {
2344            return;
2345        }
2346
2347        let cheatcode_call = call.transfer_to() == CHEATCODE_ADDRESS
2348            || call.transfer_to() == HARDHAT_CONSOLE_ADDRESS;
2349        #[cfg(feature = "monad")]
2350        let cheatcode_call = cheatcode_call
2351            || crate::monad::is_monad_cheatcode_call(
2352                self.extra_cheatcode_addresses,
2353                call.transfer_to(),
2354            );
2355        let curr_depth = ecx.journal().depth();
2356
2357        self.finish_created_accounts_frame(
2358            outcome.result.is_ok(),
2359            CreatedAccountsFrameKind::Call,
2360            curr_depth,
2361        );
2362
2363        // Clean up pranks/broadcasts if it's not a cheatcode call end. We shouldn't do
2364        // it for cheatcode calls because they are not applied for cheatcodes in the `call` hook.
2365        // This should be placed before the revert handling, because we might exit early there
2366        if !cheatcode_call {
2367            // Clean up pranks
2368            if let Some(prank) = &self.get_prank(curr_depth)
2369                && curr_depth == prank.depth
2370            {
2371                ecx.tx_mut().set_caller(prank.prank_origin);
2372
2373                // Clean single-call prank once we have returned to the original depth
2374                if prank.single_call {
2375                    self.pranks.remove(&curr_depth);
2376                }
2377            }
2378
2379            // Clean up broadcast
2380            if let Some(broadcast) = &self.broadcast
2381                && curr_depth == broadcast.depth
2382            {
2383                ecx.tx_mut().set_caller(broadcast.original_origin);
2384
2385                // Clean single-call broadcast once we have returned to the original depth
2386                if broadcast.single_call {
2387                    let _ = self.broadcast.take();
2388                }
2389            }
2390        }
2391
2392        // Handle assume no revert cheatcode.
2393        if let Some(assume_no_revert) = &mut self.assume_no_revert {
2394            // Record current reverter address before processing the expect revert if call reverted,
2395            // expect revert is set with expected reverter address and no actual reverter set yet.
2396            if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
2397                assume_no_revert.reverted_by = Some(call.transfer_to());
2398            }
2399
2400            // allow multiple cheatcode calls at the same depth
2401            let curr_depth = ecx.journal().depth();
2402            if curr_depth <= assume_no_revert.depth && !cheatcode_call {
2403                // Discard run if we're at the same depth as cheatcode, call reverted, and no
2404                // specific reason was supplied
2405                if outcome.result.is_revert() {
2406                    let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
2407                    return match revert_handlers::handle_assume_no_revert(
2408                        &assume_no_revert,
2409                        outcome.result.result,
2410                        &outcome.result.output,
2411                        &self.config.available_artifacts,
2412                    ) {
2413                        // if result is Ok, it was an anticipated revert; return an "assume" error
2414                        // to reject this run
2415                        Ok(_) => {
2416                            outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
2417                        }
2418                        // if result is Error, it was an unanticipated revert; should revert
2419                        // normally
2420                        Err(error) => {
2421                            trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2422                            outcome.result.result = InstructionResult::Revert;
2423                            outcome.result.output = error.abi_encode().into();
2424                        }
2425                    };
2426                }
2427                // Call didn't revert, reset `assume_no_revert` state.
2428                self.assume_no_revert = None;
2429            }
2430        }
2431
2432        // Handle expected reverts.
2433        if let Some(expected_revert) = &mut self.expected_revert {
2434            // Record current reverter address and call scheme before processing the expect revert
2435            // if call reverted.
2436            let call_failed = !outcome.result.result.is_ok();
2437            if call_failed {
2438                // Record current reverter address if expect revert is set with expected reverter
2439                // address and no actual reverter was set yet or if we're expecting more than one
2440                // revert.
2441                if expected_revert.reverter.is_some()
2442                    && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
2443                {
2444                    expected_revert.reverted_by = Some(call.transfer_to());
2445                }
2446            }
2447
2448            let curr_depth = ecx.journal().depth();
2449            if curr_depth <= expected_revert.depth {
2450                let needs_processing = expected_revert.needs_processing(
2451                    cheatcode_call,
2452                    call_failed,
2453                    curr_depth,
2454                    self.config.internal_expect_revert,
2455                );
2456
2457                if needs_processing {
2458                    let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2459                    let clear_last_frame_gas =
2460                        matches!(expected_revert.kind, ExpectedRevertKind::Default);
2461                    return match revert_handlers::handle_expect_revert(
2462                        cheatcode_call,
2463                        false,
2464                        self.config.internal_expect_revert,
2465                        &expected_revert,
2466                        outcome.result.result,
2467                        outcome.result.output.clone(),
2468                        &self.config.available_artifacts,
2469                    ) {
2470                        Err(error) => {
2471                            trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
2472                            outcome.result.result = InstructionResult::Revert;
2473                            outcome.result.output = error.abi_encode().into();
2474                        }
2475                        Ok((_, retdata)) => {
2476                            expected_revert.actual_count += 1;
2477                            if expected_revert.actual_count < expected_revert.count {
2478                                self.expected_revert = Some(expected_revert);
2479                            }
2480                            if clear_last_frame_gas {
2481                                self.gas_metering.last_frame_gas = None;
2482                            }
2483                            outcome.result.result = InstructionResult::Return;
2484                            outcome.result.output = retdata;
2485                        }
2486                    };
2487                }
2488
2489                // Flip `pending_processing` flag for cheatcode revert expectations, marking that
2490                // we've exited the `expectCheatcodeRevert` call scope
2491                if let ExpectedRevertKind::Cheatcode { pending_processing } =
2492                    &mut self.expected_revert.as_mut().unwrap().kind
2493                {
2494                    *pending_processing = false;
2495                }
2496            }
2497        }
2498
2499        // Exit early for calls to cheatcodes as other logic is not relevant for cheatcode
2500        // invocations
2501        if cheatcode_call {
2502            return;
2503        }
2504
2505        // Record the gas usage of the call, this allows the `lastFrameGas` cheatcode to
2506        // retrieve the gas usage of the last call or create.
2507        let frame_gas = frame_gas(&outcome.result);
2508        let snapshot_gas_used =
2509            isolated_snapshot_gas_used.unwrap_or_else(|| outcome.result.gas.total_gas_spent());
2510        self.gas_metering.last_call_gas = Some(frame_gas.clone());
2511        self.gas_metering.last_frame_gas = Some(frame_gas);
2512        self.gas_metering.last_call_snapshot_gas_used = snapshot_gas_used;
2513        self.gas_metering.last_frame_snapshot_gas_used = snapshot_gas_used;
2514
2515        // If `startStateDiffRecording` has been called, update the `reverted` status of the
2516        // previous call depth's recorded accesses, if any
2517        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2518            // The root call cannot be recorded.
2519            if ecx.journal().depth() > 0
2520                && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
2521            {
2522                // Update the reverted status of all deeper calls if this call reverted, in
2523                // accordance with EVM behavior
2524                if outcome.result.is_revert() {
2525                    mark_account_accesses_reverted(&mut last_recorded_depth);
2526                }
2527
2528                if let Some(call_access) = last_recorded_depth.first_mut() {
2529                    // Assert that we're at the correct depth before recording post-call state
2530                    // changes. Depending on the depth the cheat was
2531                    // called at, there may not be any pending
2532                    // calls to update if execution has percolated up to a higher depth.
2533                    let curr_depth = ecx.journal().depth();
2534                    if call_access.depth == curr_depth as u64
2535                        && let Ok(acc) = ecx.journal_mut().load_account(call.transfer_to())
2536                    {
2537                        debug_assert!(access_is_call(call_access.kind));
2538                        call_access.newBalance = acc.data.info.balance;
2539                        call_access.newNonce = acc.data.info.nonce;
2540                    }
2541                    merge_recorded_frame(recorded_account_diffs_stack, last_recorded_depth);
2542                }
2543            }
2544        }
2545
2546        // this will ensure we don't have false positives when trying to diagnose reverts in fork
2547        // mode
2548        let diag = self.fork_revert_diagnostic.take();
2549
2550        // If the call already reverted, preserve that primary failure and skip post-call
2551        // expect* validation so it cannot overwrite the original revert.
2552        if outcome.result.is_revert() {
2553            // if there's a revert and a previous call was diagnosed as fork related revert then we
2554            // can return a better error here
2555            if let Some(err) = diag {
2556                outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
2557            }
2558            return;
2559        }
2560
2561        if let Some(unmet) = expected_emit::check_call_emits(
2562            &mut self.expected_emits,
2563            ecx.journal().depth(),
2564            call.is_static,
2565            outcome.result.is_ok(),
2566        ) {
2567            outcome.result.result = InstructionResult::Revert;
2568            outcome.result.output = unmet.encode(|| self.signatures_identifier());
2569            return;
2570        }
2571
2572        // try to diagnose reverts in multi-fork mode where a call is made to an address that does
2573        // not exist
2574        if let TxKind::Call(test_contract) = ecx.tx().kind() {
2575            // if a call to a different contract than the original test contract returned with
2576            // `Stop` we check if the contract actually exists on the active fork
2577            if ecx.db().is_forked_mode()
2578                && outcome.result.result == InstructionResult::Stop
2579                && call.transfer_to() != test_contract
2580            {
2581                self.fork_revert_diagnostic =
2582                    ecx.db().diagnose_revert(call.transfer_to(), ecx.journal().evm_state());
2583            }
2584        }
2585
2586        // If the depth is 0, then this is the root call terminating
2587        if ecx.journal().depth() == 0 {
2588            // If we already have a revert, we shouldn't run the below logic as it can obfuscate an
2589            // earlier error that happened first with unrelated information about
2590            // another error when using cheatcodes.
2591            if outcome.result.is_revert() {
2592                return;
2593            }
2594
2595            // If there's not a revert, we can continue on to run the last logic for expect*
2596            // cheatcodes.
2597
2598            // Match expected calls
2599            if let Some(msg) =
2600                expect::first_unmet_call(&self.expected_calls, outcome.result.is_ok())
2601            {
2602                outcome.result.result = InstructionResult::Revert;
2603                outcome.result.output = Error::encode(msg);
2604                return;
2605            }
2606
2607            // Check if we have any leftover expected emits
2608            if let Some(msg) = expected_emit::first_unmet_root_emit(
2609                &mut self.expected_emits,
2610                outcome.result.is_ok(),
2611            ) {
2612                outcome.result.result = InstructionResult::Revert;
2613                outcome.result.output = Error::encode(msg);
2614                return;
2615            }
2616
2617            // Check for leftover expected creates
2618            if let Some(msg) = expect::first_unmet_create(&self.expected_creates) {
2619                outcome.result.result = InstructionResult::Revert;
2620                outcome.result.output = Error::encode(msg);
2621            }
2622        }
2623    }
2624
2625    fn create(
2626        &mut self,
2627        ecx: &mut FoundryContextFor<'_, FEN>,
2628        mut input: &mut CreateInputs,
2629    ) -> Option<CreateOutcome> {
2630        // Apply custom execution evm version.
2631        if let Some(spec_id) = self.execution_evm_version {
2632            EvmFactoryFor::<FEN>::set_execution_spec(ecx, spec_id);
2633        }
2634
2635        let gas = Gas::new(input.gas_limit());
2636        let curr_depth = ecx.journal().depth();
2637        self.start_created_accounts_frame(
2638            curr_depth == 0,
2639            CreatedAccountsFrameKind::Create,
2640            curr_depth,
2641        );
2642
2643        // Check if we should intercept this create
2644        if self.intercept_next_create_call {
2645            // Reset the flag
2646            self.intercept_next_create_call = false;
2647
2648            // Get initcode from the input
2649            let output = input.init_code();
2650
2651            // Return a revert with the initcode as error data
2652            return Some(CreateOutcome {
2653                result: InterpreterResult { result: InstructionResult::Revert, output, gas },
2654                address: None,
2655                charged_create_state_gas: input.charged_create_state_gas(),
2656            });
2657        }
2658
2659        // Apply our prank
2660        if let Some(prank) = self.get_prank(curr_depth)
2661            && let Some(changes) = prank.changes_for(curr_depth, input.caller())
2662        {
2663            if let Some(new_caller) = changes.caller {
2664                // Ensure new caller is loaded and touched
2665                let _ = journaled_account(ecx, new_caller);
2666                input.set_caller(new_caller);
2667            }
2668            if let Some(new_origin) = changes.origin {
2669                ecx.tx_mut().set_caller(new_origin);
2670            }
2671            if let Some(used) = changes.used {
2672                self.pranks.insert(curr_depth, used);
2673            }
2674        }
2675
2676        // Apply EIP-2930 access list
2677        self.apply_accesslist(ecx);
2678
2679        // Apply our broadcast
2680        if let Some(broadcast) = &mut self.broadcast
2681            && curr_depth >= broadcast.depth
2682            && input.caller() == broadcast.original_caller
2683        {
2684            if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2685                return Some(CreateOutcome {
2686                    result: InterpreterResult {
2687                        result: InstructionResult::Revert,
2688                        output: Error::encode(err),
2689                        gas,
2690                    },
2691                    address: None,
2692                    charged_create_state_gas: input.charged_create_state_gas(),
2693                });
2694            }
2695
2696            ecx.tx_mut().set_caller(broadcast.new_origin);
2697
2698            if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2699                // Reset deploy from code flag for upcoming calls;
2700                broadcast.deploy_from_code = false;
2701
2702                input.set_caller(broadcast.new_origin);
2703
2704                let rpc = ecx.db().active_fork_url();
2705                let fee_token = ecx.tx().fee_token();
2706                let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2707                let mut tx_req = TransactionRequestFor::<FEN>::default()
2708                    .with_from(broadcast.new_origin)
2709                    .with_kind(TxKind::Create)
2710                    .with_value(input.value())
2711                    .with_input(input.init_code())
2712                    .with_nonce(account.info.nonce);
2713                if let Some(fee_token) = fee_token {
2714                    tx_req.set_fee_token(fee_token);
2715                }
2716                self.broadcastable_transactions.push_back(BroadcastableTransaction {
2717                    rpc,
2718                    transaction: TransactionMaybeSigned::new(tx_req),
2719                });
2720
2721                input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2722            }
2723        }
2724
2725        // Allow cheatcodes from the address of the new contract
2726        let address = input.allow_cheatcodes(self, ecx);
2727
2728        self.record_created_account(ecx.db().active_fork_id(), address);
2729
2730        // If `recordAccountAccesses` has been called, record the create
2731        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2732            recorded_account_diffs_stack.push(vec![AccountAccess {
2733                chainInfo: crate::Vm::ChainInfo {
2734                    forkId: ecx.db().active_fork_id().unwrap_or_default(),
2735                    chainId: U256::from(ecx.cfg().chain_id()),
2736                },
2737                accessor: input.caller(),
2738                account: address,
2739                kind: crate::Vm::AccountAccessKind::Create,
2740                initialized: true,
2741                oldBalance: U256::ZERO, // updated on create_end
2742                newBalance: U256::ZERO, // updated on create_end
2743                oldNonce: 0,            // new contract starts with nonce 0
2744                newNonce: 1,            // updated on create_end (contracts start with nonce 1)
2745                value: input.value(),
2746                data: input.init_code(),
2747                reverted: false,
2748                deployedCode: Bytes::new(), // updated on create_end
2749                storageAccesses: vec![],    // updated on create_end
2750                depth: curr_depth as u64,
2751            }]);
2752        }
2753
2754        None
2755    }
2756
2757    fn create_end(
2758        &mut self,
2759        ecx: &mut FoundryContextFor<'_, FEN>,
2760        call: &CreateInputs,
2761        outcome: &mut CreateOutcome,
2762    ) {
2763        let isolated_snapshot_gas_used = self
2764            .gas_metering
2765            .take_isolated_snapshot_gas_used(ecx.journal().depth(), &outcome.result.gas);
2766        let call = Some(call);
2767        let curr_depth = ecx.journal().depth();
2768
2769        self.finish_created_accounts_frame(
2770            outcome.result.is_ok(),
2771            CreatedAccountsFrameKind::Create,
2772            curr_depth,
2773        );
2774
2775        // Clean up pranks
2776        if let Some(prank) = &self.get_prank(curr_depth)
2777            && curr_depth == prank.depth
2778        {
2779            ecx.tx_mut().set_caller(prank.prank_origin);
2780
2781            // Clean single-call prank once we have returned to the original depth
2782            if prank.single_call {
2783                self.pranks.remove(&curr_depth);
2784            }
2785        }
2786
2787        // Clean up broadcasts
2788        if let Some(broadcast) = &self.broadcast
2789            && curr_depth == broadcast.depth
2790        {
2791            ecx.tx_mut().set_caller(broadcast.original_origin);
2792
2793            // Clean single-call broadcast once we have returned to the original depth
2794            if broadcast.single_call {
2795                std::mem::take(&mut self.broadcast);
2796            }
2797        }
2798
2799        // Handle expected reverts.
2800        if let Some(expected_revert) = &mut self.expected_revert {
2801            // Record the would-be deployed address as the reverter, picking the innermost
2802            // reverting CREATE: this hook runs at every depth, the deepest frame fires
2803            // first, and the `is_none()` lock pins it. For `count > 1` the lock is
2804            // released after each successful iteration (see below) so each iteration
2805            // independently records its own innermost CREATE.
2806            //
2807            // This intentionally differs from `call_end` for `count > 1`, where
2808            // legacy nested CALL handling reports the outermost call per iteration.
2809            //
2810            // `outcome.address` is `None` for pre-frame rejection (depth/balance/nonce);
2811            // in that case the surrounding `call_end` records the caller as the reverter.
2812            if outcome.result.is_revert()
2813                && expected_revert.reverter.is_some()
2814                && expected_revert.reverted_by.is_none()
2815                && let Some(addr) = outcome.address
2816            {
2817                expected_revert.reverted_by = Some(addr);
2818            }
2819
2820            if curr_depth <= expected_revert.depth
2821                && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2822            {
2823                let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2824                return match revert_handlers::handle_expect_revert(
2825                    false,
2826                    true,
2827                    self.config.internal_expect_revert,
2828                    &expected_revert,
2829                    outcome.result.result,
2830                    outcome.result.output.clone(),
2831                    &self.config.available_artifacts,
2832                ) {
2833                    Ok((address, retdata)) => {
2834                        expected_revert.actual_count += 1;
2835                        if expected_revert.actual_count < expected_revert.count {
2836                            // Reset so the next iteration's innermost CREATE wins again.
2837                            expected_revert.reverted_by = None;
2838                            self.expected_revert = Some(expected_revert.clone());
2839                        }
2840
2841                        outcome.result.result = InstructionResult::Return;
2842                        outcome.result.output = retdata;
2843                        outcome.address = address;
2844                        self.gas_metering.last_frame_gas = None;
2845                    }
2846                    Err(err) => {
2847                        outcome.result.result = InstructionResult::Revert;
2848                        outcome.result.output = err.abi_encode().into();
2849                    }
2850                };
2851            }
2852        }
2853
2854        if curr_depth > 0 {
2855            // Record the gas usage of the create frame, this allows the `lastFrameGas` cheatcode to
2856            // retrieve the gas usage of the last call or create.
2857            self.gas_metering.last_frame_gas = Some(frame_gas(&outcome.result));
2858            self.gas_metering.last_frame_snapshot_gas_used =
2859                isolated_snapshot_gas_used.unwrap_or_else(|| outcome.result.gas.total_gas_spent());
2860        }
2861
2862        // If `startStateDiffRecording` has been called, update the `reverted` status of the
2863        // previous call depth's recorded accesses, if any.
2864        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack
2865            && let Some(mut last_depth) = recorded_account_diffs_stack.pop()
2866        {
2867            // Update the reverted status of all deeper calls if this call reverted, in
2868            // accordance with EVM behavior.
2869            if outcome.result.is_revert() {
2870                mark_account_accesses_reverted(&mut last_depth);
2871            }
2872
2873            if let Some(create_access) = last_depth.first_mut() {
2874                // Update post-create state only if recording began before this frame.
2875                if create_access.depth == curr_depth as u64 {
2876                    debug_assert_eq!(
2877                        create_access.kind as u8,
2878                        crate::Vm::AccountAccessKind::Create as u8
2879                    );
2880                    if let Some(address) = outcome.address
2881                        && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2882                    {
2883                        create_access.newBalance = created_acc.data.info.balance;
2884                        create_access.newNonce = created_acc.data.info.nonce;
2885                        create_access.deployedCode =
2886                            created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2887                    }
2888                }
2889            }
2890            merge_recorded_frame(recorded_account_diffs_stack, last_depth);
2891        }
2892
2893        // Match the create against expected_creates
2894        if !self.expected_creates.is_empty()
2895            && let (Some(address), Some(call)) = (outcome.address, call)
2896            && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2897        {
2898            let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2899            expect::observe_create(
2900                &mut self.expected_creates,
2901                call.caller(),
2902                || call.scheme().into(),
2903                &bytecode,
2904            );
2905        }
2906    }
2907}
2908
2909impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2910    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2911        // `deployCode` executes its create frame in a nested EVM one level deeper, so match it
2912        // at the depth of the cheatcode call, as for native creates.
2913        let depth =
2914            if self.deploy_code_depth.is_some_and(|d| d + 1 == depth) { depth - 1 } else { depth };
2915        let target_depth = if let Some(prank) = &self.get_prank(depth) {
2916            prank.depth
2917        } else if let Some(broadcast) = &self.broadcast {
2918            broadcast.depth
2919        } else {
2920            1
2921        };
2922
2923        if depth != target_depth {
2924            return false;
2925        }
2926
2927        match inputs.scheme() {
2928            CreateScheme::Create2 { .. } => {
2929                self.broadcast.is_some() || self.config.always_use_create_2_factory
2930            }
2931            CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
2932            _ => false,
2933        }
2934    }
2935
2936    fn create2_deployer(&self) -> Address {
2937        self.config.evm_opts.create2_deployer
2938    }
2939}
2940
2941impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2942    #[cold]
2943    fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2944        if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2945            // Keep gas constant if paused.
2946            // Make sure we record the memory changes so that memory expansion is not paused.
2947            let memory = *interpreter.gas.memory();
2948            interpreter.gas = *paused_gas;
2949            interpreter.gas.memory_mut().words_num = memory.words_num;
2950            interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2951        } else {
2952            // Record frame paused gas.
2953            self.gas_metering.paused_frames.push(interpreter.gas);
2954        }
2955    }
2956
2957    #[cold]
2958    fn meter_gas_record(
2959        &mut self,
2960        interpreter: &mut Interpreter,
2961        ecx: &mut FoundryContextFor<'_, FEN>,
2962    ) {
2963        if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2964            let curr_depth = ecx.journal().depth();
2965            let isolated_region_gas = match self.gas_metering.pending_isolated_region_gas {
2966                Some((depth, charged, used)) if depth == curr_depth => {
2967                    self.gas_metering.pending_isolated_region_gas = None;
2968                    Some((charged, used))
2969                }
2970                _ => None,
2971            };
2972            self.gas_metering.gas_records.iter_mut().for_each(|record| {
2973                if curr_depth == record.depth {
2974                    // Skip the first opcode of the first call frame as it includes the gas cost of
2975                    // creating the snapshot.
2976                    if self.gas_metering.last_gas_used != 0 {
2977                        let mut gas_diff = interpreter
2978                            .gas
2979                            .total_gas_spent()
2980                            .saturating_sub(self.gas_metering.last_gas_used);
2981                        if let Some((charged, used)) = isolated_region_gas {
2982                            gas_diff = gas_diff.saturating_sub(charged).saturating_add(used);
2983                        }
2984                        record.gas_used = record.gas_used.saturating_add(gas_diff);
2985                    }
2986
2987                    // Update `last_gas_used` to the current spent gas for the next iteration to
2988                    // compare against.
2989                    self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2990                }
2991            });
2992        }
2993    }
2994
2995    #[cold]
2996    fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2997        // Remove recorded gas if we exit frame.
2998        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2999            && will_exit(interpreter_action)
3000        {
3001            self.gas_metering.paused_frames.pop();
3002        }
3003    }
3004
3005    #[cold]
3006    const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
3007        let mut gas = Gas::new(interpreter.gas.limit());
3008        gas.memory_mut().words_num = interpreter.gas.memory().words_num;
3009        gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
3010        interpreter.gas = gas;
3011        self.gas_metering.reset = false;
3012    }
3013
3014    #[cold]
3015    fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
3016        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
3017            && will_exit(interpreter_action)
3018        {
3019            // Reset gas if spent is less than refunded.
3020            // This can happen if gas was paused / resumed or reset.
3021            // https://github.com/foundry-rs/foundry/issues/4370
3022            if interpreter.gas.total_gas_spent()
3023                < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
3024            {
3025                interpreter.gas = Gas::new(interpreter.gas.limit());
3026            }
3027        }
3028    }
3029
3030    /// Applies opcode-level overrides for `BASEFEE`, `GASPRICE` and `BLOBHASH`.
3031    ///
3032    /// Called from `step_end` *after* the opcode has executed and only when the
3033    /// opcode succeeded (the caller checks `instruction_result`). The opcode
3034    /// pushed its (possibly zeroed) result onto the stack; we replace the top
3035    /// of stack with the cheatcode-set override. This is what makes `vm.fee`,
3036    /// `vm.txGasPrice` and `vm.blobhashes` visible to called contracts under
3037    /// `--isolate` / `--gas-report`, where the inner transaction zeroes the
3038    /// real fee fields for fee-accounting purposes.
3039    ///
3040    /// We can't read the just-executed opcode from `interpreter.bytecode.opcode()`
3041    /// here because the PC has already advanced; instead `step` stashes it in
3042    /// `env_overrides.pending_opcode` for us.
3043    #[cold]
3044    fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
3045        let Some(env_overrides) = self.env_overrides.get_mut(fork_id) else { return };
3046        let Some(opcode) = env_overrides.pending_opcode.take() else { return };
3047        // Each overridden opcode pushed one value; replace it with the override.
3048        let value = match opcode {
3049            op::BASEFEE => env_overrides.basefee_override().map(U256::from),
3050            op::GASPRICE => env_overrides.gas_price_override().map(U256::from),
3051            // BLOBHASH popped the index captured in `step` and pushed the hash.
3052            op::BLOBHASH => env_overrides
3053                .pending_blobhash_index
3054                .take()
3055                .and_then(|index| env_overrides.blob_hash_override(index))
3056                .map(Into::into),
3057            _ => None,
3058        };
3059        if let Some(value) = value {
3060            Self::replace_top_of_stack(interpreter, value);
3061        }
3062    }
3063
3064    /// Replaces the top of the interpreter stack with `value`.
3065    ///
3066    /// The caller must only invoke this after a successful opcode that pushed
3067    /// a value onto the stack; the `pop()` is therefore expected to succeed.
3068    /// If it does not (e.g. because of a bug in the caller's success gating)
3069    /// we bail out instead of pushing on top of an unexpected stack, which
3070    /// would silently grow the stack and corrupt the frame.
3071    fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
3072        if interpreter.stack.pop().is_err() {
3073            debug_assert!(false, "env override expected opcode result on stack");
3074            return;
3075        }
3076        let _ = interpreter.stack.push(value);
3077    }
3078
3079    /// Generates or copies arbitrary values for storage slots.
3080    /// Invoked in inspector `step_end` (when the current opcode is not executed), if current opcode
3081    /// to execute is `SLOAD` and storage slot is cold.
3082    /// Ensures that in next step (when `SLOAD` opcode is executed) an arbitrary value is returned:
3083    /// - copies the existing arbitrary storage value (or the new generated one if no value in
3084    ///   cache) from mapped source address to the target address.
3085    /// - generates arbitrary value and saves it in target address storage.
3086    #[cold]
3087    fn arbitrary_storage_end(
3088        &mut self,
3089        interpreter: &mut Interpreter,
3090        ecx: &mut FoundryContextFor<'_, FEN>,
3091    ) {
3092        let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
3093            (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
3094        } else {
3095            return;
3096        };
3097
3098        if self.is_arbitrary_storage_slot_explicit(target_address, key) {
3099            return;
3100        }
3101
3102        let Some(value) = ecx.sload(target_address, key) else {
3103            return;
3104        };
3105
3106        if (value.is_cold && value.data.is_zero())
3107            || self.should_overwrite_arbitrary_storage(&target_address, key)
3108        {
3109            if self.has_arbitrary_storage(&target_address) {
3110                let arbitrary_value = self
3111                    .cached_arbitrary_storage_value(target_address, key)
3112                    .unwrap_or_else(|| self.rng().random());
3113                self.arbitrary_storage.as_mut().unwrap().save(
3114                    ecx,
3115                    target_address,
3116                    key,
3117                    arbitrary_value,
3118                );
3119            } else if self.is_arbitrary_storage_copy(&target_address) {
3120                let arbitrary_value = self.rng().random();
3121                self.arbitrary_storage.as_mut().unwrap().copy(
3122                    ecx,
3123                    target_address,
3124                    key,
3125                    arbitrary_value,
3126                );
3127            }
3128        }
3129    }
3130
3131    /// Restores parent interpreter state after a synthetic storage-hook callback.
3132    ///
3133    /// Returns whether a failed callback was propagated to the parent frame.
3134    #[inline]
3135    pub fn finish_storage_hook_callback(
3136        &mut self,
3137        interpreter: &mut Interpreter,
3138        ecx: &mut FoundryContextFor<'_, FEN>,
3139    ) -> bool {
3140        let Some(active) = self.active_storage_hook.as_ref() else { return false };
3141        let Some((result, output)) = active.outcome.clone() else { return false };
3142
3143        let active = self.active_storage_hook.take().expect("active storage hook exists");
3144        Self::restore_storage_hook_access(ecx, active.journal_start);
3145        self.restore_storage_hook_inspector_state(active.inspector_state);
3146        let _ = interpreter.stack.pop();
3147        if let Some(item) = active.saved_stack_item {
3148            let result = interpreter.stack.push(item);
3149            debug_assert!(result, "reserved storage-hook stack slot must be available");
3150        }
3151        interpreter.gas = active.saved_gas;
3152        interpreter.return_data.set_buffer(active.saved_return_data);
3153
3154        if result.is_ok() {
3155            false
3156        } else {
3157            interpreter.bytecode.set_action(InterpreterAction::new_return(
3158                InstructionResult::Revert,
3159                output,
3160                interpreter.gas,
3161            ));
3162            true
3163        }
3164    }
3165
3166    fn take_storage_hook_inspector_state(&mut self) -> StorageHookInspectorState {
3167        StorageHookInspectorState {
3168            accesses: std::mem::take(&mut self.accesses),
3169            recording_accesses: std::mem::replace(&mut self.recording_accesses, false),
3170            mapping_slots: self.mapping_slots.take(),
3171            recorded_logs: self.recorded_logs.take(),
3172            mocked_calls: std::mem::take(&mut self.mocked_calls),
3173            mocked_functions: std::mem::take(&mut self.mocked_functions),
3174            expected_revert: self.expected_revert.take(),
3175            assume_no_revert: self.assume_no_revert.take(),
3176            expected_calls: std::mem::take(&mut self.expected_calls),
3177            expected_emits: std::mem::take(&mut self.expected_emits),
3178            expected_creates: std::mem::take(&mut self.expected_creates),
3179        }
3180    }
3181
3182    fn restore_storage_hook_inspector_state(&mut self, state: StorageHookInspectorState) {
3183        self.accesses = state.accesses;
3184        self.recording_accesses = state.recording_accesses;
3185        self.mapping_slots = state.mapping_slots;
3186        self.recorded_logs = state.recorded_logs;
3187        self.mocked_calls = state.mocked_calls;
3188        self.mocked_functions = state.mocked_functions;
3189        self.expected_revert = state.expected_revert;
3190        self.assume_no_revert = state.assume_no_revert;
3191        self.expected_calls = state.expected_calls;
3192        self.expected_emits = state.expected_emits;
3193        self.expected_creates = state.expected_creates;
3194    }
3195
3196    fn restore_storage_hook_access(ecx: &mut FoundryContextFor<'_, FEN>, journal_start: usize) {
3197        let (_, journal) = ecx.db_journal_inner_mut();
3198        let entries =
3199            journal.journal.drain(journal_start.min(journal.journal.len())..).collect_vec();
3200        for entry in entries {
3201            match entry {
3202                JournalEntry::AccountWarmed { address } => {
3203                    journal.state.get_mut(&address).expect("warmed account exists").mark_cold();
3204                }
3205                JournalEntry::StorageWarmed { address, key } => {
3206                    // TODO(@mablr): Preserve the EIP-2200 `original_value` when bumping the REVM
3207                    // family to 42. REVM 41's `mark_cold` resets it for a slot first warmed and
3208                    // modified by the callback.
3209                    journal
3210                        .state
3211                        .get_mut(&address)
3212                        .expect("warmed account exists")
3213                        .storage
3214                        .get_mut(&key)
3215                        .expect("warmed storage slot exists")
3216                        .mark_cold();
3217                }
3218                entry => journal.journal.push(entry),
3219            }
3220        }
3221    }
3222
3223    fn capture_storage_hook(
3224        &mut self,
3225        interpreter: &Interpreter,
3226        ecx: &mut FoundryContextFor<'_, FEN>,
3227    ) {
3228        self.pending_storage_hook = None;
3229        if self.active_storage_hook.is_some() {
3230            return;
3231        }
3232        let account = interpreter.input.target_address;
3233        match interpreter.bytecode.opcode() {
3234            op::SLOAD => {
3235                let slot = try_or_return!(interpreter.stack.peek(0));
3236                let Some(hook) = self.storage_load_hooks.get(&account).copied() else { return };
3237                self.pending_storage_hook = Some(PendingStorageHook::Load { account, slot, hook });
3238            }
3239            op::SSTORE => {
3240                let slot = try_or_return!(interpreter.stack.peek(0));
3241                let (hook, mapping) = if let Some(hook) = self.storage_store_hooks.get(&account) {
3242                    (*hook, None)
3243                } else {
3244                    let Some(provenance) = self
3245                        .storage_hook_mapping_slots
3246                        .get(&account)
3247                        .and_then(|slots| slots.resolve(slot.into()))
3248                    else {
3249                        return;
3250                    };
3251                    let Some(hook) = self
3252                        .mapping_storage_store_hooks
3253                        .get(&account)
3254                        .and_then(|hooks| hooks.get(&provenance.root_slot))
3255                        .copied()
3256                    else {
3257                        return;
3258                    };
3259                    (hook, Some((provenance.root_slot, provenance.keys)))
3260                };
3261                let checkpoint = ecx.journal_mut().checkpoint();
3262                let old_value =
3263                    ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3264                ecx.journal_mut().checkpoint_revert(checkpoint);
3265                self.pending_storage_hook =
3266                    Some(PendingStorageHook::Store { account, slot, old_value, mapping, hook });
3267            }
3268            _ => {}
3269        }
3270    }
3271
3272    fn invoke_pending_storage_hook(
3273        &mut self,
3274        interpreter: &mut Interpreter,
3275        ecx: &mut FoundryContextFor<'_, FEN>,
3276    ) {
3277        let Some(pending) = self.pending_storage_hook.take() else { return };
3278        if interpreter
3279            .bytecode
3280            .action
3281            .as_ref()
3282            .and_then(InterpreterAction::instruction_result)
3283            .is_some()
3284        {
3285            return;
3286        }
3287
3288        let (hook, input, saved_stack_item) = match pending {
3289            PendingStorageHook::Load { account, slot, hook } => {
3290                let value = try_or_return!(interpreter.stack.peek(0));
3291                let mut input = Vec::with_capacity(4 + 32 * 3);
3292                input.extend_from_slice(&hook.callback_selector);
3293                input.extend_from_slice(account.into_word().as_slice());
3294                input.extend_from_slice(&slot.to_be_bytes::<32>());
3295                input.extend_from_slice(&value.to_be_bytes::<32>());
3296                (hook, Bytes::from(input), Some(value))
3297            }
3298            PendingStorageHook::Store { account, slot, old_value, mapping, hook } => {
3299                let new_value =
3300                    ecx.sload(account, slot).map(|value| value.data).unwrap_or_default();
3301                let mut input = Vec::with_capacity(4 + 32 * 4);
3302                input.extend_from_slice(&hook.callback_selector);
3303                input.extend_from_slice(account.into_word().as_slice());
3304                input.extend_from_slice(&slot.to_be_bytes::<32>());
3305                if let Some((root, keys)) = mapping {
3306                    input.extend_from_slice(root.as_slice());
3307                    input.extend_from_slice(&U256::from(32 * 6).to_be_bytes::<32>());
3308                    input.extend_from_slice(&old_value.to_be_bytes::<32>());
3309                    input.extend_from_slice(&new_value.to_be_bytes::<32>());
3310                    input.extend_from_slice(&U256::from(keys.len()).to_be_bytes::<32>());
3311                    for key in keys {
3312                        input.extend_from_slice(key.as_slice());
3313                    }
3314                } else {
3315                    input.extend_from_slice(&old_value.to_be_bytes::<32>());
3316                    input.extend_from_slice(&new_value.to_be_bytes::<32>());
3317                }
3318                (hook, Bytes::from(input), None)
3319            }
3320        };
3321
3322        let journal_start = ecx.db_journal_inner_mut().1.journal.len();
3323        let account = match ecx.journal_mut().load_account_with_code(hook.callback_target) {
3324            Ok(account) => account,
3325            Err(err) => {
3326                interpreter.bytecode.set_action(InterpreterAction::new_return(
3327                    InstructionResult::Revert,
3328                    Error::encode(err),
3329                    interpreter.gas,
3330                ));
3331                return;
3332            }
3333        };
3334        let known_bytecode =
3335            (account.info.code_hash(), account.info.code.clone().unwrap_or_default());
3336        let saved_gas = interpreter.gas;
3337        let saved_return_data = Bytes::copy_from_slice(interpreter.return_data.buffer());
3338        let gas_limit = interpreter.gas.remaining();
3339        let parent_depth = ecx.journal().depth();
3340        if saved_stack_item.is_some() {
3341            let result = interpreter.stack.pop();
3342            debug_assert!(result.is_ok(), "captured SLOAD result must be on the stack");
3343        }
3344        let inspector_state = self.take_storage_hook_inspector_state();
3345
3346        self.active_storage_hook = Some(ActiveStorageHook {
3347            parent_depth,
3348            callback_target: hook.callback_target,
3349            callback_input: input.clone(),
3350            saved_gas,
3351            saved_return_data,
3352            saved_stack_item,
3353            journal_start,
3354            inspector_state,
3355            outcome: None,
3356        });
3357        interpreter.bytecode.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
3358            CallInputs {
3359                input: CallInput::Bytes(input),
3360                return_memory_offset: 0..0,
3361                gas_limit,
3362                reservoir: 0,
3363                bytecode_address: hook.callback_target,
3364                known_bytecode,
3365                target_address: hook.callback_target,
3366                caller: CHEATCODE_ADDRESS,
3367                value: CallValue::Transfer(U256::ZERO),
3368                scheme: CallScheme::Call,
3369                is_static: false,
3370                charged_new_account_state_gas: false,
3371            },
3372        ))));
3373    }
3374
3375    /// Records storage slots reads and writes.
3376    #[cold]
3377    fn record_accesses(&mut self, interpreter: &mut Interpreter) {
3378        let access = &mut self.accesses;
3379        match interpreter.bytecode.opcode() {
3380            op::SLOAD => {
3381                let key = try_or_return!(interpreter.stack.peek(0));
3382                access.record_read(interpreter.input.target_address, key);
3383            }
3384            op::SSTORE => {
3385                let key = try_or_return!(interpreter.stack.peek(0));
3386                access.record_write(interpreter.input.target_address, key);
3387            }
3388            _ => {}
3389        }
3390    }
3391
3392    #[cold]
3393    fn record_state_diffs(
3394        &mut self,
3395        interpreter: &mut Interpreter,
3396        ecx: &mut FoundryContextFor<'_, FEN>,
3397    ) {
3398        let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
3399        match interpreter.bytecode.opcode() {
3400            op::SELFDESTRUCT => {
3401                // Ensure that we're not selfdestructing a context recording was initiated on
3402                let Some(last) = account_accesses.last_mut() else { return };
3403
3404                // get previous balance, nonce and initialized status of the target account
3405                let target = try_or_return!(interpreter.stack.peek(0));
3406                let target = Address::from_word(B256::from(target));
3407                let (initialized, old_balance, old_nonce) = ecx
3408                    .journal_mut()
3409                    .load_account(target)
3410                    .map(|account| {
3411                        (
3412                            account.data.info.exists(),
3413                            account.data.info.balance,
3414                            account.data.info.nonce,
3415                        )
3416                    })
3417                    .unwrap_or_default();
3418
3419                // load balance of this account
3420                let value = ecx
3421                    .balance(interpreter.input.target_address)
3422                    .map(|b| b.data)
3423                    .unwrap_or(U256::ZERO);
3424
3425                // register access for the target account
3426                last.push(crate::Vm::AccountAccess {
3427                    chainInfo: crate::Vm::ChainInfo {
3428                        forkId: ecx.db().active_fork_id().unwrap_or_default(),
3429                        chainId: U256::from(ecx.cfg().chain_id()),
3430                    },
3431                    accessor: interpreter.input.target_address,
3432                    account: target,
3433                    kind: crate::Vm::AccountAccessKind::SelfDestruct,
3434                    initialized,
3435                    oldBalance: old_balance,
3436                    newBalance: old_balance + value,
3437                    oldNonce: old_nonce,
3438                    newNonce: old_nonce, // nonce doesn't change on selfdestruct
3439                    value,
3440                    data: Bytes::new(),
3441                    reverted: false,
3442                    deployedCode: Bytes::new(),
3443                    storageAccesses: vec![],
3444                    depth: ecx
3445                        .journal()
3446                        .depth()
3447                        .try_into()
3448                        .expect("journaled state depth exceeds u64"),
3449                });
3450            }
3451
3452            op::SLOAD => {
3453                let Some(last) = account_accesses.last_mut() else { return };
3454
3455                let key = try_or_return!(interpreter.stack.peek(0));
3456                let address = interpreter.input.target_address;
3457
3458                // Try to include present value for informational purposes, otherwise assume
3459                // it's not set (zero value). Revert the checkpoint so this read does not warm the
3460                // slot for the actual SLOAD opcode.
3461                let checkpoint = ecx.journal_mut().checkpoint();
3462                let present_value =
3463                    ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3464                ecx.journal_mut().checkpoint_revert(checkpoint);
3465                let access = crate::Vm::StorageAccess {
3466                    account: interpreter.input.target_address,
3467                    slot: key.into(),
3468                    isWrite: false,
3469                    previousValue: present_value.into(),
3470                    newValue: present_value.into(),
3471                    reverted: false,
3472                };
3473                let curr_depth =
3474                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3475                append_storage_access(last, access, curr_depth);
3476            }
3477            op::SSTORE => {
3478                let Some(last) = account_accesses.last_mut() else { return };
3479
3480                let key = try_or_return!(interpreter.stack.peek(0));
3481                let value = try_or_return!(interpreter.stack.peek(1));
3482                let address = interpreter.input.target_address;
3483                // Try to load the account and the slot's previous value, otherwise, assume it's
3484                // not set (zero value). Revert the checkpoint so this read does not warm the slot
3485                // for the actual SSTORE opcode.
3486                let checkpoint = ecx.journal_mut().checkpoint();
3487                let previous_value =
3488                    ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
3489                ecx.journal_mut().checkpoint_revert(checkpoint);
3490
3491                let access = crate::Vm::StorageAccess {
3492                    account: address,
3493                    slot: key.into(),
3494                    isWrite: true,
3495                    previousValue: previous_value.into(),
3496                    newValue: value.into(),
3497                    reverted: false,
3498                };
3499                let curr_depth =
3500                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3501                append_storage_access(last, access, curr_depth);
3502            }
3503
3504            // Record account accesses via the EXT family of opcodes
3505            op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
3506                let kind = match interpreter.bytecode.opcode() {
3507                    op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
3508                    op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
3509                    op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
3510                    op::BALANCE => crate::Vm::AccountAccessKind::Balance,
3511                    _ => unreachable!(),
3512                };
3513                let address =
3514                    Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
3515                let checkpoint = ecx.journal_mut().checkpoint();
3516                let (initialized, balance, nonce) = ecx
3517                    .journal_mut()
3518                    .load_account(address)
3519                    .map(|acc| (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce))
3520                    .unwrap_or_default();
3521                ecx.journal_mut().checkpoint_revert(checkpoint);
3522                let curr_depth =
3523                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
3524                let account_access = crate::Vm::AccountAccess {
3525                    chainInfo: crate::Vm::ChainInfo {
3526                        forkId: ecx.db().active_fork_id().unwrap_or_default(),
3527                        chainId: U256::from(ecx.cfg().chain_id()),
3528                    },
3529                    accessor: interpreter.input.target_address,
3530                    account: address,
3531                    kind,
3532                    initialized,
3533                    oldBalance: balance,
3534                    newBalance: balance,
3535                    oldNonce: nonce,
3536                    newNonce: nonce, // EXT* operations don't change nonce
3537                    value: U256::ZERO,
3538                    data: Bytes::new(),
3539                    reverted: false,
3540                    deployedCode: Bytes::new(),
3541                    storageAccesses: vec![],
3542                    depth: curr_depth,
3543                };
3544                // Record the EXT* call as an account access at the current depth
3545                // (future storage accesses will be recorded in a new "Resume" context)
3546                if let Some(last) = account_accesses.last_mut() {
3547                    last.push(account_access);
3548                } else {
3549                    account_accesses.push(vec![account_access]);
3550                }
3551            }
3552            _ => {}
3553        }
3554    }
3555
3556    /// Checks to see if the current opcode can either mutate directly or expand memory.
3557    ///
3558    /// If the opcode at the current program counter is a match, check if the modified memory lies
3559    /// within the allowed ranges. If not, revert and fail the test.
3560    #[cold]
3561    fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
3562        let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
3563            return;
3564        };
3565
3566        // The `mem_opcode_match` macro is used to match the current opcode against a list of
3567        // opcodes that can mutate memory (either directly or expansion via reading). If the
3568        // opcode is a match, the memory offsets that are being written to are checked to be
3569        // within the allowed ranges. If not, the test is failed and the transaction is
3570        // reverted. For all opcodes that can mutate memory aside from MSTORE,
3571        // MSTORE8, and MLOAD, the size and destination offset are on the stack, and
3572        // the macro expands all of these cases. For MSTORE, MSTORE8, and MLOAD, the
3573        // size of the memory write is implicit, so these cases are hard-coded.
3574        macro_rules! mem_opcode_match {
3575            ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
3576                match interpreter.bytecode.opcode() {
3577                    ////////////////////////////////////////////////////////////////
3578                    //    OPERATIONS THAT CAN EXPAND/MUTATE MEMORY BY WRITING     //
3579                    ////////////////////////////////////////////////////////////////
3580
3581                    op::MSTORE => {
3582                        // The offset of the mstore operation is at the top of the stack.
3583                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3584
3585                        // If none of the allowed ranges contain [offset, offset + 32), memory has been
3586                        // unexpectedly mutated.
3587                        if !ranges.iter().any(|range| {
3588                            range.contains(&offset) && range.contains(&(offset + 31))
3589                        }) {
3590                            // SPECIAL CASE: When the compiler attempts to store the selector for
3591                            // `stopExpectSafeMemory`, this is allowed. It will do so at the current free memory
3592                            // pointer, which could have been updated to the exclusive upper bound during
3593                            // execution.
3594                            let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
3595                            if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3596                                return
3597                            }
3598
3599                            disallowed_mem_write(offset, 32, interpreter, ranges);
3600                            return
3601                        }
3602                    }
3603                    op::MSTORE8 => {
3604                        // The offset of the mstore8 operation is at the top of the stack.
3605                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3606
3607                        // If none of the allowed ranges contain the offset, memory has been
3608                        // unexpectedly mutated.
3609                        if !ranges.iter().any(|range| range.contains(&offset)) {
3610                            disallowed_mem_write(offset, 1, interpreter, ranges);
3611                            return
3612                        }
3613                    }
3614
3615                    ////////////////////////////////////////////////////////////////
3616                    //        OPERATIONS THAT CAN EXPAND MEMORY BY READING        //
3617                    ////////////////////////////////////////////////////////////////
3618
3619                    op::MLOAD => {
3620                        // The offset of the mload operation is at the top of the stack
3621                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
3622
3623                        // If the offset being loaded is >= than the memory size, the
3624                        // memory is being expanded. If none of the allowed ranges contain
3625                        // [offset, offset + 32), memory has been unexpectedly mutated.
3626                        if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
3627                            range.contains(&offset) && range.contains(&(offset + 31))
3628                        }) {
3629                            disallowed_mem_write(offset, 32, interpreter, ranges);
3630                            return
3631                        }
3632                    }
3633
3634                    ////////////////////////////////////////////////////////////////
3635                    //          OPERATIONS WITH OFFSET AND SIZE ON STACK          //
3636                    ////////////////////////////////////////////////////////////////
3637
3638                    op::CALL => {
3639                        // The destination offset of the operation is the fifth element on the stack.
3640                        let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
3641
3642                        // The size of the data that will be copied is the sixth element on the stack.
3643                        let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
3644
3645                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
3646                        // memory outside of the expected ranges has been touched. If the opcode
3647                        // only reads from memory, this is okay as long as the memory is not expanded.
3648                        let fail_cond = !ranges.iter().any(|range| {
3649                            range.contains(&dest_offset) &&
3650                                range.contains(&(dest_offset + size.saturating_sub(1)))
3651                        });
3652
3653                        // If the failure condition is met, set the output buffer to a revert string
3654                        // that gives information about the allowed ranges and revert.
3655                        if fail_cond {
3656                            // SPECIAL CASE: When a call to `stopExpectSafeMemory` is performed, this is allowed.
3657                            // It allocated calldata at the current free memory pointer, and will attempt to read
3658                            // from this memory region to perform the call.
3659                            let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
3660                            if to == CHEATCODE_ADDRESS {
3661                                let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
3662                                let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
3663                                // CALL has not expanded input memory yet.
3664                                if args_size >= SELECTOR_LEN
3665                                    && args_offset.saturating_add(args_size) <= interpreter.memory.size()
3666                                {
3667                                    let memory_word = interpreter.memory.slice_len(args_offset, args_size);
3668                                    if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
3669                                        return
3670                                    }
3671                                }
3672                            }
3673
3674                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
3675                            return
3676                        }
3677                    }
3678
3679                    $(op::$opcode => {
3680                        // The destination offset of the operation.
3681                        let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
3682
3683                        // The size of the data that will be copied.
3684                        let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
3685
3686                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
3687                        // memory outside of the expected ranges has been touched. If the opcode
3688                        // only reads from memory, this is okay as long as the memory is not expanded.
3689                        let fail_cond = !ranges.iter().any(|range| {
3690                                range.contains(&dest_offset) &&
3691                                    range.contains(&(dest_offset + size.saturating_sub(1)))
3692                            }) && ($writes ||
3693                                [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
3694                                    offset >= interpreter.memory.size() as u64
3695                                })
3696                            );
3697
3698                        // If the failure condition is met, set the output buffer to a revert string
3699                        // that gives information about the allowed ranges and revert.
3700                        if fail_cond {
3701                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
3702                            return
3703                        }
3704                    })*
3705
3706                    _ => {}
3707                }
3708            }
3709        }
3710
3711        // Check if the current opcode can write to memory, and if so, check if the memory
3712        // being written to is registered as safe to modify.
3713        mem_opcode_match!(
3714            (CALLDATACOPY, 0, 2, true),
3715            (CODECOPY, 0, 2, true),
3716            (RETURNDATACOPY, 0, 2, true),
3717            (EXTCODECOPY, 1, 3, true),
3718            (CALLCODE, 5, 6, true),
3719            (STATICCALL, 4, 5, true),
3720            (DELEGATECALL, 4, 5, true),
3721            (KECCAK256, 0, 1, false),
3722            (LOG0, 0, 1, false),
3723            (LOG1, 0, 1, false),
3724            (LOG2, 0, 1, false),
3725            (LOG3, 0, 1, false),
3726            (LOG4, 0, 1, false),
3727            (CREATE, 1, 2, false),
3728            (CREATE2, 1, 2, false),
3729            (RETURN, 0, 1, false),
3730            (REVERT, 0, 1, false),
3731        );
3732    }
3733
3734    #[cold]
3735    fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
3736        match interpreter.bytecode.opcode() {
3737            op::CREATE2 => self.dynamic_gas_limit = true,
3738            op::CALL => {
3739                // If first element of the stack is close to current remaining gas then assume
3740                // dynamic gas limit.
3741                self.dynamic_gas_limit =
3742                    try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
3743            }
3744            _ => self.dynamic_gas_limit = false,
3745        }
3746    }
3747}
3748
3749/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
3750/// and sets the return range to the revert string's location in memory.
3751///
3752/// This will set the interpreter's next action to a return with the revert string as the output.
3753/// And trigger a revert.
3754fn disallowed_mem_write(
3755    dest_offset: u64,
3756    size: u64,
3757    interpreter: &mut Interpreter,
3758    ranges: &[Range<u64>],
3759) {
3760    let revert_string = format!(
3761        "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
3762        dest_offset,
3763        size,
3764        ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
3765    );
3766
3767    interpreter.bytecode.set_action(InterpreterAction::new_return(
3768        InstructionResult::Revert,
3769        Bytes::from(revert_string.into_bytes()),
3770        interpreter.gas,
3771    ));
3772}
3773
3774/// Returns true if the kind of account access is a call.
3775const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
3776    matches!(
3777        kind,
3778        crate::Vm::AccountAccessKind::Call
3779            | crate::Vm::AccountAccessKind::StaticCall
3780            | crate::Vm::AccountAccessKind::CallCode
3781            | crate::Vm::AccountAccessKind::DelegateCall
3782    )
3783}
3784
3785/// Records a log into the recorded logs vector, if it exists.
3786fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
3787    if let Some(storage_recorded_logs) = recorded_logs {
3788        storage_recorded_logs.push(Vm::Log {
3789            topics: log.data.topics().to_vec(),
3790            data: log.data.data.clone(),
3791            emitter: log.address,
3792        });
3793    }
3794}
3795
3796/// Returns the [`spec::Cheatcode`] definition for a given [`spec::CheatcodeDef`] implementor.
3797const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
3798    T::CHEATCODE
3799}
3800
3801fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
3802    cheatcode_signature(cheat).split('(').next().unwrap()
3803}
3804
3805const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
3806    cheat.func.id
3807}
3808
3809const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
3810    cheat.func.signature
3811}
3812
3813/// Dispatches the cheatcode call to the appropriate function.
3814fn apply_dispatch<FEN: FoundryEvmNetwork>(
3815    calls: &Vm::VmCalls,
3816    ccx: &mut CheatsCtxt<'_, '_, FEN>,
3817    executor: &mut dyn CheatcodesExecutor<FEN>,
3818) -> Result {
3819    // Extract metadata for logging/deprecation via CheatcodeDef.
3820    macro_rules! get_cheatcode {
3821        ($($variant:ident),*) => {
3822            match calls {
3823                $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
3824            }
3825        };
3826    }
3827    let cheat = vm_calls!(get_cheatcode);
3828
3829    let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
3830    trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
3831
3832    if let spec::Status::Deprecated(replacement) = cheat.status {
3833        ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
3834    }
3835
3836    // Monomorphized dispatch: calls apply_full directly, no trait objects.
3837    macro_rules! dispatch {
3838        ($($variant:ident),*) => {
3839            match calls {
3840                $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
3841            }
3842        };
3843    }
3844    let mut result = if ccx.state.config.blocked_cheatcodes.contains(&cheat.func.selector_bytes) {
3845        Err(fmt_err!("disabled during restricted execution"))
3846    } else {
3847        vm_calls!(dispatch)
3848    };
3849
3850    // Format the error message to include the cheatcode name.
3851    if let Err(e) = &mut result
3852        && e.is_str()
3853    {
3854        let name = cheatcode_name(cheat);
3855        // Skip showing the cheatcode name for:
3856        // - assertions: too verbose, and can already be inferred from the error message
3857        // - `rpcUrl`: forge-std relies on it in `getChainWithUpdatedRpcUrl`
3858        if !name.contains("assert") && name != "rpcUrl" {
3859            *e = fmt_err!("vm.{name}: {e}");
3860        }
3861    }
3862
3863    trace!(
3864        target: "cheatcodes",
3865        return = %match &result {
3866            Ok(b) => hex::encode(b),
3867            Err(e) => e.to_string(),
3868        }
3869    );
3870
3871    result
3872}
3873
3874/// Increments the nonce of every authority whose authorization would be applied on-chain.
3875///
3876/// Mirrors EIP-7702 processing: authorizations are checked in order after the transaction has
3877/// incremented the sender nonce, and invalid authorizations are skipped without changing the
3878/// authority nonce.
3879fn apply_authorization_nonces<FEN: FoundryEvmNetwork>(
3880    ecx: &mut FoundryContextFor<'_, FEN>,
3881    authorizations: &[SignedAuthorization],
3882    sender: Address,
3883    chain_id: u64,
3884) -> Result<()> {
3885    for auth in authorizations {
3886        if (!auth.chain_id.is_zero() && auth.chain_id != U256::from(chain_id))
3887            || auth.nonce() == u64::MAX
3888        {
3889            continue;
3890        }
3891        let Ok(authority) = auth.recover_authority() else { continue };
3892        // The authority code check is skipped because attaching the delegation already replaced
3893        // the local code that EIP-7702 validates.
3894        let account = journaled_account(ecx, authority)?;
3895        // The sender nonce has not been incremented for the transaction yet.
3896        if auth.nonce() == account.info.nonce + u64::from(authority == sender) {
3897            account.info.nonce += 1;
3898        }
3899    }
3900    Ok(())
3901}
3902
3903/// Helper function to check if frame execution will exit.
3904const fn will_exit(action: &InterpreterAction) -> bool {
3905    match action {
3906        InterpreterAction::Return(result) => {
3907            result.result.is_ok_or_revert() || result.result.is_halt()
3908        }
3909        _ => false,
3910    }
3911}
3912
3913#[cfg(test)]
3914mod tests {
3915    use super::*;
3916    use env_overrides::EnvOverrides;
3917
3918    fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
3919        let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
3920        let mut cheats = Cheatcodes::new(Arc::new(config));
3921        cheats.broadcast = broadcast;
3922        cheats
3923    }
3924
3925    fn create_inputs() -> CreateInputs {
3926        CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
3927    }
3928
3929    fn broadcast_at(depth: usize) -> Broadcast {
3930        Broadcast { depth, ..Default::default() }
3931    }
3932
3933    #[test]
3934    fn flag_off_with_broadcast_returns_false() {
3935        let mut cheats = cheats(false, Some(broadcast_at(1)));
3936        assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3937    }
3938
3939    #[test]
3940    fn flag_on_without_broadcast_returns_false() {
3941        let mut cheats = cheats(true, None);
3942        assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3943    }
3944
3945    #[test]
3946    fn flag_on_with_broadcast_depth_mismatch_returns_false() {
3947        let mut cheats = cheats(true, Some(broadcast_at(2)));
3948        assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3949    }
3950
3951    #[test]
3952    fn flag_on_with_broadcast_depth_match_returns_true() {
3953        let mut cheats = cheats(true, Some(broadcast_at(1)));
3954        assert!(cheats.should_use_create2_factory(1, &create_inputs()));
3955    }
3956
3957    #[test]
3958    fn default_cheatcodes_have_no_opcode_hooks() {
3959        let cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3960        assert!(!cheats.has_step_hooks());
3961        assert!(!cheats.has_step_end_hooks());
3962        assert!(!cheats.has_log_hooks());
3963    }
3964
3965    #[test]
3966    fn active_cheatcode_state_enables_opcode_hooks() {
3967        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3968
3969        cheats.recording_accesses = true;
3970        assert!(cheats.has_step_hooks());
3971        assert!(!cheats.has_step_end_hooks());
3972        assert!(cheats.has_recording_accesses_only_step_hook());
3973
3974        cheats.recording_accesses = false;
3975        cheats.gas_metering.touched = true;
3976        assert!(!cheats.has_step_hooks());
3977        assert!(cheats.has_step_end_hooks());
3978        assert!(!cheats.has_recording_accesses_only_step_hook());
3979
3980        cheats.gas_metering.touched = false;
3981        cheats.register_storage_load_hook(Address::ZERO, Address::ZERO, [0; 4]);
3982        assert!(cheats.has_step_hooks());
3983        assert!(cheats.has_step_end_hooks());
3984        assert!(!cheats.has_recording_accesses_only_step_hook());
3985    }
3986
3987    #[test]
3988    fn mixed_step_hooks_disable_record_access_fast_path() {
3989        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3990        cheats.recording_accesses = true;
3991
3992        cheats.gas_metering.reset = true;
3993        assert!(!cheats.has_recording_accesses_only_step_hook());
3994
3995        cheats.gas_metering.reset = false;
3996        cheats.env_overrides.update(None, |o| o.basefee = Some(1));
3997        assert!(!cheats.has_recording_accesses_only_step_hook());
3998    }
3999
4000    #[test]
4001    fn env_override_hook_predicates() {
4002        fn assert_hooks(cheats: &Cheatcodes, active: bool, case: &str) {
4003            assert_eq!(cheats.has_step_hooks(), active, "step hooks: {case}");
4004            assert_eq!(cheats.has_step_end_hooks(), active, "step_end hooks: {case}");
4005        }
4006
4007        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4008        assert_hooks(&cheats, false, "empty map");
4009        let snapshot_id = U256::from(7);
4010        cheats.env_overrides.save_snapshot(snapshot_id, None, 0, 0, &[]);
4011
4012        cheats.env_overrides.update(None, |_| {});
4013        assert_hooks(&cheats, false, "inactive entry");
4014
4015        let active = [
4016            EnvOverrides { basefee: Some(1), ..Default::default() },
4017            EnvOverrides { implicit_basefee: Some(1), ..Default::default() },
4018            EnvOverrides { gas_price: Some(1), ..Default::default() },
4019            EnvOverrides { blob_hashes: Some(vec![B256::ZERO]), ..Default::default() },
4020        ];
4021        for overrides in active {
4022            let case = format!("{overrides:?}");
4023            cheats.env_overrides.update(None, |o| *o = overrides);
4024            assert_hooks(&cheats, true, &case);
4025        }
4026
4027        // Overrides on a fork that isn't active still enable the hooks.
4028        cheats.env_overrides.restore_snapshot(snapshot_id, false);
4029        cheats.env_overrides.update(Some(U256::from(1)), |o| o.basefee = Some(1));
4030        assert_hooks(&cheats, true, "override on another fork");
4031
4032        // Restoring a snapshot taken without overrides turns the hooks off.
4033        cheats.env_overrides.restore_snapshot(snapshot_id, false);
4034        assert_hooks(&cheats, false, "after restoring an empty snapshot");
4035    }
4036
4037    #[test]
4038    fn active_log_state_enables_log_hooks() {
4039        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
4040
4041        cheats.recorded_logs = Some(Default::default());
4042        assert!(cheats.has_log_hooks());
4043
4044        cheats.recorded_logs = None;
4045        cheats.expected_emits.push_back((
4046            crate::expected_emit::ExpectedEmit {
4047                depth: 0,
4048                log: None,
4049                checks: [false; 5],
4050                address: None,
4051                anonymous: false,
4052                found: false,
4053                count: 1,
4054                mismatch_error: None,
4055            },
4056            Default::default(),
4057        ));
4058        assert!(cheats.has_log_hooks());
4059    }
4060
4061    #[test]
4062    fn frame_gas_reports_settled_components() {
4063        for mut gas in [Gas::new(100_000), Gas::new_with_regular_gas_and_reservoir(100_000, 50_000)]
4064        {
4065            assert!(gas.record_regular_cost(1_000));
4066            assert!(gas.record_state_cost(20_000));
4067
4068            let mut result = InterpreterResult::new(InstructionResult::Stop, Bytes::new(), gas);
4069            let reported = frame_gas(&result);
4070            assert_eq!(reported.gasTotalUsed, 1_000);
4071            assert_eq!(reported.gasStateUsed, 20_000);
4072
4073            result.result = InstructionResult::Revert;
4074            assert_eq!(frame_gas(&result).gasStateUsed, 0);
4075        }
4076
4077        let mut gas = Gas::new(100_000);
4078        gas.refill_reservoir(20_000);
4079        let result = InterpreterResult::new(InstructionResult::Stop, Bytes::new(), gas);
4080        assert_eq!(frame_gas(&result).gasStateUsed, -20_000);
4081
4082        let mut gas = Gas::new(100_000);
4083        assert!(gas.record_state_cost(20_000));
4084        gas.spend_all();
4085        let result = InterpreterResult::new(InstructionResult::OutOfGas, Bytes::new(), gas);
4086        let reported = frame_gas(&result);
4087        assert_eq!(reported.gasTotalUsed, 100_000);
4088        assert_eq!(reported.gasStateUsed, 0);
4089    }
4090
4091    #[test]
4092    fn arbitrary_storage_cache_value_routes_copied_targets_to_source() {
4093        let mut storage = ArbitraryStorage::default();
4094        let source = Address::repeat_byte(0x11);
4095        let copied = Address::repeat_byte(0x22);
4096        let slot = U256::from(7);
4097
4098        storage.mark_arbitrary(&source, false);
4099        storage.mark_copy(&source, &copied);
4100        storage.cache_value(copied, slot, U256::ZERO);
4101
4102        assert_eq!(storage.cached_value(source, slot), Some(U256::ZERO));
4103    }
4104}