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, journaled_account,
8        mock::{MockCallDataContext, MockCallReturnData},
9        prank::Prank,
10    },
11    inspector::utils::CommonCreateInput,
12    script::{Broadcast, Wallets},
13    test::{
14        assume::AssumeNoRevert,
15        expect::{
16            self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedCreate,
17            ExpectedEmitTracker, ExpectedRevert, ExpectedRevertKind,
18        },
19        revert_handlers,
20    },
21    utils::IgnoredTraces,
22};
23use alloy_consensus::BlobTransactionSidecarVariant;
24use alloy_network::{Ethereum, Network, TransactionBuilder};
25use alloy_primitives::{
26    Address, B256, Bytes, Log, TxKind, U256, hex,
27    map::{AddressHashMap, HashMap, HashSet},
28};
29use alloy_rpc_types::AccessList;
30use alloy_signer_local::PrivateKeySigner;
31use alloy_sol_types::{SolCall, SolInterface, SolValue};
32use foundry_common::{
33    FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
34    mapping_slots::{MappingSlots, step as mapping_step},
35};
36use foundry_evm_core::{
37    Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
38    abi::Vm::stopExpectSafeMemoryCall,
39    backend::{DatabaseError, DatabaseExt, LocalForkId, RevertDiagnostic},
40    constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
41    env::FoundryContextExt,
42    evm::{
43        BlockEnvFor, EthEvmNetwork, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork,
44        NestedEvmClosure, SpecFor, TransactionRequestFor, TxEnvFor, with_cloned_context,
45    },
46};
47use foundry_evm_traces::{
48    TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
49};
50use foundry_wallets::wallet_multi::MultiWallet;
51use itertools::Itertools;
52use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
53use rand::Rng;
54use revm::{
55    Inspector,
56    bytecode::opcode as op,
57    context::{Cfg, ContextTr, Host, JournalTr, Transaction, TransactionType, result::EVMError},
58    context_interface::{CreateScheme, transaction::SignedAuthorization},
59    handler::FrameResult,
60    interpreter::{
61        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
62        InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
63        interpreter_types::{Jumps, LoopControl, MemoryTr},
64        return_ok,
65    },
66};
67use serde_json::Value;
68use std::{
69    cmp::max,
70    collections::{BTreeMap, VecDeque},
71    fmt::Debug,
72    fs::File,
73    io::BufReader,
74    ops::Range,
75    path::PathBuf,
76    sync::{Arc, OnceLock},
77};
78
79mod utils;
80
81pub mod analysis;
82pub use analysis::CheatcodeAnalysis;
83
84/// Helper trait for running nested EVM operations from inside cheatcode implementations.
85pub trait CheatcodesExecutor<FEN: FoundryEvmNetwork> {
86    /// Runs a closure with a nested EVM built from the current context.
87    /// The inspector is assembled internally — never exposed to the caller.
88    fn with_nested_evm(
89        &mut self,
90        cheats: &mut Cheatcodes<FEN>,
91        ecx: &mut FoundryContextFor<'_, FEN>,
92        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
93    ) -> Result<(), EVMError<DatabaseError>>;
94
95    /// Replays a historical transaction on the database. Inspector is assembled internally.
96    fn transact_on_db(
97        &mut self,
98        cheats: &mut Cheatcodes<FEN>,
99        ecx: &mut FoundryContextFor<'_, FEN>,
100        fork_id: Option<U256>,
101        transaction: B256,
102    ) -> eyre::Result<()>;
103
104    /// Executes a `TransactionRequest` on the database. Inspector is assembled internally.
105    fn transact_from_tx_on_db(
106        &mut self,
107        cheats: &mut Cheatcodes<FEN>,
108        ecx: &mut FoundryContextFor<'_, FEN>,
109        tx: TxEnvFor<FEN>,
110    ) -> eyre::Result<()>;
111
112    /// Runs a closure with a fresh nested EVM built from a raw database and environment.
113    /// Unlike `with_nested_evm`, this does NOT clone from `ecx` and does NOT write back.
114    /// The caller is responsible for state merging. Used by `executeTransactionCall`.
115    /// Returns the final EVM environment after the closure runs (consumed without cloning).
116    #[allow(clippy::type_complexity)]
117    fn with_fresh_nested_evm(
118        &mut self,
119        cheats: &mut Cheatcodes<FEN>,
120        db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
121        evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
122        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
123    ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>>;
124
125    /// Simulates `console.log` invocation.
126    fn console_log(&mut self, msg: &str);
127
128    /// Returns a mutable reference to the tracing inspector if it is available.
129    fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
130        None
131    }
132
133    /// Marks that the next EVM frame is an "inner context" so that isolation mode does not
134    /// trigger a nested `transact_inner`. `original_origin` is stored for the existing
135    /// inner-context adjustment logic that restores `tx.origin`.
136    fn set_in_inner_context(&mut self, _enabled: bool, _original_origin: Option<Address>) {}
137}
138
139/// Builds a sub-EVM from the current context and executes the given CREATE frame.
140pub(crate) fn exec_create<FEN: FoundryEvmNetwork>(
141    executor: &mut dyn CheatcodesExecutor<FEN>,
142    inputs: CreateInputs,
143    ccx: &mut CheatsCtxt<'_, '_, FEN>,
144) -> std::result::Result<CreateOutcome, EVMError<DatabaseError>> {
145    let mut inputs = Some(inputs);
146    let mut outcome = None;
147    executor.with_nested_evm(ccx.state, ccx.ecx, &mut |evm| {
148        let inputs = inputs.take().unwrap();
149        evm.journal_inner_mut().depth += 1;
150
151        let frame = FrameInput::Create(Box::new(inputs));
152
153        let result = match evm.run_execution(frame)? {
154            FrameResult::Call(_) => unreachable!(),
155            FrameResult::Create(create) => create,
156        };
157
158        evm.journal_inner_mut().depth -= 1;
159
160        outcome = Some(result);
161        Ok(())
162    })?;
163    Ok(outcome.unwrap())
164}
165
166/// Basic implementation of [CheatcodesExecutor] that simply returns the [Cheatcodes] instance as an
167/// inspector.
168#[derive(Debug, Default, Clone, Copy)]
169struct TransparentCheatcodesExecutor;
170
171impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for TransparentCheatcodesExecutor {
172    fn with_nested_evm(
173        &mut self,
174        cheats: &mut Cheatcodes<FEN>,
175        ecx: &mut FoundryContextFor<'_, FEN>,
176        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
177    ) -> Result<(), EVMError<DatabaseError>> {
178        with_cloned_context(ecx, |db, evm_env, journal_inner| {
179            let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
180            *evm.journal_inner_mut() = journal_inner;
181            f(&mut *evm)?;
182            let sub_inner = evm.journal_inner_mut().clone();
183            let sub_evm_env = evm.to_evm_env();
184            Ok((sub_evm_env, sub_inner))
185        })
186    }
187
188    fn with_fresh_nested_evm(
189        &mut self,
190        cheats: &mut Cheatcodes<FEN>,
191        db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
192        evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
193        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
194    ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
195        let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, cheats);
196        f(&mut *evm)?;
197        Ok(evm.to_evm_env())
198    }
199
200    fn transact_on_db(
201        &mut self,
202        cheats: &mut Cheatcodes<FEN>,
203        ecx: &mut FoundryContextFor<'_, FEN>,
204        fork_id: Option<U256>,
205        transaction: B256,
206    ) -> eyre::Result<()> {
207        let evm_env = ecx.evm_clone();
208        let (db, inner) = ecx.db_journal_inner_mut();
209        db.transact(fork_id, transaction, evm_env, inner, cheats)
210    }
211
212    fn transact_from_tx_on_db(
213        &mut self,
214        cheats: &mut Cheatcodes<FEN>,
215        ecx: &mut FoundryContextFor<'_, FEN>,
216        tx: TxEnvFor<FEN>,
217    ) -> eyre::Result<()> {
218        let evm_env = ecx.evm_clone();
219        let (db, inner) = ecx.db_journal_inner_mut();
220        db.transact_from_tx(tx, evm_env, inner, cheats)
221    }
222
223    fn console_log(&mut self, _msg: &str) {}
224}
225
226macro_rules! try_or_return {
227    ($e:expr) => {
228        match $e {
229            Ok(v) => v,
230            Err(_) => return,
231        }
232    };
233}
234
235/// Contains additional, test specific resources that should be kept for the duration of the test
236#[derive(Debug, Default)]
237pub struct TestContext {
238    /// Buffered readers for files opened for reading (path => BufReader mapping)
239    pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
240}
241
242/// Every time we clone `Context`, we want it to be empty
243impl Clone for TestContext {
244    fn clone(&self) -> Self {
245        Default::default()
246    }
247}
248
249impl TestContext {
250    /// Clears the context.
251    pub fn clear(&mut self) {
252        self.opened_read_files.clear();
253    }
254}
255
256/// Helps collecting transactions from different forks.
257#[derive(Clone, Debug)]
258pub struct BroadcastableTransaction<N: Network = Ethereum> {
259    /// The optional RPC URL.
260    pub rpc: Option<String>,
261    /// The transaction to broadcast.
262    pub transaction: TransactionMaybeSigned<N>,
263}
264
265#[derive(Clone, Debug, Copy)]
266pub struct RecordDebugStepInfo {
267    /// The debug trace node index when the recording starts.
268    pub start_node_idx: usize,
269    /// The original tracer config when the recording starts.
270    pub original_tracer_config: TracingInspectorConfig,
271}
272
273/// Environment overrides applied at the opcode level.
274///
275/// In isolation mode (and inside the synthetic transactions used by
276/// `--gas-report` / `--isolate`) the transaction environment is zeroed for
277/// fee-accounting purposes, so cheatcodes that mutate the env (e.g.
278/// `vm.fee`, `vm.txGasPrice`, `vm.blobhashes`) cannot rely on those
279/// mutations being visible to contracts via the `BASEFEE`, `GASPRICE` and
280/// `BLOBHASH` opcodes. These overrides are applied in `step_end` to fix
281/// the value that was just pushed onto the stack.
282///
283/// # Semantics when invoked from inside the synthetic isolation transaction
284///
285/// `vm.fee` / `vm.txGasPrice` / `vm.blobhashes` consult
286/// [`Cheatcodes::in_isolation_context`]; when set, they only update these
287/// overrides (so `tx.gas_price = 0` continues to apply to fee accounting and
288/// EIP-4844 inner-tx validation does not reject the synthetic call) and
289/// leave the real env untouched. After the inner transaction returns, the
290/// outer env is restored from the cached snapshot taken before
291/// `transact_inner`, which means:
292///
293/// - the override **does** persist for subsequent `BASEFEE`, `GASPRICE` and `BLOBHASH` reads (this
294///   hook fires in `step_end` regardless of isolation),
295/// - `vm.getBlobhashes()` also consults these overrides, so it returns the correct value.
296/// - but the real `block.basefee` / `tx.gas_price` / `tx.blob_hashes` do **not** reflect the
297///   cheatcode value, so other non-opcode env consumers will not see it.
298///
299/// Calling these cheatcodes outside isolation behaves as before (real env
300/// is also mutated and the override mirrors it).
301#[derive(Clone, Debug, Default)]
302pub struct EnvOverrides {
303    /// Override for the `BASEFEE` opcode (set via `vm.fee`).
304    pub basefee: Option<u64>,
305    /// Override for the `GASPRICE` opcode (set via `vm.txGasPrice`).
306    pub gas_price: Option<u128>,
307    /// Override for the `BLOBHASH` opcode (set via `vm.blobhashes`).
308    pub blob_hashes: Option<Vec<B256>>,
309    /// `tx.gas_price` captured at snapshot time when no gas_price override was
310    /// active. `sync_tx_after_env_override_restore` uses this to restore the
311    /// real pre-override value (not hardcoded 0) on revert.
312    pub pre_override_gas_price: Option<u128>,
313    /// `tx.tx_type` captured at snapshot time when no blob_hashes override was
314    /// active. Prevents tx_type being stuck at EIP4844 after reverting from a
315    /// blobhashes-set state.
316    pub pre_override_tx_type: Option<u8>,
317    /// `tx.blob_hashes` captured at snapshot time when no blob_hashes override
318    /// was active.
319    pub pre_override_blob_hashes: Option<Vec<B256>>,
320    /// The opcode about to run (captured in `step`, consumed in `step_end`),
321    /// used to know what was just executed when `step_end` fires — at that
322    /// point `interpreter.bytecode.opcode()` already points at the *next*
323    /// instruction.
324    pending_opcode: Option<u8>,
325    /// Pending index for the `BLOBHASH` opcode, captured in `step` (where
326    /// the index is still on top of the stack) for use in `step_end` (after
327    /// the opcode has consumed it and pushed the looked-up hash).
328    pending_blobhash_index: Option<u64>,
329}
330
331impl EnvOverrides {
332    /// Whether any override is set.
333    #[inline]
334    pub const fn is_any_set(&self) -> bool {
335        self.basefee.is_some() || self.gas_price.is_some() || self.blob_hashes.is_some()
336    }
337}
338
339/// Holds gas metering state.
340#[derive(Clone, Debug, Default)]
341pub struct GasMetering {
342    /// True if gas metering is paused.
343    pub paused: bool,
344    /// True if gas metering was resumed or reset during the test.
345    /// Used to reconcile gas when frame ends (if spent less than refunded).
346    pub touched: bool,
347    /// True if gas metering should be reset to frame limit.
348    pub reset: bool,
349    /// Stores paused gas frames.
350    pub paused_frames: Vec<Gas>,
351
352    /// The group and name of the active snapshot.
353    pub active_gas_snapshot: Option<(String, String)>,
354
355    /// Cache of the amount of gas used in previous call.
356    /// This is used by the `lastCallGas` cheatcode.
357    pub last_call_gas: Option<crate::Vm::Gas>,
358
359    /// Cache of the amount of gas used in previous call or create frame.
360    /// This is used by the `lastFrameGas` cheatcode.
361    pub last_frame_gas: Option<crate::Vm::Gas>,
362
363    /// True if gas recording is enabled.
364    pub recording: bool,
365    /// The gas used in the last frame.
366    pub last_gas_used: u64,
367    /// Gas records for the active snapshots.
368    pub gas_records: Vec<GasRecord>,
369}
370
371impl GasMetering {
372    /// Start the gas recording.
373    pub const fn start(&mut self) {
374        self.recording = true;
375    }
376
377    /// Stop the gas recording.
378    pub const fn stop(&mut self) {
379        self.recording = false;
380    }
381
382    /// Resume paused gas metering.
383    pub fn resume(&mut self) {
384        if self.paused {
385            self.paused = false;
386            self.touched = true;
387        }
388        self.paused_frames.clear();
389    }
390
391    /// Reset gas to limit.
392    pub fn reset(&mut self) {
393        self.paused = false;
394        self.touched = true;
395        self.reset = true;
396        self.paused_frames.clear();
397    }
398}
399
400/// Holds data about arbitrary storage.
401#[derive(Clone, Debug, Default)]
402pub struct ArbitraryStorage {
403    /// Mapping of arbitrary storage addresses to generated values (slot, arbitrary value).
404    /// (SLOADs return random value if storage slot wasn't accessed).
405    /// Changed values are recorded and used to copy storage to different addresses.
406    values: HashMap<Address, HashMap<U256, U256>>,
407    /// Mapping of address with storage copied to arbitrary storage address source.
408    copies: HashMap<Address, Address>,
409    /// Address with storage slots that should be overwritten even if previously set.
410    overwrites: HashSet<Address>,
411}
412
413impl ArbitraryStorage {
414    /// Marks an address with arbitrary storage.
415    pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
416        self.values.insert(*address, HashMap::default());
417        if overwrite {
418            self.overwrites.insert(*address);
419        } else {
420            self.overwrites.remove(address);
421        }
422    }
423
424    /// Maps an address that copies storage with the arbitrary storage address.
425    pub fn mark_copy(&mut self, from: &Address, to: &Address) {
426        if self.values.contains_key(from) {
427            self.copies.insert(*to, *from);
428        }
429    }
430
431    /// Returns addresses explicitly marked with arbitrary storage.
432    fn targets(&self) -> impl Iterator<Item = Address> + '_ {
433        self.values.keys().copied()
434    }
435
436    /// Returns addresses explicitly marked with arbitrary storage and whether nonzero slots are
437    /// overwritten.
438    fn target_overwrite_modes(&self) -> impl Iterator<Item = (Address, bool)> + '_ {
439        self.values.keys().map(|address| (*address, self.overwrites.contains(address)))
440    }
441
442    /// Returns addresses that copy storage from arbitrary-storage targets.
443    fn copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
444        self.copies.keys().copied()
445    }
446
447    /// Returns copied arbitrary-storage targets and their source address.
448    fn copied_target_sources(&self) -> impl Iterator<Item = (Address, Address)> + '_ {
449        self.copies.iter().map(|(target, source)| (*target, *source))
450    }
451
452    /// Caches a concrete value for a slot on an arbitrary-storage address or copied target.
453    fn cache_value(&mut self, address: Address, slot: U256, data: U256) {
454        if let Some(values) = self.values.get_mut(&address) {
455            values.insert(slot, data);
456            return;
457        }
458
459        let Some(source) = self.copies.get(&address).copied() else {
460            return;
461        };
462        if let Some(values) = self.values.get_mut(&source) {
463            values.insert(slot, data);
464        }
465    }
466
467    /// Returns a cached arbitrary value for a slot.
468    fn cached_value(&self, address: Address, slot: U256) -> Option<U256> {
469        self.values.get(&address).and_then(|values| values.get(&slot)).copied()
470    }
471
472    /// Saves arbitrary storage value for a given address:
473    /// - store value in changed values cache.
474    /// - update account's storage with given value.
475    pub fn save<CTX: ContextTr>(
476        &mut self,
477        ecx: &mut CTX,
478        address: Address,
479        slot: U256,
480        data: U256,
481    ) {
482        self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
483        if ecx.journal_mut().load_account(address).is_ok() {
484            ecx.journal_mut()
485                .sstore(address, slot, data)
486                .expect("could not set arbitrary storage value");
487        }
488    }
489
490    /// Copies arbitrary storage value from source address to the given target address:
491    /// - if a value is present in arbitrary values cache, then update target storage and return
492    ///   existing value.
493    /// - if no value was yet generated for given slot, then save new value in cache and update both
494    ///   source and target storages.
495    pub fn copy<CTX: ContextTr>(
496        &mut self,
497        ecx: &mut CTX,
498        target: Address,
499        slot: U256,
500        new_value: U256,
501    ) -> U256 {
502        let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
503        let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
504        let value = match storage_cache.get(&slot) {
505            Some(value) => *value,
506            None => {
507                storage_cache.insert(slot, new_value);
508                // Update source storage with new value.
509                if ecx.journal_mut().load_account(*source).is_ok() {
510                    ecx.journal_mut()
511                        .sstore(*source, slot, new_value)
512                        .expect("could not copy arbitrary storage value");
513                }
514                new_value
515            }
516        };
517        // Update target storage with new value.
518        if ecx.journal_mut().load_account(target).is_ok() {
519            ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
520        }
521        value
522    }
523}
524
525/// List of transactions that can be broadcasted.
526pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
527
528/// An EVM inspector that handles calls to various cheatcodes, each with their own behavior.
529///
530/// Cheatcodes can be called by contracts during execution to modify the VM environment, such as
531/// mocking addresses, signatures and altering call reverts.
532///
533/// Executing cheatcodes can be very powerful. Most cheatcodes are limited to evm internals, but
534/// there are also cheatcodes like `ffi` which can execute arbitrary commands or `writeFile` and
535/// `readFile` which can manipulate files of the filesystem. Therefore, several restrictions are
536/// implemented for these cheatcodes:
537/// - `ffi`, and file cheatcodes are _always_ opt-in (via foundry config) and never enabled by
538///   default: all respective cheatcode handlers implement the appropriate checks
539/// - File cheatcodes require explicit permissions which paths are allowed for which operation, see
540///   `Config.fs_permission`
541/// - Only permitted accounts are allowed to execute cheatcodes in forking mode, this ensures no
542///   contract deployed on the live network is able to execute cheatcodes by simply calling the
543///   cheatcode address: by default, the caller, test contract and newly deployed contracts are
544///   allowed to execute cheatcodes
545#[derive(Clone, Debug)]
546pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
547    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities
548    pub analysis: Option<CheatcodeAnalysis>,
549
550    /// The block environment
551    ///
552    /// Used in the cheatcode handler to overwrite the block environment separately from the
553    /// execution block environment.
554    pub block: Option<BlockEnvFor<FEN>>,
555
556    /// Currently active EIP-7702 delegations that will be consumed when building the next
557    /// transaction. Set by `vm.attachDelegation()` and consumed via `.take()` during
558    /// transaction construction.
559    pub active_delegations: Vec<SignedAuthorization>,
560
561    /// The active EIP-4844 blob that will be attached to the next call.
562    pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
563
564    /// The gas price.
565    ///
566    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
567    /// in the execution environment.
568    pub gas_price: Option<u128>,
569
570    /// Address labels
571    pub labels: AddressHashMap<String>,
572
573    /// Prank information, mapped to the call depth where pranks were added.
574    pub pranks: BTreeMap<usize, Prank>,
575
576    /// Expected revert information
577    pub expected_revert: Option<ExpectedRevert>,
578
579    /// Assume next call can revert and discard fuzz run if it does.
580    pub assume_no_revert: Option<AssumeNoRevert>,
581
582    /// Additional diagnostic for reverts
583    pub fork_revert_diagnostic: Option<RevertDiagnostic>,
584
585    /// Recorded storage reads and writes
586    pub accesses: RecordAccess,
587
588    /// Whether storage access recording is currently active
589    pub recording_accesses: bool,
590
591    /// Recorded account accesses (calls, creates) organized by relative call depth, where the
592    /// topmost vector corresponds to accesses at the depth at which account access recording
593    /// began. Each vector in the matrix represents a list of accesses at a specific call
594    /// depth. Once that call context has ended, the last vector is removed from the matrix and
595    /// merged into the previous vector.
596    pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
597
598    /// The information of the debug step recording.
599    pub record_debug_steps_info: Option<RecordDebugStepInfo>,
600
601    /// Recorded logs
602    pub recorded_logs: Option<Vec<crate::Vm::Log>>,
603
604    /// Mocked calls
605    // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext`
606    pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
607
608    /// Mocked functions. Maps target address to be mocked to pair of (calldata, mock address).
609    pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
610
611    /// Expected calls
612    pub expected_calls: ExpectedCallTracker,
613    /// Expected emits
614    pub expected_emits: ExpectedEmitTracker,
615    /// Expected creates
616    pub expected_creates: Vec<ExpectedCreate>,
617
618    /// Map of context depths to memory offset ranges that may be written to within the call depth.
619    pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
620
621    /// Current broadcasting information
622    pub broadcast: Option<Broadcast>,
623
624    /// Scripting based transactions
625    pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
626
627    /// Current EIP-2930 access lists.
628    pub access_list: Option<AccessList>,
629
630    /// Additional, user configurable context this Inspector has access to when inspecting a call.
631    pub config: Arc<CheatsConfig>,
632
633    /// Test-scoped context holding data that needs to be reset every test run
634    pub test_context: TestContext,
635
636    /// Whether to commit FS changes such as file creations, writes and deletes.
637    /// Used to prevent duplicate changes file executing non-committing calls.
638    pub fs_commit: bool,
639
640    /// Serialized JSON values.
641    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
642    pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
643
644    /// All recorded ETH `deal`s.
645    pub eth_deals: Vec<DealRecord>,
646
647    /// Gas metering state.
648    pub gas_metering: GasMetering,
649
650    /// Contains gas snapshots made over the course of a test suite.
651    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
652    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
653
654    /// Mapping slots.
655    pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
656
657    /// The current program counter.
658    pub pc: usize,
659    /// Breakpoints supplied by the `breakpoint` cheatcode.
660    /// `char -> (address, pc)`
661    pub breakpoints: Breakpoints,
662
663    /// Whether the next contract creation should be intercepted to return its initcode.
664    pub intercept_next_create_call: bool,
665
666    /// Optional cheatcodes `TestRunner`. Used for generating random values from uint and int
667    /// strategies.
668    test_runner: Option<TestRunner>,
669
670    /// Ignored traces.
671    pub ignored_traces: IgnoredTraces,
672
673    /// Addresses with arbitrary storage.
674    pub arbitrary_storage: Option<ArbitraryStorage>,
675
676    /// Deprecated cheatcodes mapped to the reason. Used to report warnings on test results.
677    pub deprecated: HashMap<&'static str, Option<&'static str>>,
678    /// Unlocked wallets used in scripts and testing of scripts.
679    pub wallets: Option<Wallets>,
680    /// Parsed secp256k1 private-key signers for repeated `vm.addr` / `vm.sign` calls.
681    pub private_key_signers: HashMap<U256, PrivateKeySigner>,
682    /// Signatures identifier for decoding events and functions
683    signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
684    /// Used to determine whether the broadcasted call has dynamic gas limit.
685    pub dynamic_gas_limit: bool,
686    // Custom execution evm version.
687    pub execution_evm_version: Option<SpecFor<FEN>>,
688
689    /// Opcode-level environment overrides for `BASEFEE`, `GASPRICE` and
690    /// `BLOBHASH`. Set by `vm.fee`, `vm.txGasPrice`, `vm.blobhashes` and
691    /// applied in [`Inspector::step_end`].
692    ///
693    /// Needed because in isolation mode the synthetic inner transaction
694    /// zeroes the corresponding tx/block env fields for fee-accounting.
695    ///
696    /// Keyed by active fork ID (`None` -> local) so that multi-fork tests do not bleed overrides
697    /// across forks when `vm.selectFork` / `vm.createSelectFork` switches the active fork.
698    pub env_overrides: HashMap<Option<LocalForkId>, EnvOverrides>,
699
700    /// Per-state-snapshot copies of [`Self::env_overrides`], captured by
701    /// `vm.snapshotState` and restored by `vm.revertToState[AndDelete]`.
702    ///
703    /// `env_overrides` lives on the cheatcode inspector rather than in
704    /// `EvmEnv`, so the backend's snapshot/revert mechanism does not see
705    /// it. Without this, an override set after a snapshot would survive a
706    /// `revertToState`, and the BASEFEE/GASPRICE/BLOBHASH opcodes (which
707    /// the override layer rewrites in `step_end`) would keep returning
708    /// the post-snapshot value even though `EvmEnv` was rolled back.
709    pub env_overrides_snapshots: HashMap<U256, HashMap<Option<LocalForkId>, EnvOverrides>>,
710
711    /// Whether we are currently executing inside an isolation context, i.e.
712    /// the synthetic inner transaction wrapped by
713    /// `InspectorStackRefMut::transact_inner` (used by `--gas-report` and
714    /// `--isolate`).
715    ///
716    /// Toggled by the inspector stack around the inner `transact_raw`
717    /// call. Cheatcodes that mutate the tx/block env consult this flag and
718    /// route the change through `EnvOverrides` instead of the actual env
719    /// when `true`, so they don't fight with the fee-accounting zeroing.
720    pub in_isolation_context: bool,
721}
722
723// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
724// `CheatsConfig` which is unused, and inside it `ProjectPathsConfig` is relatively expensive to
725// create.
726impl Default for Cheatcodes {
727    fn default() -> Self {
728        Self::new(Arc::default())
729    }
730}
731
732impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
733    /// Creates a new `Cheatcodes` with the given settings.
734    pub fn new(config: Arc<CheatsConfig>) -> Self {
735        Self {
736            analysis: None,
737            fs_commit: true,
738            labels: config.labels.clone(),
739            config,
740            block: Default::default(),
741            active_delegations: Default::default(),
742            active_blob_sidecar: Default::default(),
743            gas_price: Default::default(),
744            pranks: Default::default(),
745            expected_revert: Default::default(),
746            assume_no_revert: Default::default(),
747            fork_revert_diagnostic: Default::default(),
748            accesses: Default::default(),
749            recording_accesses: Default::default(),
750            recorded_account_diffs_stack: Default::default(),
751            recorded_logs: Default::default(),
752            record_debug_steps_info: Default::default(),
753            mocked_calls: Default::default(),
754            mocked_functions: Default::default(),
755            expected_calls: Default::default(),
756            expected_emits: Default::default(),
757            expected_creates: Default::default(),
758            allowed_mem_writes: Default::default(),
759            broadcast: Default::default(),
760            broadcastable_transactions: Default::default(),
761            access_list: Default::default(),
762            test_context: Default::default(),
763            serialized_jsons: Default::default(),
764            eth_deals: Default::default(),
765            gas_metering: Default::default(),
766            gas_snapshots: Default::default(),
767            mapping_slots: Default::default(),
768            pc: Default::default(),
769            breakpoints: Default::default(),
770            intercept_next_create_call: Default::default(),
771            test_runner: Default::default(),
772            ignored_traces: Default::default(),
773            arbitrary_storage: Default::default(),
774            deprecated: Default::default(),
775            wallets: Default::default(),
776            private_key_signers: Default::default(),
777            signatures_identifier: Default::default(),
778            dynamic_gas_limit: Default::default(),
779            execution_evm_version: None,
780            env_overrides: Default::default(),
781            env_overrides_snapshots: Default::default(),
782            in_isolation_context: false,
783        }
784    }
785
786    /// Enables cheatcode analysis capabilities by providing a solar compiler instance.
787    pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
788        self.analysis = Some(analysis);
789    }
790
791    /// Returns the env overrides for the given fork (`None` = no-fork / local).
792    pub fn env_overrides_for(&self, fork_id: Option<U256>) -> Option<&EnvOverrides> {
793        self.env_overrides.get(&fork_id).filter(|o| o.is_any_set())
794    }
795
796    /// Returns a mutable reference to the env overrides for the given fork, inserting a
797    /// default entry if absent.
798    pub fn env_overrides_for_mut(&mut self, fork_id: Option<U256>) -> &mut EnvOverrides {
799        self.env_overrides.entry(fork_id).or_default()
800    }
801
802    /// Returns the configured prank at given depth or the first prank configured at a lower depth.
803    /// For example, if pranks configured for depth 1, 3 and 5, the prank for depth 4 is the one
804    /// configured at depth 3.
805    pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
806        self.pranks.range(..=depth).last().map(|(_, prank)| prank)
807    }
808
809    /// Returns the configured wallets if available, else creates a new instance.
810    pub fn wallets(&mut self) -> &Wallets {
811        self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
812    }
813
814    /// Sets the unlocked wallets.
815    pub fn set_wallets(&mut self, wallets: Wallets) {
816        self.wallets = Some(wallets);
817    }
818
819    /// Adds a delegation to the active delegations list.
820    pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
821        self.active_delegations.push(authorization);
822    }
823
824    /// Returns the signatures identifier.
825    pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
826        self.signatures_identifier
827            .get_or_init(|| {
828                if let Some(artifacts) = &self.config.available_artifacts {
829                    return SignaturesIdentifier::new_offline_with_abis(
830                        artifacts.values().map(|contract| &contract.abi),
831                    )
832                    .ok();
833                }
834                SignaturesIdentifier::new(true).ok()
835            })
836            .as_ref()
837    }
838
839    /// Decodes the input data and applies the cheatcode.
840    fn apply_cheatcode(
841        &mut self,
842        ecx: &mut FoundryContextFor<'_, FEN>,
843        call: &CallInputs,
844        executor: &mut dyn CheatcodesExecutor<FEN>,
845    ) -> Result {
846        // decode the cheatcode call
847        let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
848            if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
849                let msg = format!(
850                    "unknown cheatcode with selector {selector}; \
851                     you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
852                     and the `forge` version"
853                );
854                return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
855            }
856            e
857        })?;
858
859        let caller = call.caller;
860
861        // ensure the caller is allowed to execute cheatcodes,
862        // but only if the backend is in forking mode
863        ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
864
865        apply_dispatch(
866            &decoded,
867            &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
868            executor,
869        )
870    }
871
872    /// Grants cheat code access for new contracts if the caller also has
873    /// cheatcode access or the new contract is created in top most call.
874    ///
875    /// There may be cheatcodes in the constructor of the new contract, in order to allow them
876    /// automatically we need to determine the new address.
877    fn allow_cheatcodes_on_create(
878        &self,
879        ecx: &mut FoundryContextFor<FEN>,
880        caller: Address,
881        created_address: Address,
882    ) {
883        if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
884            ecx.db_mut().allow_cheatcode_access(created_address);
885        }
886    }
887
888    /// Apply EIP-2930 access list.
889    ///
890    /// If the transaction type is [TransactionType::Legacy] we need to upgrade it to
891    /// [TransactionType::Eip2930] in order to use access lists. Other transaction types support
892    /// access lists themselves.
893    fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
894        if let Some(access_list) = &self.access_list {
895            ecx.tx_mut().set_access_list(access_list.clone());
896
897            if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
898                ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
899            }
900        }
901    }
902
903    /// Called when there was a revert.
904    ///
905    /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's
906    /// revert would run into issues.
907    pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
908        trace!(deals=?self.eth_deals.len(), "rolling back deals");
909
910        // Delay revert clean up until expected revert is handled, if set.
911        if self.expected_revert.is_some() {
912            return;
913        }
914
915        // we only want to apply cleanup top level
916        if ecx.journal().depth() > 0 {
917            return;
918        }
919
920        // Roll back all previously applied deals
921        // This will prevent overflow issues in revm's [`JournaledState::journal_revert`] routine
922        // which rolls back any transfers.
923        while let Some(record) = self.eth_deals.pop() {
924            if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
925                acc.info.balance = record.old_balance;
926            }
927        }
928    }
929
930    pub fn call_with_executor(
931        &mut self,
932        ecx: &mut FoundryContextFor<'_, FEN>,
933        call: &mut CallInputs,
934        executor: &mut dyn CheatcodesExecutor<FEN>,
935    ) -> Option<CallOutcome> {
936        // Apply custom execution evm version.
937        if let Some(spec_id) = self.execution_evm_version {
938            ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
939        }
940
941        let gas = Gas::new(call.gas_limit);
942        let curr_depth = ecx.journal().depth();
943
944        // At the root call to test function or script `run()`/`setUp()` functions, we are
945        // decreasing sender nonce to ensure that it matches on-chain nonce once we start
946        // broadcasting.
947        if curr_depth == 0 {
948            let sender = ecx.tx().caller();
949            let account = match super::evm::journaled_account(ecx, sender) {
950                Ok(account) => account,
951                Err(err) => {
952                    return Some(CallOutcome {
953                        result: InterpreterResult {
954                            result: InstructionResult::Revert,
955                            output: err.abi_encode().into(),
956                            gas,
957                        },
958                        memory_offset: call.return_memory_offset.clone(),
959                        was_precompile_called: false,
960                        precompile_call_logs: vec![],
961                        charged_new_account_state_gas: false,
962                    });
963                }
964            };
965            let prev = account.info.nonce;
966            account.info.nonce = prev.saturating_sub(1);
967
968            trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
969        }
970
971        if call.target_address == CHEATCODE_ADDRESS {
972            return match self.apply_cheatcode(ecx, call, executor) {
973                Ok(retdata) => Some(CallOutcome {
974                    result: InterpreterResult {
975                        result: InstructionResult::Return,
976                        output: retdata.into(),
977                        gas,
978                    },
979                    memory_offset: call.return_memory_offset.clone(),
980                    was_precompile_called: true,
981                    precompile_call_logs: vec![],
982                    charged_new_account_state_gas: false,
983                }),
984                Err(err) => Some(CallOutcome {
985                    result: InterpreterResult {
986                        result: InstructionResult::Revert,
987                        output: err.abi_encode().into(),
988                        gas,
989                    },
990                    memory_offset: call.return_memory_offset.clone(),
991                    was_precompile_called: false,
992                    precompile_call_logs: vec![],
993                    charged_new_account_state_gas: false,
994                }),
995            };
996        }
997
998        if call.target_address == HARDHAT_CONSOLE_ADDRESS {
999            return None;
1000        }
1001
1002        // `expectRevert`: track max call depth. This is also done in `initialize_interp`, but
1003        // precompile calls don't create an interpreter frame so we must also track it here.
1004        // The callee executes at `curr_depth + 1`.
1005        if let Some(expected) = &mut self.expected_revert {
1006            expected.max_depth = max(curr_depth + 1, expected.max_depth);
1007        }
1008
1009        // Handle expected calls
1010
1011        // Grab the different calldatas expected.
1012        if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
1013        {
1014            let input = call.input.as_bytes(ecx);
1015            let value = call.transfer_value();
1016
1017            // Match every partial/full calldata
1018            for (calldata, (expected, actual_count)) in expected_calls_for_target {
1019                // Increment actual times seen if...
1020                // The calldata is at most, as big as this call's input, and
1021                if calldata.len() <= input.len() &&
1022                    // Both calldata match, taking the length of the assumed smaller one (which will have at least the selector), and
1023                    input.get(..calldata.len()) == Some(calldata.as_ref()) &&
1024                    // The value matches, if provided
1025                    expected.value.is_none_or(|expected_value| Some(expected_value) == value) &&
1026                    // The gas matches, if provided
1027                    expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
1028                    // The minimum gas matches, if provided
1029                    expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
1030                {
1031                    *actual_count += 1;
1032                }
1033            }
1034        }
1035
1036        // Apply our prank
1037        if let Some(prank) = &self.get_prank(curr_depth) {
1038            // Apply delegate call, `call.caller`` will not equal `prank.prank_caller`
1039            if prank.delegate_call
1040                && curr_depth == prank.depth
1041                && call.scheme == CallScheme::DelegateCall
1042            {
1043                call.target_address = prank.new_caller;
1044                call.caller = prank.new_caller;
1045                if let Some(new_origin) = prank.new_origin {
1046                    ecx.tx_mut().set_caller(new_origin);
1047                }
1048            }
1049
1050            if curr_depth >= prank.depth && call.caller == prank.prank_caller {
1051                // At the target depth we set `msg.sender`
1052                let prank_applied = if curr_depth == prank.depth {
1053                    // Ensure new caller is loaded and touched
1054                    let _ = journaled_account(ecx, prank.new_caller);
1055                    call.caller = prank.new_caller;
1056                    true
1057                } else {
1058                    false
1059                };
1060
1061                // At the target depth, or deeper, we set `tx.origin`
1062                let prank_applied = if let Some(new_origin) = prank.new_origin {
1063                    ecx.tx_mut().set_caller(new_origin);
1064                    true
1065                } else {
1066                    prank_applied
1067                };
1068
1069                // If prank applied for first time, then update
1070                if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1071                    self.pranks.insert(curr_depth, applied_prank);
1072                }
1073            }
1074        }
1075
1076        // Handle mocked calls
1077        if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
1078            let input = call.input.bytes(ecx);
1079            let value = call.transfer_value();
1080            let ctx = MockCallDataContext { calldata: input.clone(), value };
1081
1082            if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
1083                Some(queue) => Some(queue),
1084                None => mocks
1085                    .iter_mut()
1086                    .find(|(mock, _)| {
1087                        input.get(..mock.calldata.len()) == Some(&mock.calldata[..])
1088                            && mock.value.is_none_or(|mock_value| Some(mock_value) == value)
1089                    })
1090                    .map(|(_, v)| v),
1091            } && let Some(return_data) = return_data_queue.front().map(|x| x.to_owned())
1092            {
1093                if let Some(value) = call.transfer_value() {
1094                    let checkpoint = ecx.journal_mut().checkpoint();
1095                    match ecx.journal_mut().transfer_loaded(
1096                        call.transfer_from(),
1097                        call.transfer_to(),
1098                        value,
1099                    ) {
1100                        None => {
1101                            if return_data.ret_type.is_ok() {
1102                                ecx.journal_mut().checkpoint_commit();
1103                            } else {
1104                                ecx.journal_mut().checkpoint_revert(checkpoint);
1105                            }
1106                        }
1107                        Some(err) => {
1108                            ecx.journal_mut().checkpoint_revert(checkpoint);
1109                            return Some(CallOutcome {
1110                                result: InterpreterResult {
1111                                    result: err.into(),
1112                                    output: Bytes::new(),
1113                                    gas,
1114                                },
1115                                memory_offset: call.return_memory_offset.clone(),
1116                                was_precompile_called: false,
1117                                precompile_call_logs: vec![],
1118                                charged_new_account_state_gas: false,
1119                            });
1120                        }
1121                    }
1122                }
1123
1124                // If the mocked calls stack has a single element in it, don't empty it
1125                if return_data_queue.len() > 1 {
1126                    return_data_queue.pop_front();
1127                }
1128
1129                return Some(CallOutcome {
1130                    result: InterpreterResult {
1131                        result: return_data.ret_type,
1132                        output: return_data.data,
1133                        gas,
1134                    },
1135                    memory_offset: call.return_memory_offset.clone(),
1136                    was_precompile_called: true,
1137                    precompile_call_logs: vec![],
1138                    charged_new_account_state_gas: false,
1139                });
1140            }
1141        }
1142
1143        // Apply EIP-2930 access list
1144        self.apply_accesslist(ecx);
1145
1146        // Apply our broadcast
1147        if let Some(broadcast) = &self.broadcast {
1148            // Additional check as transfers in forge scripts seem to be estimated at 2300
1149            // by revm leading to "Intrinsic gas too low" failure when simulated on chain.
1150            let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
1151            self.dynamic_gas_limit = false;
1152
1153            // We only apply a broadcast *to a specific depth*.
1154            //
1155            // We do this because any subsequent contract calls *must* exist on chain and
1156            // we only want to grab *this* call, not internal ones
1157            if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
1158                // At the target depth we set `msg.sender` & tx.origin.
1159                // We are simulating the caller as being an EOA, so *both* must be set to the
1160                // broadcast.origin.
1161                ecx.tx_mut().set_caller(broadcast.new_origin);
1162
1163                call.caller = broadcast.new_origin;
1164                // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here
1165                // because we only need the from, to, value, and data. We can later change this
1166                // into 1559, in the cli package, relatively easily once we
1167                // know the target chain supports EIP-1559.
1168                if !call.is_static {
1169                    if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1170                        return Some(CallOutcome {
1171                            result: InterpreterResult {
1172                                result: InstructionResult::Revert,
1173                                output: Error::encode(err),
1174                                gas,
1175                            },
1176                            memory_offset: call.return_memory_offset.clone(),
1177                            was_precompile_called: false,
1178                            precompile_call_logs: vec![],
1179                            charged_new_account_state_gas: false,
1180                        });
1181                    }
1182
1183                    let input = call.input.bytes(ecx);
1184                    let chain_id = ecx.cfg().chain_id();
1185                    let rpc = ecx.db().active_fork_url();
1186                    let account =
1187                        ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
1188
1189                    let mut tx_req = TransactionRequestFor::<FEN>::default()
1190                        .with_from(broadcast.new_origin)
1191                        .with_to(call.target_address)
1192                        .with_value(call.transfer_value().unwrap_or_default())
1193                        .with_input(input)
1194                        .with_nonce(account.info.nonce)
1195                        .with_chain_id(chain_id);
1196                    if is_fixed_gas_limit {
1197                        tx_req.set_gas_limit(call.gas_limit)
1198                    }
1199
1200                    let active_delegations = std::mem::take(&mut self.active_delegations);
1201                    // Set active blob sidecar, if any.
1202                    if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
1203                        // Ensure blob and delegation are not set for the same tx.
1204                        if !active_delegations.is_empty() {
1205                            let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1206                            return Some(CallOutcome {
1207                                result: InterpreterResult {
1208                                    result: InstructionResult::Revert,
1209                                    output: Error::encode(msg),
1210                                    gas,
1211                                },
1212                                memory_offset: call.return_memory_offset.clone(),
1213                                was_precompile_called: false,
1214                                precompile_call_logs: vec![],
1215                                charged_new_account_state_gas: false,
1216                            });
1217                        }
1218                        tx_req.set_blob_sidecar(blob_sidecar);
1219                    }
1220
1221                    // Apply active EIP-7702 delegations, if any.
1222                    if !active_delegations.is_empty() {
1223                        for auth in &active_delegations {
1224                            let Ok(authority) = auth.recover_authority() else {
1225                                continue;
1226                            };
1227                            if authority == broadcast.new_origin {
1228                                // Increment nonce of broadcasting account to reflect signed
1229                                // authorization.
1230                                account.info.nonce += 1;
1231                            }
1232                        }
1233                        tx_req.set_authorization_list(active_delegations);
1234                    }
1235                    if let Some(fee_token) = self.config.fee_token {
1236                        tx_req.set_fee_token(fee_token);
1237                    }
1238                    self.broadcastable_transactions.push_back(BroadcastableTransaction {
1239                        rpc,
1240                        transaction: TransactionMaybeSigned::new(tx_req),
1241                    });
1242                    debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1243
1244                    // Explicitly increment nonce if calls are not isolated.
1245                    if !self.config.evm_opts.isolate {
1246                        let prev = account.info.nonce;
1247                        account.info.nonce += 1;
1248                        debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1249                    }
1250                } else if broadcast.single_call {
1251                    let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1252                    return Some(CallOutcome {
1253                        result: InterpreterResult {
1254                            result: InstructionResult::Revert,
1255                            output: Error::encode(msg),
1256                            gas,
1257                        },
1258                        memory_offset: call.return_memory_offset.clone(),
1259                        was_precompile_called: false,
1260                        precompile_call_logs: vec![],
1261                        charged_new_account_state_gas: false,
1262                    });
1263                }
1264            }
1265        }
1266
1267        // Record called accounts if `startStateDiffRecording` has been called
1268        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1269            // Determine if account is "initialized," ie, it has a non-zero balance, a non-zero
1270            // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code
1271            let (initialized, old_balance, old_nonce) =
1272                if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1273                    (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce)
1274                } else {
1275                    (false, U256::ZERO, 0)
1276                };
1277
1278            let kind = match call.scheme {
1279                CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1280                CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1281                CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1282                CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1283            };
1284
1285            // Record this call by pushing it to a new pending vector; all subsequent calls at
1286            // that depth will be pushed to the same vector. When the call ends, the
1287            // RecordedAccountAccess (and all subsequent RecordedAccountAccesses) will be
1288            // updated with the revert status of this call, since the EVM does not mark accounts
1289            // as "warm" if the call from which they were accessed is reverted
1290            recorded_account_diffs_stack.push(vec![AccountAccess {
1291                chainInfo: crate::Vm::ChainInfo {
1292                    forkId: ecx.db().active_fork_id().unwrap_or_default(),
1293                    chainId: U256::from(ecx.cfg().chain_id()),
1294                },
1295                accessor: call.caller,
1296                account: call.bytecode_address,
1297                kind,
1298                initialized,
1299                oldBalance: old_balance,
1300                newBalance: U256::ZERO, // updated on call_end
1301                oldNonce: old_nonce,
1302                newNonce: 0, // updated on call_end
1303                value: call.call_value(),
1304                data: call.input.bytes(ecx),
1305                reverted: false,
1306                deployedCode: Bytes::new(),
1307                storageAccesses: vec![], // updated on step
1308                depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1309            }]);
1310        }
1311
1312        None
1313    }
1314
1315    pub fn rng(&mut self) -> &mut impl Rng {
1316        self.test_runner().rng()
1317    }
1318
1319    pub fn test_runner(&mut self) -> &mut TestRunner {
1320        self.test_runner.get_or_insert_with(|| match self.config.seed {
1321            Some(seed) => TestRunner::new_with_rng(
1322                proptest::test_runner::Config::default(),
1323                TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1324            ),
1325            None => TestRunner::new(proptest::test_runner::Config::default()),
1326        })
1327    }
1328
1329    pub fn set_seed(&mut self, seed: U256) {
1330        self.test_runner = Some(TestRunner::new_with_rng(
1331            proptest::test_runner::Config::default(),
1332            TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1333        ));
1334    }
1335
1336    /// Returns existing or set a default `ArbitraryStorage` option.
1337    /// Used by `setArbitraryStorage` cheatcode to track addresses with arbitrary storage.
1338    pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1339        self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1340    }
1341
1342    /// Returns addresses explicitly marked with arbitrary storage.
1343    pub fn arbitrary_storage_targets(&self) -> impl Iterator<Item = Address> + '_ {
1344        self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::targets)
1345    }
1346
1347    /// Returns addresses explicitly marked with arbitrary storage and whether nonzero slots are
1348    /// overwritten.
1349    pub fn arbitrary_storage_target_overwrite_modes(
1350        &self,
1351    ) -> impl Iterator<Item = (Address, bool)> + '_ {
1352        self.arbitrary_storage
1353            .as_ref()
1354            .into_iter()
1355            .flat_map(ArbitraryStorage::target_overwrite_modes)
1356    }
1357
1358    /// Returns addresses that copy storage from arbitrary-storage targets.
1359    pub fn arbitrary_storage_copied_targets(&self) -> impl Iterator<Item = Address> + '_ {
1360        self.arbitrary_storage.as_ref().into_iter().flat_map(ArbitraryStorage::copied_targets)
1361    }
1362
1363    /// Returns copied arbitrary-storage targets and their source address.
1364    pub fn arbitrary_storage_copied_target_sources(
1365        &self,
1366    ) -> impl Iterator<Item = (Address, Address)> + '_ {
1367        self.arbitrary_storage
1368            .as_ref()
1369            .into_iter()
1370            .flat_map(ArbitraryStorage::copied_target_sources)
1371    }
1372
1373    /// Caches a concrete replay value for a slot on an arbitrary-storage address or copied target.
1374    pub fn cache_arbitrary_storage_value(&mut self, address: Address, slot: U256, value: U256) {
1375        if let Some(storage) = &mut self.arbitrary_storage {
1376            storage.cache_value(address, slot, value);
1377        }
1378    }
1379
1380    /// Returns a cached arbitrary-storage replay value for a slot.
1381    pub fn cached_arbitrary_storage_value(&self, address: Address, slot: U256) -> Option<U256> {
1382        self.arbitrary_storage.as_ref().and_then(|storage| storage.cached_value(address, slot))
1383    }
1384
1385    /// Whether the given address has arbitrary storage.
1386    pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1387        match &self.arbitrary_storage {
1388            Some(storage) => storage.values.contains_key(address),
1389            None => false,
1390        }
1391    }
1392
1393    /// Whether the given slot of address with arbitrary storage should be overwritten.
1394    /// True if address is marked as and overwrite and if no value was previously generated for
1395    /// given slot.
1396    pub fn should_overwrite_arbitrary_storage(
1397        &self,
1398        address: &Address,
1399        storage_slot: U256,
1400    ) -> bool {
1401        match &self.arbitrary_storage {
1402            Some(storage) => {
1403                storage.overwrites.contains(address)
1404                    && storage
1405                        .values
1406                        .get(address)
1407                        .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1408                        .is_none()
1409            }
1410            None => false,
1411        }
1412    }
1413
1414    /// Whether the given address is a copy of an address with arbitrary storage.
1415    pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1416        match &self.arbitrary_storage {
1417            Some(storage) => storage.copies.contains_key(address),
1418            None => false,
1419        }
1420    }
1421
1422    #[inline(always)]
1423    pub fn has_step_hooks(&self) -> bool {
1424        self.broadcast.is_some()
1425            || self.gas_metering.paused
1426            || self.gas_metering.reset
1427            || self.recording_accesses
1428            || self.recorded_account_diffs_stack.is_some()
1429            || !self.allowed_mem_writes.is_empty()
1430            || self.mapping_slots.is_some()
1431            || self.gas_metering.recording
1432            || self.has_active_env_overrides()
1433    }
1434
1435    #[inline(always)]
1436    pub fn has_step_end_hooks(&self) -> bool {
1437        self.gas_metering.paused
1438            || self.gas_metering.touched
1439            || self.arbitrary_storage.is_some()
1440            || self.has_active_env_overrides()
1441    }
1442
1443    #[inline(always)]
1444    pub fn has_log_hooks(&self) -> bool {
1445        !self.expected_emits.is_empty() || self.recorded_logs.is_some()
1446    }
1447
1448    #[inline(always)]
1449    pub fn has_recording_accesses_only_step_hook(&self) -> bool {
1450        self.recording_accesses
1451            && self.broadcast.is_none()
1452            && !self.gas_metering.paused
1453            && !self.gas_metering.reset
1454            && self.recorded_account_diffs_stack.is_none()
1455            && self.allowed_mem_writes.is_empty()
1456            && self.mapping_slots.is_none()
1457            && !self.gas_metering.recording
1458            && !self.has_active_env_overrides()
1459    }
1460
1461    #[inline(always)]
1462    fn has_active_env_overrides(&self) -> bool {
1463        self.env_overrides.values().any(EnvOverrides::is_any_set)
1464    }
1465
1466    /// Returns struct definitions from the analysis, if available.
1467    pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1468        self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1469    }
1470}
1471
1472impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1473    fn initialize_interp(
1474        &mut self,
1475        interpreter: &mut Interpreter,
1476        ecx: &mut FoundryContextFor<'_, FEN>,
1477    ) {
1478        // When the first interpreter is initialized we've circumvented the balance and gas checks,
1479        // so we apply our actual block data with the correct fees and all.
1480        if let Some(block) = self.block.take() {
1481            ecx.set_block(block);
1482        }
1483        if let Some(gas_price) = self.gas_price.take() {
1484            ecx.tx_mut().set_gas_price(gas_price);
1485        }
1486
1487        // Record gas for current frame.
1488        if self.gas_metering.paused {
1489            self.gas_metering.paused_frames.push(interpreter.gas);
1490        }
1491
1492        // `expectRevert`: track the max call depth during `expectRevert`
1493        if let Some(expected) = &mut self.expected_revert {
1494            expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1495        }
1496    }
1497
1498    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1499        self.pc = interpreter.bytecode.pc();
1500
1501        if !self.has_step_hooks() {
1502            return;
1503        }
1504
1505        if self.broadcast.is_some() {
1506            self.set_gas_limit_type(interpreter);
1507        }
1508
1509        // `pauseGasMetering`: pause / resume interpreter gas.
1510        if self.gas_metering.paused {
1511            self.meter_gas(interpreter);
1512        }
1513
1514        // `resetGasMetering`: reset interpreter gas.
1515        if self.gas_metering.reset {
1516            self.meter_gas_reset(interpreter);
1517        }
1518
1519        // `record`: record storage reads and writes.
1520        if self.recording_accesses {
1521            self.record_accesses(interpreter);
1522        }
1523
1524        // `startStateDiffRecording`: record granular ordered storage accesses.
1525        if self.recorded_account_diffs_stack.is_some() {
1526            self.record_state_diffs(interpreter, ecx);
1527        }
1528
1529        // `expectSafeMemory`: check if the current opcode is allowed to interact with memory.
1530        if !self.allowed_mem_writes.is_empty() {
1531            self.check_mem_opcodes(
1532                interpreter,
1533                ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1534            );
1535        }
1536
1537        // `startMappingRecording`: record SSTORE and KECCAK256.
1538        if let Some(mapping_slots) = &mut self.mapping_slots {
1539            mapping_step(mapping_slots, interpreter);
1540        }
1541
1542        // `snapshotGas*`: take a snapshot of the current gas.
1543        if self.gas_metering.recording {
1544            self.meter_gas_record(interpreter, ecx);
1545        }
1546
1547        // Capture the opcode for `step_end` to use, since by the time
1548        // `step_end` runs the PC has already advanced past it. Also peek the
1549        // BLOBHASH index now (still on top of stack before execution) so we
1550        // can look up the override later.
1551        if !self.env_overrides.is_empty() {
1552            let fork_id = ecx.db().active_fork_id();
1553            if let Some(env_overrides) =
1554                self.env_overrides.get_mut(&fork_id).filter(|o| o.is_any_set())
1555            {
1556                // Always clear stale pending state first so a leftover value from
1557                // a prior step (e.g. when `peek` failed, or when an override
1558                // wasn't actually used) cannot leak into the next opcode.
1559                env_overrides.pending_opcode = None;
1560                env_overrides.pending_blobhash_index = None;
1561
1562                let opcode = interpreter.bytecode.opcode();
1563                match opcode {
1564                    op::BASEFEE | op::GASPRICE => {
1565                        env_overrides.pending_opcode = Some(opcode);
1566                    }
1567                    op::BLOBHASH => {
1568                        env_overrides.pending_opcode = Some(opcode);
1569                        env_overrides.pending_blobhash_index =
1570                            interpreter.stack.peek(0).ok().and_then(|index| index.try_into().ok());
1571                    }
1572                    _ => {}
1573                }
1574            }
1575        }
1576    }
1577
1578    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1579        if !self.has_step_end_hooks() {
1580            return;
1581        }
1582
1583        if self.gas_metering.paused {
1584            self.meter_gas_end(interpreter);
1585        }
1586
1587        if self.gas_metering.touched {
1588            self.meter_gas_check(interpreter);
1589        }
1590
1591        // `setArbitraryStorage` and `copyStorage`: add arbitrary values to storage.
1592        if self.arbitrary_storage.is_some() {
1593            self.arbitrary_storage_end(interpreter, ecx);
1594        }
1595
1596        // Apply opcode-level env overrides (basefee/gasprice/blobhash). Needed
1597        // in isolation mode where the actual tx/block env is zeroed for
1598        // fee-accounting; in non-isolation mode the override and the real env
1599        // are kept in sync by the cheatcode handlers, so this is a no-op fixup.
1600        //
1601        // We must only rewrite the stack if the opcode actually completed
1602        // successfully and pushed its result; otherwise (stack underflow on
1603        // BLOBHASH, OOG before push, etc.) the stack is in an error state and
1604        // a blind `pop()+push()` would corrupt the failing frame.
1605        if !self.env_overrides.is_empty() {
1606            let fork_id = ecx.db().active_fork_id();
1607            if self.env_overrides.get(&fork_id).is_some_and(|o| o.is_any_set()) {
1608                // Mirrors the pattern used by `meter_gas_record`: when `action` is
1609                // `Some` with an `instruction_result`, the opcode has set a
1610                // non-continue result (halt/revert/error) — i.e. it didn't push
1611                // its normal result. `None` means "still running", which is the
1612                // success path for a stack-only opcode in `step_end`.
1613                let opcode_failed = interpreter
1614                    .bytecode
1615                    .action
1616                    .as_ref()
1617                    .and_then(|a| a.instruction_result())
1618                    .is_some();
1619                if opcode_failed {
1620                    if let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) {
1621                        env_overrides.pending_opcode = None;
1622                        env_overrides.pending_blobhash_index = None;
1623                    }
1624                } else {
1625                    self.apply_env_overrides(interpreter, fork_id);
1626                }
1627            }
1628        }
1629    }
1630
1631    fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1632        if !self.expected_emits.is_empty()
1633            && let Some(err) = expect::handle_expect_emit(self, &log, None)
1634        {
1635            // Because we do not have access to the interpreter here, we cannot fail the test
1636            // immediately. In most cases the failure will still be caught on `call_end`.
1637            // In the rare case it is not, we log the error here.
1638            let _ = sh_err!("{err:?}");
1639        }
1640
1641        // `recordLogs`
1642        record_logs(&mut self.recorded_logs, &log);
1643    }
1644
1645    fn log_full(
1646        &mut self,
1647        interpreter: &mut Interpreter,
1648        _ecx: &mut FoundryContextFor<'_, FEN>,
1649        log: Log,
1650    ) {
1651        if !self.expected_emits.is_empty() {
1652            expect::handle_expect_emit(self, &log, Some(interpreter));
1653        }
1654
1655        // `recordLogs`
1656        record_logs(&mut self.recorded_logs, &log);
1657    }
1658
1659    fn call(
1660        &mut self,
1661        ecx: &mut FoundryContextFor<'_, FEN>,
1662        inputs: &mut CallInputs,
1663    ) -> Option<CallOutcome> {
1664        Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1665    }
1666
1667    fn call_end(
1668        &mut self,
1669        ecx: &mut FoundryContextFor<'_, FEN>,
1670        call: &CallInputs,
1671        outcome: &mut CallOutcome,
1672    ) {
1673        let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1674            || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1675
1676        // Clean up pranks/broadcasts if it's not a cheatcode call end. We shouldn't do
1677        // it for cheatcode calls because they are not applied for cheatcodes in the `call` hook.
1678        // This should be placed before the revert handling, because we might exit early there
1679        if !cheatcode_call {
1680            // Clean up pranks
1681            let curr_depth = ecx.journal().depth();
1682            if let Some(prank) = &self.get_prank(curr_depth)
1683                && curr_depth == prank.depth
1684            {
1685                ecx.tx_mut().set_caller(prank.prank_origin);
1686
1687                // Clean single-call prank once we have returned to the original depth
1688                if prank.single_call {
1689                    self.pranks.remove(&curr_depth);
1690                }
1691            }
1692
1693            // Clean up broadcast
1694            if let Some(broadcast) = &self.broadcast
1695                && curr_depth == broadcast.depth
1696            {
1697                ecx.tx_mut().set_caller(broadcast.original_origin);
1698
1699                // Clean single-call broadcast once we have returned to the original depth
1700                if broadcast.single_call {
1701                    let _ = self.broadcast.take();
1702                }
1703            }
1704        }
1705
1706        // Handle assume no revert cheatcode.
1707        if let Some(assume_no_revert) = &mut self.assume_no_revert {
1708            // Record current reverter address before processing the expect revert if call reverted,
1709            // expect revert is set with expected reverter address and no actual reverter set yet.
1710            if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1711                assume_no_revert.reverted_by = Some(call.target_address);
1712            }
1713
1714            // allow multiple cheatcode calls at the same depth
1715            let curr_depth = ecx.journal().depth();
1716            if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1717                // Discard run if we're at the same depth as cheatcode, call reverted, and no
1718                // specific reason was supplied
1719                if outcome.result.is_revert() {
1720                    let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1721                    return match revert_handlers::handle_assume_no_revert(
1722                        &assume_no_revert,
1723                        outcome.result.result,
1724                        &outcome.result.output,
1725                        &self.config.available_artifacts,
1726                    ) {
1727                        // if result is Ok, it was an anticipated revert; return an "assume" error
1728                        // to reject this run
1729                        Ok(_) => {
1730                            outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1731                        }
1732                        // if result is Error, it was an unanticipated revert; should revert
1733                        // normally
1734                        Err(error) => {
1735                            trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1736                            outcome.result.result = InstructionResult::Revert;
1737                            outcome.result.output = error.abi_encode().into();
1738                        }
1739                    };
1740                }
1741                // Call didn't revert, reset `assume_no_revert` state.
1742                self.assume_no_revert = None;
1743            }
1744        }
1745
1746        // Handle expected reverts.
1747        if let Some(expected_revert) = &mut self.expected_revert {
1748            // Record current reverter address and call scheme before processing the expect revert
1749            // if call reverted.
1750            let call_failed = !matches!(outcome.result.result, return_ok!());
1751            if call_failed {
1752                // Record current reverter address if expect revert is set with expected reverter
1753                // address and no actual reverter was set yet or if we're expecting more than one
1754                // revert.
1755                if expected_revert.reverter.is_some()
1756                    && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1757                {
1758                    expected_revert.reverted_by = Some(call.target_address);
1759                }
1760            }
1761
1762            let curr_depth = ecx.journal().depth();
1763            if curr_depth <= expected_revert.depth {
1764                // Decide whether this `call_end` should consume the pending `expectRevert`.
1765                // With `internal_expect_revert` enabled, a same-depth revert can satisfy it, but
1766                // we must not consume it for external calls that succeed (e.g. calls to
1767                // non-contract addresses that return `Stop` before Solidity's own revert).
1768                let internal = self.config.internal_expect_revert;
1769                let went_deeper = expected_revert.max_depth > expected_revert.depth;
1770                let needs_processing = match expected_revert.kind {
1771                    ExpectedRevertKind::Default => (|| {
1772                        // Cheatcode reverts propagate up; let the outer frame catch them.
1773                        if cheatcode_call {
1774                            return false;
1775                        }
1776                        // Any failure satisfies the expectation.
1777                        if call_failed {
1778                            return true;
1779                        }
1780                        // Traditional expectRevert: succeeded external call went deeper.
1781                        if !internal && went_deeper {
1782                            return true;
1783                        }
1784                        // Test function returned: catch dangling expectations.
1785                        if curr_depth == 0 {
1786                            return true;
1787                        }
1788                        // Same-depth success with internal mode off is an error; with it on,
1789                        // keep waiting for the actual revert.
1790                        !internal
1791                    })(),
1792                    // `pending_processing == true` means we're in the `call_end` hook for
1793                    // `vm.expectCheatcodeRevert` and shouldn't expect a revert here.
1794                    ExpectedRevertKind::Cheatcode { pending_processing } => {
1795                        cheatcode_call && !pending_processing
1796                    }
1797                };
1798
1799                if needs_processing {
1800                    let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1801                    let clear_last_frame_gas =
1802                        matches!(expected_revert.kind, ExpectedRevertKind::Default);
1803                    return match revert_handlers::handle_expect_revert(
1804                        cheatcode_call,
1805                        false,
1806                        self.config.internal_expect_revert,
1807                        &expected_revert,
1808                        outcome.result.result,
1809                        outcome.result.output.clone(),
1810                        &self.config.available_artifacts,
1811                    ) {
1812                        Err(error) => {
1813                            trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1814                            outcome.result.result = InstructionResult::Revert;
1815                            outcome.result.output = error.abi_encode().into();
1816                        }
1817                        Ok((_, retdata)) => {
1818                            expected_revert.actual_count += 1;
1819                            if expected_revert.actual_count < expected_revert.count {
1820                                self.expected_revert = Some(expected_revert);
1821                            }
1822                            if clear_last_frame_gas {
1823                                self.gas_metering.last_frame_gas = None;
1824                            }
1825                            outcome.result.result = InstructionResult::Return;
1826                            outcome.result.output = retdata;
1827                        }
1828                    };
1829                }
1830
1831                // Flip `pending_processing` flag for cheatcode revert expectations, marking that
1832                // we've exited the `expectCheatcodeRevert` call scope
1833                if let ExpectedRevertKind::Cheatcode { pending_processing } =
1834                    &mut self.expected_revert.as_mut().unwrap().kind
1835                {
1836                    *pending_processing = false;
1837                }
1838            }
1839        }
1840
1841        // Exit early for calls to cheatcodes as other logic is not relevant for cheatcode
1842        // invocations
1843        if cheatcode_call {
1844            return;
1845        }
1846
1847        // Record the gas usage of the call, this allows the `lastFrameGas` cheatcode to
1848        // retrieve the gas usage of the last call or create.
1849        let gas = outcome.result.gas;
1850        let frame_gas = crate::Vm::Gas {
1851            gasLimit: gas.limit(),
1852            gasTotalUsed: gas.total_gas_spent(),
1853            gasMemoryUsed: 0,
1854            gasRefunded: gas.refunded(),
1855            gasRemaining: gas.remaining(),
1856        };
1857        self.gas_metering.last_call_gas = Some(frame_gas.clone());
1858        self.gas_metering.last_frame_gas = Some(frame_gas);
1859
1860        // If `startStateDiffRecording` has been called, update the `reverted` status of the
1861        // previous call depth's recorded accesses, if any
1862        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1863            // The root call cannot be recorded.
1864            if ecx.journal().depth() > 0
1865                && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1866            {
1867                // Update the reverted status of all deeper calls if this call reverted, in
1868                // accordance with EVM behavior
1869                if outcome.result.is_revert() {
1870                    for element in &mut *last_recorded_depth {
1871                        element.reverted = true;
1872                        for storage_access in &mut element.storageAccesses {
1873                            storage_access.reverted = true;
1874                        }
1875                    }
1876                }
1877
1878                if let Some(call_access) = last_recorded_depth.first_mut() {
1879                    // Assert that we're at the correct depth before recording post-call state
1880                    // changes. Depending on the depth the cheat was
1881                    // called at, there may not be any pending
1882                    // calls to update if execution has percolated up to a higher depth.
1883                    let curr_depth = ecx.journal().depth();
1884                    if call_access.depth == curr_depth as u64
1885                        && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1886                    {
1887                        debug_assert!(access_is_call(call_access.kind));
1888                        call_access.newBalance = acc.data.info.balance;
1889                        call_access.newNonce = acc.data.info.nonce;
1890                    }
1891                    // Merge the last depth's AccountAccesses into the AccountAccesses at the
1892                    // current depth, or push them back onto the pending
1893                    // vector if higher depths were not recorded. This
1894                    // preserves ordering of accesses.
1895                    if let Some(last) = recorded_account_diffs_stack.last_mut() {
1896                        last.extend(last_recorded_depth);
1897                    } else {
1898                        recorded_account_diffs_stack.push(last_recorded_depth);
1899                    }
1900                }
1901            }
1902        }
1903
1904        // this will ensure we don't have false positives when trying to diagnose reverts in fork
1905        // mode
1906        let diag = self.fork_revert_diagnostic.take();
1907
1908        // If the call already reverted, preserve that primary failure and skip post-call
1909        // expect* validation so it cannot overwrite the original revert.
1910        if outcome.result.is_revert() {
1911            // if there's a revert and a previous call was diagnosed as fork related revert then we
1912            // can return a better error here
1913            if let Some(err) = diag {
1914                outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1915            }
1916            return;
1917        }
1918
1919        // At the end of the call,
1920        // we need to check if we've found all the emits.
1921        // We know we've found all the expected emits in the right order
1922        // if the queue is fully matched.
1923        // If it's not fully matched, then either:
1924        // 1. Not enough events were emitted (we'll know this because the amount of times we
1925        // inspected events will be less than the size of the queue) 2. The wrong events
1926        // were emitted (The inspected events should match the size of the queue, but still some
1927        // events will not be matched)
1928
1929        // First, check that we're at the call depth where the emits were declared from.
1930        let should_check_emits = self
1931            .expected_emits
1932            .iter()
1933            .any(|(expected, _)| {
1934                let curr_depth = ecx.journal().depth();
1935                expected.depth == curr_depth
1936            }) &&
1937            // Ignore staticcalls
1938            !call.is_static;
1939        if should_check_emits {
1940            let expected_counts = self
1941                .expected_emits
1942                .iter()
1943                .filter_map(|(expected, count_map)| {
1944                    let count = match expected.address {
1945                        Some(emitter) => match count_map.get(&emitter) {
1946                            Some(log_count) => expected
1947                                .log
1948                                .as_ref()
1949                                .map(|l| log_count.count(l))
1950                                .unwrap_or_else(|| log_count.count_unchecked()),
1951                            None => 0,
1952                        },
1953                        None => match &expected.log {
1954                            Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1955                            None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1956                        },
1957                    };
1958
1959                    (count != expected.count).then_some((expected, count))
1960                })
1961                .collect::<Vec<_>>();
1962
1963            // Revert if not all emits expected were matched.
1964            if let Some((expected, _)) = self
1965                .expected_emits
1966                .iter()
1967                .find(|(expected, _)| !expected.found && expected.count > 0)
1968            {
1969                outcome.result.result = InstructionResult::Revert;
1970                let mismatch_error = expected.mismatch_error.clone();
1971                let expected_log = expected.log.clone();
1972                let checks = expected.checks;
1973                let anonymous = expected.anonymous;
1974                let error_msg = mismatch_error
1975                    .as_ref()
1976                    .map(|mismatch| {
1977                        mismatch.to_error_msg(self, checks, expected_log.as_ref(), anonymous)
1978                    })
1979                    .unwrap_or_else(|| "log != expected log".to_string());
1980                outcome.result.output = error_msg.abi_encode().into();
1981                return;
1982            }
1983
1984            if !expected_counts.is_empty() {
1985                let msg = if outcome.result.is_ok() {
1986                    let (expected, count) = expected_counts.first().unwrap();
1987                    format!("log emitted {count} times, expected {}", expected.count)
1988                } else {
1989                    "expected an emit, but the call reverted instead. \
1990                     ensure you're testing the happy path when using `expectEmit`"
1991                        .to_string()
1992                };
1993
1994                outcome.result.result = InstructionResult::Revert;
1995                outcome.result.output = Error::encode(msg);
1996                return;
1997            }
1998
1999            // All emits were found, we're good.
2000            // Clear the queue, as we expect the user to declare more events for the next call
2001            // if they wanna match further events.
2002            self.expected_emits.clear()
2003        }
2004
2005        // try to diagnose reverts in multi-fork mode where a call is made to an address that does
2006        // not exist
2007        if let TxKind::Call(test_contract) = ecx.tx().kind() {
2008            // if a call to a different contract than the original test contract returned with
2009            // `Stop` we check if the contract actually exists on the active fork
2010            if ecx.db().is_forked_mode()
2011                && outcome.result.result == InstructionResult::Stop
2012                && call.target_address != test_contract
2013            {
2014                self.fork_revert_diagnostic =
2015                    ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
2016            }
2017        }
2018
2019        // If the depth is 0, then this is the root call terminating
2020        if ecx.journal().depth() == 0 {
2021            // If we already have a revert, we shouldn't run the below logic as it can obfuscate an
2022            // earlier error that happened first with unrelated information about
2023            // another error when using cheatcodes.
2024            if outcome.result.is_revert() {
2025                return;
2026            }
2027
2028            // If there's not a revert, we can continue on to run the last logic for expect*
2029            // cheatcodes.
2030
2031            // Match expected calls
2032            for (address, calldatas) in &self.expected_calls {
2033                // Loop over each address, and for each address, loop over each calldata it expects.
2034                for (calldata, (expected, actual_count)) in calldatas {
2035                    // Grab the values we expect to see
2036                    let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
2037
2038                    let failed = match call_type {
2039                        // If the cheatcode was called with a `count` argument,
2040                        // we must check that the EVM performed a CALL with this calldata exactly
2041                        // `count` times.
2042                        ExpectedCallType::Count => *count != *actual_count,
2043                        // If the cheatcode was called without a `count` argument,
2044                        // we must check that the EVM performed a CALL with this calldata at least
2045                        // `count` times. The amount of times to check was
2046                        // the amount of time the cheatcode was called.
2047                        ExpectedCallType::NonCount => *count > *actual_count,
2048                    };
2049                    if failed {
2050                        let expected_values = [
2051                            Some(format!("data {}", hex::encode_prefixed(calldata))),
2052                            value.as_ref().map(|v| format!("value {v}")),
2053                            gas.map(|g| format!("gas {g}")),
2054                            min_gas.map(|g| format!("minimum gas {g}")),
2055                        ]
2056                        .into_iter()
2057                        .flatten()
2058                        .join(", ");
2059                        let but = if outcome.result.is_ok() {
2060                            let s = if *actual_count == 1 { "" } else { "s" };
2061                            format!("was called {actual_count} time{s}")
2062                        } else {
2063                            "the call reverted instead; \
2064                             ensure you're testing the happy path when using `expectCall`"
2065                                .to_string()
2066                        };
2067                        let s = if *count == 1 { "" } else { "s" };
2068                        let msg = format!(
2069                            "expected call to {address} with {expected_values} \
2070                             to be called {count} time{s}, but {but}"
2071                        );
2072                        outcome.result.result = InstructionResult::Revert;
2073                        outcome.result.output = Error::encode(msg);
2074
2075                        return;
2076                    }
2077                }
2078            }
2079
2080            // Check if we have any leftover expected emits
2081            // First, if any emits were found at the root call, then we its ok and we remove them.
2082            // For count=0 expectations, NOT being found is success, so mark them as found
2083            for (expected, _) in &mut self.expected_emits {
2084                if expected.count == 0 && !expected.found {
2085                    expected.found = true;
2086                }
2087            }
2088            self.expected_emits.retain(|(expected, _)| !expected.found);
2089            // If not empty, we got mismatched emits
2090            if !self.expected_emits.is_empty() {
2091                let msg = if outcome.result.is_ok() {
2092                    "expected an emit, but no logs were emitted afterwards. \
2093                     you might have mismatched events or not enough events were emitted"
2094                } else {
2095                    "expected an emit, but the call reverted instead. \
2096                     ensure you're testing the happy path when using `expectEmit`"
2097                };
2098                outcome.result.result = InstructionResult::Revert;
2099                outcome.result.output = Error::encode(msg);
2100                return;
2101            }
2102
2103            // Check for leftover expected creates
2104            if let Some(expected_create) = self.expected_creates.first() {
2105                let msg = format!(
2106                    "expected {} call by address {} for bytecode {} but not found",
2107                    expected_create.create_scheme,
2108                    hex::encode_prefixed(expected_create.deployer),
2109                    hex::encode_prefixed(&expected_create.bytecode),
2110                );
2111                outcome.result.result = InstructionResult::Revert;
2112                outcome.result.output = Error::encode(msg);
2113            }
2114        }
2115    }
2116
2117    fn create(
2118        &mut self,
2119        ecx: &mut FoundryContextFor<'_, FEN>,
2120        mut input: &mut CreateInputs,
2121    ) -> Option<CreateOutcome> {
2122        // Apply custom execution evm version.
2123        if let Some(spec_id) = self.execution_evm_version {
2124            ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
2125        }
2126
2127        let gas = Gas::new(input.gas_limit());
2128        // Check if we should intercept this create
2129        if self.intercept_next_create_call {
2130            // Reset the flag
2131            self.intercept_next_create_call = false;
2132
2133            // Get initcode from the input
2134            let output = input.init_code();
2135
2136            // Return a revert with the initcode as error data
2137            return Some(CreateOutcome {
2138                result: InterpreterResult { result: InstructionResult::Revert, output, gas },
2139                address: None,
2140            });
2141        }
2142
2143        let curr_depth = ecx.journal().depth();
2144
2145        // Apply our prank
2146        if let Some(prank) = &self.get_prank(curr_depth)
2147            && curr_depth >= prank.depth
2148            && input.caller() == prank.prank_caller
2149        {
2150            // At the target depth we set `msg.sender`
2151            let prank_applied = if curr_depth == prank.depth {
2152                // Ensure new caller is loaded and touched
2153                let _ = journaled_account(ecx, prank.new_caller);
2154                input.set_caller(prank.new_caller);
2155                true
2156            } else {
2157                false
2158            };
2159
2160            // At the target depth, or deeper, we set `tx.origin`
2161            let prank_applied = if let Some(new_origin) = prank.new_origin {
2162                ecx.tx_mut().set_caller(new_origin);
2163                true
2164            } else {
2165                prank_applied
2166            };
2167
2168            // If prank applied for first time, then update
2169            if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
2170                self.pranks.insert(curr_depth, applied_prank);
2171            }
2172        }
2173
2174        // Apply EIP-2930 access list
2175        self.apply_accesslist(ecx);
2176
2177        // Apply our broadcast
2178        if let Some(broadcast) = &mut self.broadcast
2179            && curr_depth >= broadcast.depth
2180            && input.caller() == broadcast.original_caller
2181        {
2182            if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
2183                return Some(CreateOutcome {
2184                    result: InterpreterResult {
2185                        result: InstructionResult::Revert,
2186                        output: Error::encode(err),
2187                        gas,
2188                    },
2189                    address: None,
2190                });
2191            }
2192
2193            ecx.tx_mut().set_caller(broadcast.new_origin);
2194
2195            if curr_depth == broadcast.depth || broadcast.deploy_from_code {
2196                // Reset deploy from code flag for upcoming calls;
2197                broadcast.deploy_from_code = false;
2198
2199                input.set_caller(broadcast.new_origin);
2200
2201                let rpc = ecx.db().active_fork_url();
2202                let account = &ecx.journal().evm_state()[&broadcast.new_origin];
2203                let mut tx_req = TransactionRequestFor::<FEN>::default()
2204                    .with_from(broadcast.new_origin)
2205                    .with_kind(TxKind::Create)
2206                    .with_value(input.value())
2207                    .with_input(input.init_code())
2208                    .with_nonce(account.info.nonce);
2209                if let Some(fee_token) = self.config.fee_token {
2210                    tx_req.set_fee_token(fee_token);
2211                }
2212                self.broadcastable_transactions.push_back(BroadcastableTransaction {
2213                    rpc,
2214                    transaction: TransactionMaybeSigned::new(tx_req),
2215                });
2216
2217                input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
2218            }
2219        }
2220
2221        // Allow cheatcodes from the address of the new contract
2222        let address = input.allow_cheatcodes(self, ecx);
2223
2224        // If `recordAccountAccesses` has been called, record the create
2225        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2226            recorded_account_diffs_stack.push(vec![AccountAccess {
2227                chainInfo: crate::Vm::ChainInfo {
2228                    forkId: ecx.db().active_fork_id().unwrap_or_default(),
2229                    chainId: U256::from(ecx.cfg().chain_id()),
2230                },
2231                accessor: input.caller(),
2232                account: address,
2233                kind: crate::Vm::AccountAccessKind::Create,
2234                initialized: true,
2235                oldBalance: U256::ZERO, // updated on create_end
2236                newBalance: U256::ZERO, // updated on create_end
2237                oldNonce: 0,            // new contract starts with nonce 0
2238                newNonce: 1,            // updated on create_end (contracts start with nonce 1)
2239                value: input.value(),
2240                data: input.init_code(),
2241                reverted: false,
2242                deployedCode: Bytes::new(), // updated on create_end
2243                storageAccesses: vec![],    // updated on create_end
2244                depth: curr_depth as u64,
2245            }]);
2246        }
2247
2248        None
2249    }
2250
2251    fn create_end(
2252        &mut self,
2253        ecx: &mut FoundryContextFor<'_, FEN>,
2254        call: &CreateInputs,
2255        outcome: &mut CreateOutcome,
2256    ) {
2257        let call = Some(call);
2258        let curr_depth = ecx.journal().depth();
2259
2260        // Clean up pranks
2261        if let Some(prank) = &self.get_prank(curr_depth)
2262            && curr_depth == prank.depth
2263        {
2264            ecx.tx_mut().set_caller(prank.prank_origin);
2265
2266            // Clean single-call prank once we have returned to the original depth
2267            if prank.single_call {
2268                std::mem::take(&mut self.pranks);
2269            }
2270        }
2271
2272        // Clean up broadcasts
2273        if let Some(broadcast) = &self.broadcast
2274            && curr_depth == broadcast.depth
2275        {
2276            ecx.tx_mut().set_caller(broadcast.original_origin);
2277
2278            // Clean single-call broadcast once we have returned to the original depth
2279            if broadcast.single_call {
2280                std::mem::take(&mut self.broadcast);
2281            }
2282        }
2283
2284        // Handle expected reverts.
2285        if let Some(expected_revert) = &mut self.expected_revert {
2286            // Record the would-be deployed address as the reverter, picking the innermost
2287            // reverting CREATE: this hook runs at every depth, the deepest frame fires
2288            // first, and the `is_none()` lock pins it. For `count > 1` the lock is
2289            // released after each successful iteration (see below) so each iteration
2290            // independently records its own innermost CREATE.
2291            //
2292            // This intentionally differs from `call_end` for `count > 1`, where
2293            // legacy nested CALL handling reports the outermost call per iteration.
2294            //
2295            // `outcome.address` is `None` for pre-frame rejection (depth/balance/nonce);
2296            // in that case the surrounding `call_end` records the caller as the reverter.
2297            if outcome.result.is_revert()
2298                && expected_revert.reverter.is_some()
2299                && expected_revert.reverted_by.is_none()
2300                && let Some(addr) = outcome.address
2301            {
2302                expected_revert.reverted_by = Some(addr);
2303            }
2304
2305            if curr_depth <= expected_revert.depth
2306                && matches!(expected_revert.kind, ExpectedRevertKind::Default)
2307            {
2308                let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
2309                return match revert_handlers::handle_expect_revert(
2310                    false,
2311                    true,
2312                    self.config.internal_expect_revert,
2313                    &expected_revert,
2314                    outcome.result.result,
2315                    outcome.result.output.clone(),
2316                    &self.config.available_artifacts,
2317                ) {
2318                    Ok((address, retdata)) => {
2319                        expected_revert.actual_count += 1;
2320                        if expected_revert.actual_count < expected_revert.count {
2321                            // Reset so the next iteration's innermost CREATE wins again.
2322                            expected_revert.reverted_by = None;
2323                            self.expected_revert = Some(expected_revert.clone());
2324                        }
2325
2326                        outcome.result.result = InstructionResult::Return;
2327                        outcome.result.output = retdata;
2328                        outcome.address = address;
2329                        self.gas_metering.last_frame_gas = None;
2330                    }
2331                    Err(err) => {
2332                        outcome.result.result = InstructionResult::Revert;
2333                        outcome.result.output = err.abi_encode().into();
2334                    }
2335                };
2336            }
2337        }
2338
2339        if curr_depth > 0 {
2340            // Record the gas usage of the create frame, this allows the `lastFrameGas` cheatcode to
2341            // retrieve the gas usage of the last call or create.
2342            let gas = outcome.result.gas;
2343            self.gas_metering.last_frame_gas = Some(crate::Vm::Gas {
2344                gasLimit: gas.limit(),
2345                gasTotalUsed: gas.total_gas_spent(),
2346                gasMemoryUsed: 0,
2347                gasRefunded: gas.refunded(),
2348                gasRemaining: gas.remaining(),
2349            });
2350        }
2351
2352        // If `startStateDiffRecording` has been called, update the `reverted` status of the
2353        // previous call depth's recorded accesses, if any
2354        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
2355            // The root call cannot be recorded.
2356            if curr_depth > 0
2357                && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
2358            {
2359                // Update the reverted status of all deeper calls if this call reverted, in
2360                // accordance with EVM behavior
2361                if outcome.result.is_revert() {
2362                    for element in &mut *last_depth {
2363                        element.reverted = true;
2364                        for storage_access in &mut element.storageAccesses {
2365                            storage_access.reverted = true;
2366                        }
2367                    }
2368                }
2369
2370                if let Some(create_access) = last_depth.first_mut() {
2371                    // Assert that we're at the correct depth before recording post-create state
2372                    // changes. Depending on what depth the cheat was called at, there
2373                    // may not be any pending calls to update if execution has
2374                    // percolated up to a higher depth.
2375                    let depth = ecx.journal().depth();
2376                    if create_access.depth == depth as u64 {
2377                        debug_assert_eq!(
2378                            create_access.kind as u8,
2379                            crate::Vm::AccountAccessKind::Create as u8
2380                        );
2381                        if let Some(address) = outcome.address
2382                            && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2383                        {
2384                            create_access.newBalance = created_acc.data.info.balance;
2385                            create_access.newNonce = created_acc.data.info.nonce;
2386                            create_access.deployedCode = created_acc
2387                                .data
2388                                .info
2389                                .code
2390                                .clone()
2391                                .unwrap_or_default()
2392                                .original_bytes();
2393                        }
2394                    }
2395                    // Merge the last depth's AccountAccesses into the AccountAccesses at the
2396                    // current depth, or push them back onto the pending
2397                    // vector if higher depths were not recorded. This
2398                    // preserves ordering of accesses.
2399                    if let Some(last) = recorded_account_diffs_stack.last_mut() {
2400                        last.append(last_depth);
2401                    } else {
2402                        recorded_account_diffs_stack.push(last_depth.clone());
2403                    }
2404                }
2405            }
2406        }
2407
2408        // Match the create against expected_creates
2409        if !self.expected_creates.is_empty()
2410            && let (Some(address), Some(call)) = (outcome.address, call)
2411            && let Ok(created_acc) = ecx.journal_mut().load_account(address)
2412        {
2413            let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
2414            if let Some((index, _)) =
2415                self.expected_creates.iter().find_position(|expected_create| {
2416                    expected_create.deployer == call.caller()
2417                        && expected_create.create_scheme.eq(call.scheme().into())
2418                        && expected_create.bytecode == bytecode
2419                })
2420            {
2421                self.expected_creates.swap_remove(index);
2422            }
2423        }
2424    }
2425}
2426
2427impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
2428    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
2429        let target_depth = if let Some(prank) = &self.get_prank(depth) {
2430            prank.depth
2431        } else if let Some(broadcast) = &self.broadcast {
2432            broadcast.depth
2433        } else {
2434            1
2435        };
2436
2437        if depth != target_depth {
2438            return false;
2439        }
2440
2441        match inputs.scheme() {
2442            CreateScheme::Create2 { .. } => {
2443                self.broadcast.is_some() || self.config.always_use_create_2_factory
2444            }
2445            CreateScheme::Create => self.config.batch_rewrite_creates && self.broadcast.is_some(),
2446            _ => false,
2447        }
2448    }
2449
2450    fn create2_deployer(&self) -> Address {
2451        self.config.evm_opts.create2_deployer
2452    }
2453}
2454
2455impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2456    #[cold]
2457    fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2458        if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2459            // Keep gas constant if paused.
2460            // Make sure we record the memory changes so that memory expansion is not paused.
2461            let memory = *interpreter.gas.memory();
2462            interpreter.gas = *paused_gas;
2463            interpreter.gas.memory_mut().words_num = memory.words_num;
2464            interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2465        } else {
2466            // Record frame paused gas.
2467            self.gas_metering.paused_frames.push(interpreter.gas);
2468        }
2469    }
2470
2471    #[cold]
2472    fn meter_gas_record(
2473        &mut self,
2474        interpreter: &mut Interpreter,
2475        ecx: &mut FoundryContextFor<'_, FEN>,
2476    ) {
2477        if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2478            self.gas_metering.gas_records.iter_mut().for_each(|record| {
2479                let curr_depth = ecx.journal().depth();
2480                if curr_depth == record.depth {
2481                    // Skip the first opcode of the first call frame as it includes the gas cost of
2482                    // creating the snapshot.
2483                    if self.gas_metering.last_gas_used != 0 {
2484                        let gas_diff = interpreter
2485                            .gas
2486                            .total_gas_spent()
2487                            .saturating_sub(self.gas_metering.last_gas_used);
2488                        record.gas_used = record.gas_used.saturating_add(gas_diff);
2489                    }
2490
2491                    // Update `last_gas_used` to the current spent gas for the next iteration to
2492                    // compare against.
2493                    self.gas_metering.last_gas_used = interpreter.gas.total_gas_spent();
2494                }
2495            });
2496        }
2497    }
2498
2499    #[cold]
2500    fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2501        // Remove recorded gas if we exit frame.
2502        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2503            && will_exit(interpreter_action)
2504        {
2505            self.gas_metering.paused_frames.pop();
2506        }
2507    }
2508
2509    #[cold]
2510    const fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2511        let mut gas = Gas::new(interpreter.gas.limit());
2512        gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2513        gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2514        interpreter.gas = gas;
2515        self.gas_metering.reset = false;
2516    }
2517
2518    #[cold]
2519    fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2520        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2521            && will_exit(interpreter_action)
2522        {
2523            // Reset gas if spent is less than refunded.
2524            // This can happen if gas was paused / resumed or reset.
2525            // https://github.com/foundry-rs/foundry/issues/4370
2526            if interpreter.gas.total_gas_spent()
2527                < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2528            {
2529                interpreter.gas = Gas::new(interpreter.gas.limit());
2530            }
2531        }
2532    }
2533
2534    /// Applies opcode-level overrides for `BASEFEE`, `GASPRICE` and `BLOBHASH`.
2535    ///
2536    /// Called from `step_end` *after* the opcode has executed and only when the
2537    /// opcode succeeded (the caller checks `instruction_result`). The opcode
2538    /// pushed its (possibly zeroed) result onto the stack; we replace the top
2539    /// of stack with the cheatcode-set override. This is what makes `vm.fee`,
2540    /// `vm.txGasPrice` and `vm.blobhashes` visible to called contracts under
2541    /// `--isolate` / `--gas-report`, where the inner transaction zeroes the
2542    /// real fee fields for fee-accounting purposes.
2543    ///
2544    /// We can't read the just-executed opcode from `interpreter.bytecode.opcode()`
2545    /// here because the PC has already advanced; instead `step` stashes it in
2546    /// `env_overrides.pending_opcode` for us.
2547    #[cold]
2548    fn apply_env_overrides(&mut self, interpreter: &mut Interpreter, fork_id: Option<U256>) {
2549        let Some(env_overrides) = self.env_overrides.get_mut(&fork_id) else { return };
2550        let Some(opcode) = env_overrides.pending_opcode.take() else { return };
2551        match opcode {
2552            op::BASEFEE => {
2553                if let Some(basefee) = env_overrides.basefee {
2554                    // BASEFEE pushed one value; replace it.
2555                    Self::replace_top_of_stack(interpreter, U256::from(basefee));
2556                }
2557            }
2558            op::GASPRICE => {
2559                if let Some(gas_price) = env_overrides.gas_price {
2560                    // GASPRICE pushed one value; replace it.
2561                    Self::replace_top_of_stack(interpreter, U256::from(gas_price));
2562                }
2563            }
2564            op::BLOBHASH => {
2565                let blob_hashes = env_overrides.blob_hashes.clone();
2566                let blobhash_index = env_overrides.pending_blobhash_index.take();
2567                if let Some(ref blob_hashes) = blob_hashes
2568                    && let Some(index) = blobhash_index
2569                {
2570                    // BLOBHASH popped the index and pushed the hash; replace
2571                    // the hash with our override (zero for out-of-range, per EIP-4844).
2572                    let hash = blob_hashes.get(index as usize).copied().unwrap_or_default();
2573                    Self::replace_top_of_stack(interpreter, hash.into());
2574                }
2575            }
2576            _ => {}
2577        }
2578    }
2579
2580    /// Replaces the top of the interpreter stack with `value`.
2581    ///
2582    /// The caller must only invoke this after a successful opcode that pushed
2583    /// a value onto the stack; the `pop()` is therefore expected to succeed.
2584    /// If it does not (e.g. because of a bug in the caller's success gating)
2585    /// we bail out instead of pushing on top of an unexpected stack, which
2586    /// would silently grow the stack and corrupt the frame.
2587    fn replace_top_of_stack(interpreter: &mut Interpreter, value: U256) {
2588        if interpreter.stack.pop().is_err() {
2589            debug_assert!(false, "env override expected opcode result on stack");
2590            return;
2591        }
2592        let _ = interpreter.stack.push(value);
2593    }
2594
2595    /// Generates or copies arbitrary values for storage slots.
2596    /// Invoked in inspector `step_end` (when the current opcode is not executed), if current opcode
2597    /// to execute is `SLOAD` and storage slot is cold.
2598    /// Ensures that in next step (when `SLOAD` opcode is executed) an arbitrary value is returned:
2599    /// - copies the existing arbitrary storage value (or the new generated one if no value in
2600    ///   cache) from mapped source address to the target address.
2601    /// - generates arbitrary value and saves it in target address storage.
2602    #[cold]
2603    fn arbitrary_storage_end(
2604        &mut self,
2605        interpreter: &mut Interpreter,
2606        ecx: &mut FoundryContextFor<'_, FEN>,
2607    ) {
2608        let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2609            (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2610        } else {
2611            return;
2612        };
2613
2614        let Some(value) = ecx.sload(target_address, key) else {
2615            return;
2616        };
2617
2618        if (value.is_cold && value.data.is_zero())
2619            || self.should_overwrite_arbitrary_storage(&target_address, key)
2620        {
2621            if self.has_arbitrary_storage(&target_address) {
2622                let arbitrary_value = self
2623                    .cached_arbitrary_storage_value(target_address, key)
2624                    .unwrap_or_else(|| self.rng().random());
2625                self.arbitrary_storage.as_mut().unwrap().save(
2626                    ecx,
2627                    target_address,
2628                    key,
2629                    arbitrary_value,
2630                );
2631            } else if self.is_arbitrary_storage_copy(&target_address) {
2632                let arbitrary_value = self.rng().random();
2633                self.arbitrary_storage.as_mut().unwrap().copy(
2634                    ecx,
2635                    target_address,
2636                    key,
2637                    arbitrary_value,
2638                );
2639            }
2640        }
2641    }
2642
2643    /// Records storage slots reads and writes.
2644    #[cold]
2645    fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2646        let access = &mut self.accesses;
2647        match interpreter.bytecode.opcode() {
2648            op::SLOAD => {
2649                let key = try_or_return!(interpreter.stack.peek(0));
2650                access.record_read(interpreter.input.target_address, key);
2651            }
2652            op::SSTORE => {
2653                let key = try_or_return!(interpreter.stack.peek(0));
2654                access.record_write(interpreter.input.target_address, key);
2655            }
2656            _ => {}
2657        }
2658    }
2659
2660    #[cold]
2661    fn record_state_diffs(
2662        &mut self,
2663        interpreter: &mut Interpreter,
2664        ecx: &mut FoundryContextFor<'_, FEN>,
2665    ) {
2666        let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2667        match interpreter.bytecode.opcode() {
2668            op::SELFDESTRUCT => {
2669                // Ensure that we're not selfdestructing a context recording was initiated on
2670                let Some(last) = account_accesses.last_mut() else { return };
2671
2672                // get previous balance, nonce and initialized status of the target account
2673                let target = try_or_return!(interpreter.stack.peek(0));
2674                let target = Address::from_word(B256::from(target));
2675                let (initialized, old_balance, old_nonce) = ecx
2676                    .journal_mut()
2677                    .load_account(target)
2678                    .map(|account| {
2679                        (
2680                            account.data.info.exists(),
2681                            account.data.info.balance,
2682                            account.data.info.nonce,
2683                        )
2684                    })
2685                    .unwrap_or_default();
2686
2687                // load balance of this account
2688                let value = ecx
2689                    .balance(interpreter.input.target_address)
2690                    .map(|b| b.data)
2691                    .unwrap_or(U256::ZERO);
2692
2693                // register access for the target account
2694                last.push(crate::Vm::AccountAccess {
2695                    chainInfo: crate::Vm::ChainInfo {
2696                        forkId: ecx.db().active_fork_id().unwrap_or_default(),
2697                        chainId: U256::from(ecx.cfg().chain_id()),
2698                    },
2699                    accessor: interpreter.input.target_address,
2700                    account: target,
2701                    kind: crate::Vm::AccountAccessKind::SelfDestruct,
2702                    initialized,
2703                    oldBalance: old_balance,
2704                    newBalance: old_balance + value,
2705                    oldNonce: old_nonce,
2706                    newNonce: old_nonce, // nonce doesn't change on selfdestruct
2707                    value,
2708                    data: Bytes::new(),
2709                    reverted: false,
2710                    deployedCode: Bytes::new(),
2711                    storageAccesses: vec![],
2712                    depth: ecx
2713                        .journal()
2714                        .depth()
2715                        .try_into()
2716                        .expect("journaled state depth exceeds u64"),
2717                });
2718            }
2719
2720            op::SLOAD => {
2721                let Some(last) = account_accesses.last_mut() else { return };
2722
2723                let key = try_or_return!(interpreter.stack.peek(0));
2724                let address = interpreter.input.target_address;
2725
2726                // Try to include present value for informational purposes, otherwise assume
2727                // it's not set (zero value). Revert the checkpoint so this read does not warm the
2728                // slot for the actual SLOAD opcode.
2729                let checkpoint = ecx.journal_mut().checkpoint();
2730                let present_value =
2731                    ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
2732                ecx.journal_mut().checkpoint_revert(checkpoint);
2733                let access = crate::Vm::StorageAccess {
2734                    account: interpreter.input.target_address,
2735                    slot: key.into(),
2736                    isWrite: false,
2737                    previousValue: present_value.into(),
2738                    newValue: present_value.into(),
2739                    reverted: false,
2740                };
2741                let curr_depth =
2742                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2743                append_storage_access(last, access, curr_depth);
2744            }
2745            op::SSTORE => {
2746                let Some(last) = account_accesses.last_mut() else { return };
2747
2748                let key = try_or_return!(interpreter.stack.peek(0));
2749                let value = try_or_return!(interpreter.stack.peek(1));
2750                let address = interpreter.input.target_address;
2751                // Try to load the account and the slot's previous value, otherwise, assume it's
2752                // not set (zero value). Revert the checkpoint so this read does not warm the slot
2753                // for the actual SSTORE opcode.
2754                let checkpoint = ecx.journal_mut().checkpoint();
2755                let previous_value =
2756                    ecx.sload(address, key).map(|previous| previous.data).unwrap_or_default();
2757                ecx.journal_mut().checkpoint_revert(checkpoint);
2758
2759                let access = crate::Vm::StorageAccess {
2760                    account: address,
2761                    slot: key.into(),
2762                    isWrite: true,
2763                    previousValue: previous_value.into(),
2764                    newValue: value.into(),
2765                    reverted: false,
2766                };
2767                let curr_depth =
2768                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2769                append_storage_access(last, access, curr_depth);
2770            }
2771
2772            // Record account accesses via the EXT family of opcodes
2773            op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2774                let kind = match interpreter.bytecode.opcode() {
2775                    op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2776                    op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2777                    op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2778                    op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2779                    _ => unreachable!(),
2780                };
2781                let address =
2782                    Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2783                let checkpoint = ecx.journal_mut().checkpoint();
2784                let (initialized, balance, nonce) = ecx
2785                    .journal_mut()
2786                    .load_account(address)
2787                    .map(|acc| (acc.data.info.exists(), acc.data.info.balance, acc.data.info.nonce))
2788                    .unwrap_or_default();
2789                ecx.journal_mut().checkpoint_revert(checkpoint);
2790                let curr_depth =
2791                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2792                let account_access = crate::Vm::AccountAccess {
2793                    chainInfo: crate::Vm::ChainInfo {
2794                        forkId: ecx.db().active_fork_id().unwrap_or_default(),
2795                        chainId: U256::from(ecx.cfg().chain_id()),
2796                    },
2797                    accessor: interpreter.input.target_address,
2798                    account: address,
2799                    kind,
2800                    initialized,
2801                    oldBalance: balance,
2802                    newBalance: balance,
2803                    oldNonce: nonce,
2804                    newNonce: nonce, // EXT* operations don't change nonce
2805                    value: U256::ZERO,
2806                    data: Bytes::new(),
2807                    reverted: false,
2808                    deployedCode: Bytes::new(),
2809                    storageAccesses: vec![],
2810                    depth: curr_depth,
2811                };
2812                // Record the EXT* call as an account access at the current depth
2813                // (future storage accesses will be recorded in a new "Resume" context)
2814                if let Some(last) = account_accesses.last_mut() {
2815                    last.push(account_access);
2816                } else {
2817                    account_accesses.push(vec![account_access]);
2818                }
2819            }
2820            _ => {}
2821        }
2822    }
2823
2824    /// Checks to see if the current opcode can either mutate directly or expand memory.
2825    ///
2826    /// If the opcode at the current program counter is a match, check if the modified memory lies
2827    /// within the allowed ranges. If not, revert and fail the test.
2828    #[cold]
2829    fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2830        let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2831            return;
2832        };
2833
2834        // The `mem_opcode_match` macro is used to match the current opcode against a list of
2835        // opcodes that can mutate memory (either directly or expansion via reading). If the
2836        // opcode is a match, the memory offsets that are being written to are checked to be
2837        // within the allowed ranges. If not, the test is failed and the transaction is
2838        // reverted. For all opcodes that can mutate memory aside from MSTORE,
2839        // MSTORE8, and MLOAD, the size and destination offset are on the stack, and
2840        // the macro expands all of these cases. For MSTORE, MSTORE8, and MLOAD, the
2841        // size of the memory write is implicit, so these cases are hard-coded.
2842        macro_rules! mem_opcode_match {
2843            ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2844                match interpreter.bytecode.opcode() {
2845                    ////////////////////////////////////////////////////////////////
2846                    //    OPERATIONS THAT CAN EXPAND/MUTATE MEMORY BY WRITING     //
2847                    ////////////////////////////////////////////////////////////////
2848
2849                    op::MSTORE => {
2850                        // The offset of the mstore operation is at the top of the stack.
2851                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2852
2853                        // If none of the allowed ranges contain [offset, offset + 32), memory has been
2854                        // unexpectedly mutated.
2855                        if !ranges.iter().any(|range| {
2856                            range.contains(&offset) && range.contains(&(offset + 31))
2857                        }) {
2858                            // SPECIAL CASE: When the compiler attempts to store the selector for
2859                            // `stopExpectSafeMemory`, this is allowed. It will do so at the current free memory
2860                            // pointer, which could have been updated to the exclusive upper bound during
2861                            // execution.
2862                            let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2863                            if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2864                                return
2865                            }
2866
2867                            disallowed_mem_write(offset, 32, interpreter, ranges);
2868                            return
2869                        }
2870                    }
2871                    op::MSTORE8 => {
2872                        // The offset of the mstore8 operation is at the top of the stack.
2873                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2874
2875                        // If none of the allowed ranges contain the offset, memory has been
2876                        // unexpectedly mutated.
2877                        if !ranges.iter().any(|range| range.contains(&offset)) {
2878                            disallowed_mem_write(offset, 1, interpreter, ranges);
2879                            return
2880                        }
2881                    }
2882
2883                    ////////////////////////////////////////////////////////////////
2884                    //        OPERATIONS THAT CAN EXPAND MEMORY BY READING        //
2885                    ////////////////////////////////////////////////////////////////
2886
2887                    op::MLOAD => {
2888                        // The offset of the mload operation is at the top of the stack
2889                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2890
2891                        // If the offset being loaded is >= than the memory size, the
2892                        // memory is being expanded. If none of the allowed ranges contain
2893                        // [offset, offset + 32), memory has been unexpectedly mutated.
2894                        if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2895                            range.contains(&offset) && range.contains(&(offset + 31))
2896                        }) {
2897                            disallowed_mem_write(offset, 32, interpreter, ranges);
2898                            return
2899                        }
2900                    }
2901
2902                    ////////////////////////////////////////////////////////////////
2903                    //          OPERATIONS WITH OFFSET AND SIZE ON STACK          //
2904                    ////////////////////////////////////////////////////////////////
2905
2906                    op::CALL => {
2907                        // The destination offset of the operation is the fifth element on the stack.
2908                        let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2909
2910                        // The size of the data that will be copied is the sixth element on the stack.
2911                        let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2912
2913                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
2914                        // memory outside of the expected ranges has been touched. If the opcode
2915                        // only reads from memory, this is okay as long as the memory is not expanded.
2916                        let fail_cond = !ranges.iter().any(|range| {
2917                            range.contains(&dest_offset) &&
2918                                range.contains(&(dest_offset + size.saturating_sub(1)))
2919                        });
2920
2921                        // If the failure condition is met, set the output buffer to a revert string
2922                        // that gives information about the allowed ranges and revert.
2923                        if fail_cond {
2924                            // SPECIAL CASE: When a call to `stopExpectSafeMemory` is performed, this is allowed.
2925                            // It allocated calldata at the current free memory pointer, and will attempt to read
2926                            // from this memory region to perform the call.
2927                            let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2928                            if to == CHEATCODE_ADDRESS {
2929                                let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2930                                let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2931                                let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2932                                if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2933                                    return
2934                                }
2935                            }
2936
2937                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
2938                            return
2939                        }
2940                    }
2941
2942                    $(op::$opcode => {
2943                        // The destination offset of the operation.
2944                        let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2945
2946                        // The size of the data that will be copied.
2947                        let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2948
2949                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
2950                        // memory outside of the expected ranges has been touched. If the opcode
2951                        // only reads from memory, this is okay as long as the memory is not expanded.
2952                        let fail_cond = !ranges.iter().any(|range| {
2953                                range.contains(&dest_offset) &&
2954                                    range.contains(&(dest_offset + size.saturating_sub(1)))
2955                            }) && ($writes ||
2956                                [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2957                                    offset >= interpreter.memory.size() as u64
2958                                })
2959                            );
2960
2961                        // If the failure condition is met, set the output buffer to a revert string
2962                        // that gives information about the allowed ranges and revert.
2963                        if fail_cond {
2964                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
2965                            return
2966                        }
2967                    })*
2968
2969                    _ => {}
2970                }
2971            }
2972        }
2973
2974        // Check if the current opcode can write to memory, and if so, check if the memory
2975        // being written to is registered as safe to modify.
2976        mem_opcode_match!(
2977            (CALLDATACOPY, 0, 2, true),
2978            (CODECOPY, 0, 2, true),
2979            (RETURNDATACOPY, 0, 2, true),
2980            (EXTCODECOPY, 1, 3, true),
2981            (CALLCODE, 5, 6, true),
2982            (STATICCALL, 4, 5, true),
2983            (DELEGATECALL, 4, 5, true),
2984            (KECCAK256, 0, 1, false),
2985            (LOG0, 0, 1, false),
2986            (LOG1, 0, 1, false),
2987            (LOG2, 0, 1, false),
2988            (LOG3, 0, 1, false),
2989            (LOG4, 0, 1, false),
2990            (CREATE, 1, 2, false),
2991            (CREATE2, 1, 2, false),
2992            (RETURN, 0, 1, false),
2993            (REVERT, 0, 1, false),
2994        );
2995    }
2996
2997    #[cold]
2998    fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2999        match interpreter.bytecode.opcode() {
3000            op::CREATE2 => self.dynamic_gas_limit = true,
3001            op::CALL => {
3002                // If first element of the stack is close to current remaining gas then assume
3003                // dynamic gas limit.
3004                self.dynamic_gas_limit =
3005                    try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
3006            }
3007            _ => self.dynamic_gas_limit = false,
3008        }
3009    }
3010}
3011
3012/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
3013/// and sets the return range to the revert string's location in memory.
3014///
3015/// This will set the interpreter's next action to a return with the revert string as the output.
3016/// And trigger a revert.
3017fn disallowed_mem_write(
3018    dest_offset: u64,
3019    size: u64,
3020    interpreter: &mut Interpreter,
3021    ranges: &[Range<u64>],
3022) {
3023    let revert_string = format!(
3024        "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
3025        dest_offset,
3026        size,
3027        ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
3028    );
3029
3030    interpreter.bytecode.set_action(InterpreterAction::new_return(
3031        InstructionResult::Revert,
3032        Bytes::from(revert_string.into_bytes()),
3033        interpreter.gas,
3034    ));
3035}
3036
3037/// Returns true if the kind of account access is a call.
3038const fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
3039    matches!(
3040        kind,
3041        crate::Vm::AccountAccessKind::Call
3042            | crate::Vm::AccountAccessKind::StaticCall
3043            | crate::Vm::AccountAccessKind::CallCode
3044            | crate::Vm::AccountAccessKind::DelegateCall
3045    )
3046}
3047
3048/// Records a log into the recorded logs vector, if it exists.
3049fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
3050    if let Some(storage_recorded_logs) = recorded_logs {
3051        storage_recorded_logs.push(Vm::Log {
3052            topics: log.data.topics().to_vec(),
3053            data: log.data.data.clone(),
3054            emitter: log.address,
3055        });
3056    }
3057}
3058
3059/// Appends an AccountAccess that resumes the recording of the current context.
3060fn append_storage_access(
3061    last: &mut Vec<AccountAccess>,
3062    storage_access: crate::Vm::StorageAccess,
3063    storage_depth: u64,
3064) {
3065    // Assert that there's an existing record for the current context.
3066    if !last.is_empty() && last.first().unwrap().depth < storage_depth {
3067        // Three cases to consider:
3068        // 1. If there hasn't been a context switch since the start of this context, then add the
3069        //    storage access to the current context record.
3070        // 2. If there's an existing Resume record, then add the storage access to it.
3071        // 3. Otherwise, create a new Resume record based on the current context.
3072        if last.len() == 1 {
3073            last.first_mut().unwrap().storageAccesses.push(storage_access);
3074        } else {
3075            let last_record = last.last_mut().unwrap();
3076            if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
3077                last_record.storageAccesses.push(storage_access);
3078            } else {
3079                let entry = last.first().unwrap();
3080                let resume_record = crate::Vm::AccountAccess {
3081                    chainInfo: crate::Vm::ChainInfo {
3082                        forkId: entry.chainInfo.forkId,
3083                        chainId: entry.chainInfo.chainId,
3084                    },
3085                    accessor: entry.accessor,
3086                    account: entry.account,
3087                    kind: crate::Vm::AccountAccessKind::Resume,
3088                    initialized: entry.initialized,
3089                    storageAccesses: vec![storage_access],
3090                    reverted: entry.reverted,
3091                    // The remaining fields are defaults
3092                    oldBalance: U256::ZERO,
3093                    newBalance: U256::ZERO,
3094                    oldNonce: 0,
3095                    newNonce: 0,
3096                    value: U256::ZERO,
3097                    data: Bytes::new(),
3098                    deployedCode: Bytes::new(),
3099                    depth: entry.depth,
3100                };
3101                last.push(resume_record);
3102            }
3103        }
3104    }
3105}
3106
3107/// Returns the [`spec::Cheatcode`] definition for a given [`spec::CheatcodeDef`] implementor.
3108const fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
3109    T::CHEATCODE
3110}
3111
3112fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
3113    cheat.func.signature.split('(').next().unwrap()
3114}
3115
3116const fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
3117    cheat.func.id
3118}
3119
3120const fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
3121    cheat.func.signature
3122}
3123
3124/// Dispatches the cheatcode call to the appropriate function.
3125fn apply_dispatch<FEN: FoundryEvmNetwork>(
3126    calls: &Vm::VmCalls,
3127    ccx: &mut CheatsCtxt<'_, '_, FEN>,
3128    executor: &mut dyn CheatcodesExecutor<FEN>,
3129) -> Result {
3130    // Extract metadata for logging/deprecation via CheatcodeDef.
3131    macro_rules! get_cheatcode {
3132        ($($variant:ident),*) => {
3133            match calls {
3134                $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
3135            }
3136        };
3137    }
3138    let cheat = vm_calls!(get_cheatcode);
3139
3140    let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
3141    trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
3142
3143    if let spec::Status::Deprecated(replacement) = cheat.status {
3144        ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
3145    }
3146
3147    // Monomorphized dispatch: calls apply_full directly, no trait objects.
3148    macro_rules! dispatch {
3149        ($($variant:ident),*) => {
3150            match calls {
3151                $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
3152            }
3153        };
3154    }
3155    let mut result = vm_calls!(dispatch);
3156
3157    // Format the error message to include the cheatcode name.
3158    if let Err(e) = &mut result
3159        && e.is_str()
3160    {
3161        let name = cheatcode_name(cheat);
3162        // Skip showing the cheatcode name for:
3163        // - assertions: too verbose, and can already be inferred from the error message
3164        // - `rpcUrl`: forge-std relies on it in `getChainWithUpdatedRpcUrl`
3165        if !name.contains("assert") && name != "rpcUrl" {
3166            *e = fmt_err!("vm.{name}: {e}");
3167        }
3168    }
3169
3170    trace!(
3171        target: "cheatcodes",
3172        return = %match &result {
3173            Ok(b) => hex::encode(b),
3174            Err(e) => e.to_string(),
3175        }
3176    );
3177
3178    result
3179}
3180
3181/// Helper function to check if frame execution will exit.
3182const fn will_exit(action: &InterpreterAction) -> bool {
3183    match action {
3184        InterpreterAction::Return(result) => {
3185            result.result.is_ok_or_revert() || result.result.is_halt()
3186        }
3187        _ => false,
3188    }
3189}
3190
3191#[cfg(test)]
3192mod tests {
3193    use super::*;
3194
3195    fn cheats(flag: bool, broadcast: Option<Broadcast>) -> Cheatcodes {
3196        let config = CheatsConfig { batch_rewrite_creates: flag, ..Default::default() };
3197        let mut cheats = Cheatcodes::new(Arc::new(config));
3198        cheats.broadcast = broadcast;
3199        cheats
3200    }
3201
3202    fn create_inputs() -> CreateInputs {
3203        CreateInputs::new(Address::ZERO, CreateScheme::Create, U256::ZERO, Bytes::new(), 100_000, 0)
3204    }
3205
3206    fn broadcast_at(depth: usize) -> Broadcast {
3207        Broadcast { depth, ..Default::default() }
3208    }
3209
3210    #[test]
3211    fn flag_off_with_broadcast_returns_false() {
3212        let mut cheats = cheats(false, Some(broadcast_at(1)));
3213        assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3214    }
3215
3216    #[test]
3217    fn flag_on_without_broadcast_returns_false() {
3218        let mut cheats = cheats(true, None);
3219        assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3220    }
3221
3222    #[test]
3223    fn flag_on_with_broadcast_depth_mismatch_returns_false() {
3224        let mut cheats = cheats(true, Some(broadcast_at(2)));
3225        assert!(!cheats.should_use_create2_factory(1, &create_inputs()));
3226    }
3227
3228    #[test]
3229    fn flag_on_with_broadcast_depth_match_returns_true() {
3230        let mut cheats = cheats(true, Some(broadcast_at(1)));
3231        assert!(cheats.should_use_create2_factory(1, &create_inputs()));
3232    }
3233
3234    #[test]
3235    fn default_cheatcodes_have_no_opcode_hooks() {
3236        let cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3237        assert!(!cheats.has_step_hooks());
3238        assert!(!cheats.has_step_end_hooks());
3239        assert!(!cheats.has_log_hooks());
3240    }
3241
3242    #[test]
3243    fn active_cheatcode_state_enables_opcode_hooks() {
3244        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3245
3246        cheats.recording_accesses = true;
3247        assert!(cheats.has_step_hooks());
3248        assert!(!cheats.has_step_end_hooks());
3249        assert!(cheats.has_recording_accesses_only_step_hook());
3250
3251        cheats.recording_accesses = false;
3252        cheats.gas_metering.touched = true;
3253        assert!(!cheats.has_step_hooks());
3254        assert!(cheats.has_step_end_hooks());
3255        assert!(!cheats.has_recording_accesses_only_step_hook());
3256    }
3257
3258    #[test]
3259    fn mixed_step_hooks_disable_record_access_fast_path() {
3260        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3261        cheats.recording_accesses = true;
3262
3263        cheats.gas_metering.reset = true;
3264        assert!(!cheats.has_recording_accesses_only_step_hook());
3265
3266        cheats.gas_metering.reset = false;
3267        cheats.env_overrides.insert(None, EnvOverrides { basefee: Some(1), ..Default::default() });
3268        assert!(!cheats.has_recording_accesses_only_step_hook());
3269    }
3270
3271    #[test]
3272    fn inactive_env_override_entries_do_not_enable_opcode_hooks() {
3273        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3274        cheats.env_overrides.insert(None, EnvOverrides::default());
3275
3276        assert!(!cheats.has_step_hooks());
3277        assert!(!cheats.has_step_end_hooks());
3278
3279        cheats.env_overrides.get_mut(&None).unwrap().basefee = Some(1);
3280        assert!(cheats.has_step_hooks());
3281        assert!(cheats.has_step_end_hooks());
3282    }
3283
3284    #[test]
3285    fn active_log_state_enables_log_hooks() {
3286        let mut cheats = Cheatcodes::<EthEvmNetwork>::new(Arc::default());
3287
3288        cheats.recorded_logs = Some(Default::default());
3289        assert!(cheats.has_log_hooks());
3290
3291        cheats.recorded_logs = None;
3292        cheats.expected_emits.push_back((
3293            expect::ExpectedEmit {
3294                depth: 0,
3295                log: None,
3296                checks: [false; 5],
3297                address: None,
3298                anonymous: false,
3299                found: false,
3300                count: 1,
3301                mismatch_error: None,
3302            },
3303            Default::default(),
3304        ));
3305        assert!(cheats.has_log_hooks());
3306    }
3307
3308    #[test]
3309    fn arbitrary_storage_cache_value_routes_copied_targets_to_source() {
3310        let mut storage = ArbitraryStorage::default();
3311        let source = Address::repeat_byte(0x11);
3312        let copied = Address::repeat_byte(0x22);
3313        let slot = U256::from(7);
3314
3315        storage.mark_arbitrary(&source, false);
3316        storage.mark_copy(&source, &copied);
3317        storage.cache_value(copied, slot, U256::ZERO);
3318
3319        assert_eq!(storage.cached_value(source, slot), Some(U256::ZERO));
3320    }
3321}