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_sol_types::{SolCall, SolInterface, SolValue};
31use foundry_common::{
32    FoundryTransactionBuilder, SELECTOR_LEN, TransactionMaybeSigned,
33    mapping_slots::{MappingSlots, step as mapping_step},
34};
35use foundry_evm_core::{
36    Breakpoints, EvmEnv, FoundryTransaction, InspectorExt,
37    abi::Vm::stopExpectSafeMemoryCall,
38    backend::{DatabaseError, DatabaseExt, RevertDiagnostic},
39    constants::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME},
40    env::FoundryContextExt,
41    evm::{
42        BlockEnvFor, EthEvmNetwork, FoundryContextFor, FoundryEvmFactory, FoundryEvmNetwork,
43        IntoNestedEvm, NestedEvm, NestedEvmClosure, SpecFor, TransactionRequestFor, TxEnvFor,
44        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    inspector::JournalExt,
61    interpreter::{
62        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
63        InstructionResult, Interpreter, InterpreterAction, InterpreterResult,
64        interpreter_types::{Jumps, LoopControl, MemoryTr},
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()
180                .create_foundry_evm_with_inspector(db, evm_env, cheats)
181                .into_nested_evm();
182            *evm.journal_inner_mut() = journal_inner;
183            f(&mut evm)?;
184            let sub_inner = evm.journal_inner_mut().clone();
185            let sub_evm_env = evm.to_evm_env();
186            Ok((sub_evm_env, sub_inner))
187        })
188    }
189
190    fn with_fresh_nested_evm(
191        &mut self,
192        cheats: &mut Cheatcodes<FEN>,
193        db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
194        evm_env: EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>,
195        f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
196    ) -> Result<EvmEnv<SpecFor<FEN>, BlockEnvFor<FEN>>, EVMError<DatabaseError>> {
197        let mut evm = FEN::EvmFactory::default()
198            .create_foundry_evm_with_inspector(db, evm_env, cheats)
199            .into_nested_evm();
200        f(&mut evm)?;
201        Ok(evm.to_evm_env())
202    }
203
204    fn transact_on_db(
205        &mut self,
206        cheats: &mut Cheatcodes<FEN>,
207        ecx: &mut FoundryContextFor<'_, FEN>,
208        fork_id: Option<U256>,
209        transaction: B256,
210    ) -> eyre::Result<()> {
211        let evm_env = ecx.evm_clone();
212        let (db, inner) = ecx.db_journal_inner_mut();
213        db.transact(fork_id, transaction, evm_env, inner, cheats)
214    }
215
216    fn transact_from_tx_on_db(
217        &mut self,
218        cheats: &mut Cheatcodes<FEN>,
219        ecx: &mut FoundryContextFor<'_, FEN>,
220        tx: TxEnvFor<FEN>,
221    ) -> eyre::Result<()> {
222        let evm_env = ecx.evm_clone();
223        let (db, inner) = ecx.db_journal_inner_mut();
224        db.transact_from_tx(tx, evm_env, inner, cheats)
225    }
226
227    fn console_log(&mut self, _msg: &str) {}
228}
229
230macro_rules! try_or_return {
231    ($e:expr) => {
232        match $e {
233            Ok(v) => v,
234            Err(_) => return,
235        }
236    };
237}
238
239/// Contains additional, test specific resources that should be kept for the duration of the test
240#[derive(Debug, Default)]
241pub struct TestContext {
242    /// Buffered readers for files opened for reading (path => BufReader mapping)
243    pub opened_read_files: HashMap<PathBuf, BufReader<File>>,
244}
245
246/// Every time we clone `Context`, we want it to be empty
247impl Clone for TestContext {
248    fn clone(&self) -> Self {
249        Default::default()
250    }
251}
252
253impl TestContext {
254    /// Clears the context.
255    pub fn clear(&mut self) {
256        self.opened_read_files.clear();
257    }
258}
259
260/// Helps collecting transactions from different forks.
261#[derive(Clone, Debug)]
262pub struct BroadcastableTransaction<N: Network = Ethereum> {
263    /// The optional RPC URL.
264    pub rpc: Option<String>,
265    /// The transaction to broadcast.
266    pub transaction: TransactionMaybeSigned<N>,
267}
268
269#[derive(Clone, Debug, Copy)]
270pub struct RecordDebugStepInfo {
271    /// The debug trace node index when the recording starts.
272    pub start_node_idx: usize,
273    /// The original tracer config when the recording starts.
274    pub original_tracer_config: TracingInspectorConfig,
275}
276
277/// Holds gas metering state.
278#[derive(Clone, Debug, Default)]
279pub struct GasMetering {
280    /// True if gas metering is paused.
281    pub paused: bool,
282    /// True if gas metering was resumed or reset during the test.
283    /// Used to reconcile gas when frame ends (if spent less than refunded).
284    pub touched: bool,
285    /// True if gas metering should be reset to frame limit.
286    pub reset: bool,
287    /// Stores paused gas frames.
288    pub paused_frames: Vec<Gas>,
289
290    /// The group and name of the active snapshot.
291    pub active_gas_snapshot: Option<(String, String)>,
292
293    /// Cache of the amount of gas used in previous call.
294    /// This is used by the `lastCallGas` cheatcode.
295    pub last_call_gas: Option<crate::Vm::Gas>,
296
297    /// True if gas recording is enabled.
298    pub recording: bool,
299    /// The gas used in the last frame.
300    pub last_gas_used: u64,
301    /// Gas records for the active snapshots.
302    pub gas_records: Vec<GasRecord>,
303}
304
305impl GasMetering {
306    /// Start the gas recording.
307    pub fn start(&mut self) {
308        self.recording = true;
309    }
310
311    /// Stop the gas recording.
312    pub fn stop(&mut self) {
313        self.recording = false;
314    }
315
316    /// Resume paused gas metering.
317    pub fn resume(&mut self) {
318        if self.paused {
319            self.paused = false;
320            self.touched = true;
321        }
322        self.paused_frames.clear();
323    }
324
325    /// Reset gas to limit.
326    pub fn reset(&mut self) {
327        self.paused = false;
328        self.touched = true;
329        self.reset = true;
330        self.paused_frames.clear();
331    }
332}
333
334/// Holds data about arbitrary storage.
335#[derive(Clone, Debug, Default)]
336pub struct ArbitraryStorage {
337    /// Mapping of arbitrary storage addresses to generated values (slot, arbitrary value).
338    /// (SLOADs return random value if storage slot wasn't accessed).
339    /// Changed values are recorded and used to copy storage to different addresses.
340    pub values: HashMap<Address, HashMap<U256, U256>>,
341    /// Mapping of address with storage copied to arbitrary storage address source.
342    pub copies: HashMap<Address, Address>,
343    /// Address with storage slots that should be overwritten even if previously set.
344    pub overwrites: HashSet<Address>,
345}
346
347impl ArbitraryStorage {
348    /// Marks an address with arbitrary storage.
349    pub fn mark_arbitrary(&mut self, address: &Address, overwrite: bool) {
350        self.values.insert(*address, HashMap::default());
351        if overwrite {
352            self.overwrites.insert(*address);
353        } else {
354            self.overwrites.remove(address);
355        }
356    }
357
358    /// Maps an address that copies storage with the arbitrary storage address.
359    pub fn mark_copy(&mut self, from: &Address, to: &Address) {
360        if self.values.contains_key(from) {
361            self.copies.insert(*to, *from);
362        }
363    }
364
365    /// Saves arbitrary storage value for a given address:
366    /// - store value in changed values cache.
367    /// - update account's storage with given value.
368    pub fn save<CTX: ContextTr>(
369        &mut self,
370        ecx: &mut CTX,
371        address: Address,
372        slot: U256,
373        data: U256,
374    ) {
375        self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data);
376        if ecx.journal_mut().load_account(address).is_ok() {
377            ecx.journal_mut()
378                .sstore(address, slot, data)
379                .expect("could not set arbitrary storage value");
380        }
381    }
382
383    /// Copies arbitrary storage value from source address to the given target address:
384    /// - if a value is present in arbitrary values cache, then update target storage and return
385    ///   existing value.
386    /// - if no value was yet generated for given slot, then save new value in cache and update both
387    ///   source and target storages.
388    pub fn copy<CTX: ContextTr>(
389        &mut self,
390        ecx: &mut CTX,
391        target: Address,
392        slot: U256,
393        new_value: U256,
394    ) -> U256 {
395        let source = self.copies.get(&target).expect("missing arbitrary copy target entry");
396        let storage_cache = self.values.get_mut(source).expect("missing arbitrary source storage");
397        let value = match storage_cache.get(&slot) {
398            Some(value) => *value,
399            None => {
400                storage_cache.insert(slot, new_value);
401                // Update source storage with new value.
402                if ecx.journal_mut().load_account(*source).is_ok() {
403                    ecx.journal_mut()
404                        .sstore(*source, slot, new_value)
405                        .expect("could not copy arbitrary storage value");
406                }
407                new_value
408            }
409        };
410        // Update target storage with new value.
411        if ecx.journal_mut().load_account(target).is_ok() {
412            ecx.journal_mut().sstore(target, slot, value).expect("could not set storage");
413        }
414        value
415    }
416}
417
418/// List of transactions that can be broadcasted.
419pub type BroadcastableTransactions<N> = VecDeque<BroadcastableTransaction<N>>;
420
421/// An EVM inspector that handles calls to various cheatcodes, each with their own behavior.
422///
423/// Cheatcodes can be called by contracts during execution to modify the VM environment, such as
424/// mocking addresses, signatures and altering call reverts.
425///
426/// Executing cheatcodes can be very powerful. Most cheatcodes are limited to evm internals, but
427/// there are also cheatcodes like `ffi` which can execute arbitrary commands or `writeFile` and
428/// `readFile` which can manipulate files of the filesystem. Therefore, several restrictions are
429/// implemented for these cheatcodes:
430/// - `ffi`, and file cheatcodes are _always_ opt-in (via foundry config) and never enabled by
431///   default: all respective cheatcode handlers implement the appropriate checks
432/// - File cheatcodes require explicit permissions which paths are allowed for which operation, see
433///   `Config.fs_permission`
434/// - Only permitted accounts are allowed to execute cheatcodes in forking mode, this ensures no
435///   contract deployed on the live network is able to execute cheatcodes by simply calling the
436///   cheatcode address: by default, the caller, test contract and newly deployed contracts are
437///   allowed to execute cheatcodes
438#[derive(Clone, Debug)]
439pub struct Cheatcodes<FEN: FoundryEvmNetwork = EthEvmNetwork> {
440    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities
441    pub analysis: Option<CheatcodeAnalysis>,
442
443    /// The block environment
444    ///
445    /// Used in the cheatcode handler to overwrite the block environment separately from the
446    /// execution block environment.
447    pub block: Option<BlockEnvFor<FEN>>,
448
449    /// Currently active EIP-7702 delegations that will be consumed when building the next
450    /// transaction. Set by `vm.attachDelegation()` and consumed via `.take()` during
451    /// transaction construction.
452    pub active_delegations: Vec<SignedAuthorization>,
453
454    /// The active EIP-4844 blob that will be attached to the next call.
455    pub active_blob_sidecar: Option<BlobTransactionSidecarVariant>,
456
457    /// The gas price.
458    ///
459    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
460    /// in the execution environment.
461    pub gas_price: Option<u128>,
462
463    /// Address labels
464    pub labels: AddressHashMap<String>,
465
466    /// Prank information, mapped to the call depth where pranks were added.
467    pub pranks: BTreeMap<usize, Prank>,
468
469    /// Expected revert information
470    pub expected_revert: Option<ExpectedRevert>,
471
472    /// Assume next call can revert and discard fuzz run if it does.
473    pub assume_no_revert: Option<AssumeNoRevert>,
474
475    /// Additional diagnostic for reverts
476    pub fork_revert_diagnostic: Option<RevertDiagnostic>,
477
478    /// Recorded storage reads and writes
479    pub accesses: RecordAccess,
480
481    /// Whether storage access recording is currently active
482    pub recording_accesses: bool,
483
484    /// Recorded account accesses (calls, creates) organized by relative call depth, where the
485    /// topmost vector corresponds to accesses at the depth at which account access recording
486    /// began. Each vector in the matrix represents a list of accesses at a specific call
487    /// depth. Once that call context has ended, the last vector is removed from the matrix and
488    /// merged into the previous vector.
489    pub recorded_account_diffs_stack: Option<Vec<Vec<AccountAccess>>>,
490
491    /// The information of the debug step recording.
492    pub record_debug_steps_info: Option<RecordDebugStepInfo>,
493
494    /// Recorded logs
495    pub recorded_logs: Option<Vec<crate::Vm::Log>>,
496
497    /// Mocked calls
498    // **Note**: inner must a BTreeMap because of special `Ord` impl for `MockCallDataContext`
499    pub mocked_calls: HashMap<Address, BTreeMap<MockCallDataContext, VecDeque<MockCallReturnData>>>,
500
501    /// Mocked functions. Maps target address to be mocked to pair of (calldata, mock address).
502    pub mocked_functions: HashMap<Address, HashMap<Bytes, Address>>,
503
504    /// Expected calls
505    pub expected_calls: ExpectedCallTracker,
506    /// Expected emits
507    pub expected_emits: ExpectedEmitTracker,
508    /// Expected creates
509    pub expected_creates: Vec<ExpectedCreate>,
510
511    /// Map of context depths to memory offset ranges that may be written to within the call depth.
512    pub allowed_mem_writes: HashMap<u64, Vec<Range<u64>>>,
513
514    /// Current broadcasting information
515    pub broadcast: Option<Broadcast>,
516
517    /// Scripting based transactions
518    pub broadcastable_transactions: BroadcastableTransactions<FEN::Network>,
519
520    /// Current EIP-2930 access lists.
521    pub access_list: Option<AccessList>,
522
523    /// Additional, user configurable context this Inspector has access to when inspecting a call.
524    pub config: Arc<CheatsConfig>,
525
526    /// Test-scoped context holding data that needs to be reset every test run
527    pub test_context: TestContext,
528
529    /// Whether to commit FS changes such as file creations, writes and deletes.
530    /// Used to prevent duplicate changes file executing non-committing calls.
531    pub fs_commit: bool,
532
533    /// Serialized JSON values.
534    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
535    pub serialized_jsons: BTreeMap<String, BTreeMap<String, Value>>,
536
537    /// All recorded ETH `deal`s.
538    pub eth_deals: Vec<DealRecord>,
539
540    /// Gas metering state.
541    pub gas_metering: GasMetering,
542
543    /// Contains gas snapshots made over the course of a test suite.
544    // **Note**: both must a BTreeMap to ensure the order of the keys is deterministic.
545    pub gas_snapshots: BTreeMap<String, BTreeMap<String, String>>,
546
547    /// Mapping slots.
548    pub mapping_slots: Option<AddressHashMap<MappingSlots>>,
549
550    /// The current program counter.
551    pub pc: usize,
552    /// Breakpoints supplied by the `breakpoint` cheatcode.
553    /// `char -> (address, pc)`
554    pub breakpoints: Breakpoints,
555
556    /// Whether the next contract creation should be intercepted to return its initcode.
557    pub intercept_next_create_call: bool,
558
559    /// Optional cheatcodes `TestRunner`. Used for generating random values from uint and int
560    /// strategies.
561    test_runner: Option<TestRunner>,
562
563    /// Ignored traces.
564    pub ignored_traces: IgnoredTraces,
565
566    /// Addresses with arbitrary storage.
567    pub arbitrary_storage: Option<ArbitraryStorage>,
568
569    /// Deprecated cheatcodes mapped to the reason. Used to report warnings on test results.
570    pub deprecated: HashMap<&'static str, Option<&'static str>>,
571    /// Unlocked wallets used in scripts and testing of scripts.
572    pub wallets: Option<Wallets>,
573    /// Signatures identifier for decoding events and functions
574    signatures_identifier: OnceLock<Option<SignaturesIdentifier>>,
575    /// Used to determine whether the broadcasted call has dynamic gas limit.
576    pub dynamic_gas_limit: bool,
577    // Custom execution evm version.
578    pub execution_evm_version: Option<SpecFor<FEN>>,
579}
580
581// This is not derived because calling this in `fn new` with `..Default::default()` creates a second
582// `CheatsConfig` which is unused, and inside it `ProjectPathsConfig` is relatively expensive to
583// create.
584impl Default for Cheatcodes {
585    fn default() -> Self {
586        Self::new(Arc::default())
587    }
588}
589
590impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
591    /// Creates a new `Cheatcodes` with the given settings.
592    pub fn new(config: Arc<CheatsConfig>) -> Self {
593        Self {
594            analysis: None,
595            fs_commit: true,
596            labels: config.labels.clone(),
597            config,
598            block: Default::default(),
599            active_delegations: Default::default(),
600            active_blob_sidecar: Default::default(),
601            gas_price: Default::default(),
602            pranks: Default::default(),
603            expected_revert: Default::default(),
604            assume_no_revert: Default::default(),
605            fork_revert_diagnostic: Default::default(),
606            accesses: Default::default(),
607            recording_accesses: Default::default(),
608            recorded_account_diffs_stack: Default::default(),
609            recorded_logs: Default::default(),
610            record_debug_steps_info: Default::default(),
611            mocked_calls: Default::default(),
612            mocked_functions: Default::default(),
613            expected_calls: Default::default(),
614            expected_emits: Default::default(),
615            expected_creates: Default::default(),
616            allowed_mem_writes: Default::default(),
617            broadcast: Default::default(),
618            broadcastable_transactions: Default::default(),
619            access_list: Default::default(),
620            test_context: Default::default(),
621            serialized_jsons: Default::default(),
622            eth_deals: Default::default(),
623            gas_metering: Default::default(),
624            gas_snapshots: Default::default(),
625            mapping_slots: Default::default(),
626            pc: Default::default(),
627            breakpoints: Default::default(),
628            intercept_next_create_call: Default::default(),
629            test_runner: Default::default(),
630            ignored_traces: Default::default(),
631            arbitrary_storage: Default::default(),
632            deprecated: Default::default(),
633            wallets: Default::default(),
634            signatures_identifier: Default::default(),
635            dynamic_gas_limit: Default::default(),
636            execution_evm_version: None,
637        }
638    }
639
640    /// Enables cheatcode analysis capabilities by providing a solar compiler instance.
641    pub fn set_analysis(&mut self, analysis: CheatcodeAnalysis) {
642        self.analysis = Some(analysis);
643    }
644
645    /// Returns the configured prank at given depth or the first prank configured at a lower depth.
646    /// For example, if pranks configured for depth 1, 3 and 5, the prank for depth 4 is the one
647    /// configured at depth 3.
648    pub fn get_prank(&self, depth: usize) -> Option<&Prank> {
649        self.pranks.range(..=depth).last().map(|(_, prank)| prank)
650    }
651
652    /// Returns the configured wallets if available, else creates a new instance.
653    pub fn wallets(&mut self) -> &Wallets {
654        self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None))
655    }
656
657    /// Sets the unlocked wallets.
658    pub fn set_wallets(&mut self, wallets: Wallets) {
659        self.wallets = Some(wallets);
660    }
661
662    /// Adds a delegation to the active delegations list.
663    pub fn add_delegation(&mut self, authorization: SignedAuthorization) {
664        self.active_delegations.push(authorization);
665    }
666
667    /// Returns the signatures identifier.
668    pub fn signatures_identifier(&self) -> Option<&SignaturesIdentifier> {
669        self.signatures_identifier.get_or_init(|| SignaturesIdentifier::new(true).ok()).as_ref()
670    }
671
672    /// Decodes the input data and applies the cheatcode.
673    fn apply_cheatcode(
674        &mut self,
675        ecx: &mut FoundryContextFor<'_, FEN>,
676        call: &CallInputs,
677        executor: &mut dyn CheatcodesExecutor<FEN>,
678    ) -> Result {
679        // decode the cheatcode call
680        let decoded = Vm::VmCalls::abi_decode(&call.input.bytes(ecx)).map_err(|e| {
681            if let alloy_sol_types::Error::UnknownSelector { name: _, selector } = e {
682                let msg = format!(
683                    "unknown cheatcode with selector {selector}; \
684                     you may have a mismatch between the `Vm` interface (likely in `forge-std`) \
685                     and the `forge` version"
686                );
687                return alloy_sol_types::Error::Other(std::borrow::Cow::Owned(msg));
688            }
689            e
690        })?;
691
692        let caller = call.caller;
693
694        // ensure the caller is allowed to execute cheatcodes,
695        // but only if the backend is in forking mode
696        ecx.db_mut().ensure_cheatcode_access_forking_mode(&caller)?;
697
698        apply_dispatch(
699            &decoded,
700            &mut CheatsCtxt { state: self, ecx, gas_limit: call.gas_limit, caller },
701            executor,
702        )
703    }
704
705    /// Grants cheat code access for new contracts if the caller also has
706    /// cheatcode access or the new contract is created in top most call.
707    ///
708    /// There may be cheatcodes in the constructor of the new contract, in order to allow them
709    /// automatically we need to determine the new address.
710    fn allow_cheatcodes_on_create(
711        &self,
712        ecx: &mut FoundryContextFor<FEN>,
713        caller: Address,
714        created_address: Address,
715    ) {
716        if ecx.journal().depth() <= 1 || ecx.db().has_cheatcode_access(&caller) {
717            ecx.db_mut().allow_cheatcode_access(created_address);
718        }
719    }
720
721    /// Apply EIP-2930 access list.
722    ///
723    /// If the transaction type is [TransactionType::Legacy] we need to upgrade it to
724    /// [TransactionType::Eip2930] in order to use access lists. Other transaction types support
725    /// access lists themselves.
726    fn apply_accesslist(&mut self, ecx: &mut FoundryContextFor<FEN>) {
727        if let Some(access_list) = &self.access_list {
728            ecx.tx_mut().set_access_list(access_list.clone());
729
730            if ecx.tx().tx_type() == TransactionType::Legacy as u8 {
731                ecx.tx_mut().set_tx_type(TransactionType::Eip2930 as u8);
732            }
733        }
734    }
735
736    /// Called when there was a revert.
737    ///
738    /// Cleanup any previously applied cheatcodes that altered the state in such a way that revm's
739    /// revert would run into issues.
740    pub fn on_revert(&mut self, ecx: &mut FoundryContextFor<FEN>) {
741        trace!(deals=?self.eth_deals.len(), "rolling back deals");
742
743        // Delay revert clean up until expected revert is handled, if set.
744        if self.expected_revert.is_some() {
745            return;
746        }
747
748        // we only want to apply cleanup top level
749        if ecx.journal().depth() > 0 {
750            return;
751        }
752
753        // Roll back all previously applied deals
754        // This will prevent overflow issues in revm's [`JournaledState::journal_revert`] routine
755        // which rolls back any transfers.
756        while let Some(record) = self.eth_deals.pop() {
757            if let Some(acc) = ecx.journal_mut().evm_state_mut().get_mut(&record.address) {
758                acc.info.balance = record.old_balance;
759            }
760        }
761    }
762
763    pub fn call_with_executor(
764        &mut self,
765        ecx: &mut FoundryContextFor<'_, FEN>,
766        call: &mut CallInputs,
767        executor: &mut dyn CheatcodesExecutor<FEN>,
768    ) -> Option<CallOutcome> {
769        // Apply custom execution evm version.
770        if let Some(spec_id) = self.execution_evm_version {
771            ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
772        }
773
774        let gas = Gas::new(call.gas_limit);
775        let curr_depth = ecx.journal().depth();
776
777        // At the root call to test function or script `run()`/`setUp()` functions, we are
778        // decreasing sender nonce to ensure that it matches on-chain nonce once we start
779        // broadcasting.
780        if curr_depth == 0 {
781            let sender = ecx.tx().caller();
782            let account = match super::evm::journaled_account(ecx, sender) {
783                Ok(account) => account,
784                Err(err) => {
785                    return Some(CallOutcome {
786                        result: InterpreterResult {
787                            result: InstructionResult::Revert,
788                            output: err.abi_encode().into(),
789                            gas,
790                        },
791                        memory_offset: call.return_memory_offset.clone(),
792                        was_precompile_called: false,
793                        precompile_call_logs: vec![],
794                    });
795                }
796            };
797            let prev = account.info.nonce;
798            account.info.nonce = prev.saturating_sub(1);
799
800            trace!(target: "cheatcodes", %sender, nonce=account.info.nonce, prev, "corrected nonce");
801        }
802
803        if call.target_address == CHEATCODE_ADDRESS {
804            return match self.apply_cheatcode(ecx, call, executor) {
805                Ok(retdata) => Some(CallOutcome {
806                    result: InterpreterResult {
807                        result: InstructionResult::Return,
808                        output: retdata.into(),
809                        gas,
810                    },
811                    memory_offset: call.return_memory_offset.clone(),
812                    was_precompile_called: true,
813                    precompile_call_logs: vec![],
814                }),
815                Err(err) => Some(CallOutcome {
816                    result: InterpreterResult {
817                        result: InstructionResult::Revert,
818                        output: err.abi_encode().into(),
819                        gas,
820                    },
821                    memory_offset: call.return_memory_offset.clone(),
822                    was_precompile_called: false,
823                    precompile_call_logs: vec![],
824                }),
825            };
826        }
827
828        if call.target_address == HARDHAT_CONSOLE_ADDRESS {
829            return None;
830        }
831
832        // `expectRevert`: track max call depth. This is also done in `initialize_interp`, but
833        // precompile calls don't create an interpreter frame so we must also track it here.
834        // The callee executes at `curr_depth + 1`.
835        if let Some(expected) = &mut self.expected_revert {
836            expected.max_depth = max(curr_depth + 1, expected.max_depth);
837        }
838
839        // Handle expected calls
840
841        // Grab the different calldatas expected.
842        if let Some(expected_calls_for_target) = self.expected_calls.get_mut(&call.bytecode_address)
843        {
844            // Match every partial/full calldata
845            for (calldata, (expected, actual_count)) in expected_calls_for_target {
846                // Increment actual times seen if...
847                // The calldata is at most, as big as this call's input, and
848                if calldata.len() <= call.input.len() &&
849                    // Both calldata match, taking the length of the assumed smaller one (which will have at least the selector), and
850                    *calldata == call.input.bytes(ecx)[..calldata.len()] &&
851                    // The value matches, if provided
852                    expected
853                        .value.is_none_or(|value| Some(value) == call.transfer_value()) &&
854                    // The gas matches, if provided
855                    expected.gas.is_none_or(|gas| gas == call.gas_limit) &&
856                    // The minimum gas matches, if provided
857                    expected.min_gas.is_none_or(|min_gas| min_gas <= call.gas_limit)
858                {
859                    *actual_count += 1;
860                }
861            }
862        }
863
864        // Handle mocked calls
865        if let Some(mocks) = self.mocked_calls.get_mut(&call.bytecode_address) {
866            let ctx = MockCallDataContext {
867                calldata: call.input.bytes(ecx),
868                value: call.transfer_value(),
869            };
870
871            if let Some(return_data_queue) = match mocks.get_mut(&ctx) {
872                Some(queue) => Some(queue),
873                None => mocks
874                    .iter_mut()
875                    .find(|(mock, _)| {
876                        call.input.bytes(ecx).get(..mock.calldata.len()) == Some(&mock.calldata[..])
877                            && mock.value.is_none_or(|value| Some(value) == call.transfer_value())
878                    })
879                    .map(|(_, v)| v),
880            } && let Some(return_data) = if return_data_queue.len() == 1 {
881                // If the mocked calls stack has a single element in it, don't empty it
882                return_data_queue.front().map(|x| x.to_owned())
883            } else {
884                // Else, we pop the front element
885                return_data_queue.pop_front()
886            } {
887                return Some(CallOutcome {
888                    result: InterpreterResult {
889                        result: return_data.ret_type,
890                        output: return_data.data,
891                        gas,
892                    },
893                    memory_offset: call.return_memory_offset.clone(),
894                    was_precompile_called: true,
895                    precompile_call_logs: vec![],
896                });
897            }
898        }
899
900        // Apply our prank
901        if let Some(prank) = &self.get_prank(curr_depth) {
902            // Apply delegate call, `call.caller`` will not equal `prank.prank_caller`
903            if prank.delegate_call
904                && curr_depth == prank.depth
905                && let CallScheme::DelegateCall = call.scheme
906            {
907                call.target_address = prank.new_caller;
908                call.caller = prank.new_caller;
909                if let Some(new_origin) = prank.new_origin {
910                    ecx.tx_mut().set_caller(new_origin);
911                }
912            }
913
914            if curr_depth >= prank.depth && call.caller == prank.prank_caller {
915                let mut prank_applied = false;
916
917                // At the target depth we set `msg.sender`
918                if curr_depth == prank.depth {
919                    // Ensure new caller is loaded and touched
920                    let _ = journaled_account(ecx, prank.new_caller);
921                    call.caller = prank.new_caller;
922                    prank_applied = true;
923                }
924
925                // At the target depth, or deeper, we set `tx.origin`
926                if let Some(new_origin) = prank.new_origin {
927                    ecx.tx_mut().set_caller(new_origin);
928                    prank_applied = true;
929                }
930
931                // If prank applied for first time, then update
932                if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
933                    self.pranks.insert(curr_depth, applied_prank);
934                }
935            }
936        }
937
938        // Apply EIP-2930 access list
939        self.apply_accesslist(ecx);
940
941        // Apply our broadcast
942        if let Some(broadcast) = &self.broadcast {
943            // Additional check as transfers in forge scripts seem to be estimated at 2300
944            // by revm leading to "Intrinsic gas too low" failure when simulated on chain.
945            let is_fixed_gas_limit = call.gas_limit >= 21_000 && !self.dynamic_gas_limit;
946            self.dynamic_gas_limit = false;
947
948            // We only apply a broadcast *to a specific depth*.
949            //
950            // We do this because any subsequent contract calls *must* exist on chain and
951            // we only want to grab *this* call, not internal ones
952            if curr_depth == broadcast.depth && call.caller == broadcast.original_caller {
953                // At the target depth we set `msg.sender` & tx.origin.
954                // We are simulating the caller as being an EOA, so *both* must be set to the
955                // broadcast.origin.
956                ecx.tx_mut().set_caller(broadcast.new_origin);
957
958                call.caller = broadcast.new_origin;
959                // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here
960                // because we only need the from, to, value, and data. We can later change this
961                // into 1559, in the cli package, relatively easily once we
962                // know the target chain supports EIP-1559.
963                if !call.is_static {
964                    if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
965                        return Some(CallOutcome {
966                            result: InterpreterResult {
967                                result: InstructionResult::Revert,
968                                output: Error::encode(err),
969                                gas,
970                            },
971                            memory_offset: call.return_memory_offset.clone(),
972                            was_precompile_called: false,
973                            precompile_call_logs: vec![],
974                        });
975                    }
976
977                    let input = call.input.bytes(ecx);
978                    let chain_id = ecx.cfg().chain_id();
979                    let rpc = ecx.db().active_fork_url();
980                    let account =
981                        ecx.journal_mut().evm_state_mut().get_mut(&broadcast.new_origin).unwrap();
982
983                    let mut tx_req = TransactionRequestFor::<FEN>::default()
984                        .with_from(broadcast.new_origin)
985                        .with_to(call.target_address)
986                        .with_value(call.transfer_value().unwrap_or_default())
987                        .with_input(input)
988                        .with_nonce(account.info.nonce)
989                        .with_chain_id(chain_id);
990                    if is_fixed_gas_limit {
991                        tx_req.set_gas_limit(call.gas_limit)
992                    }
993
994                    let active_delegations = std::mem::take(&mut self.active_delegations);
995                    // Set active blob sidecar, if any.
996                    if let Some(blob_sidecar) = self.active_blob_sidecar.take() {
997                        // Ensure blob and delegation are not set for the same tx.
998                        if !active_delegations.is_empty() {
999                            let msg = "both delegation and blob are active; `attachBlob` and `attachDelegation` are not compatible";
1000                            return Some(CallOutcome {
1001                                result: InterpreterResult {
1002                                    result: InstructionResult::Revert,
1003                                    output: Error::encode(msg),
1004                                    gas,
1005                                },
1006                                memory_offset: call.return_memory_offset.clone(),
1007                                was_precompile_called: false,
1008                                precompile_call_logs: vec![],
1009                            });
1010                        }
1011                        tx_req.set_blob_sidecar(blob_sidecar);
1012                    }
1013
1014                    // Apply active EIP-7702 delegations, if any.
1015                    if !active_delegations.is_empty() {
1016                        for auth in &active_delegations {
1017                            let Ok(authority) = auth.recover_authority() else {
1018                                continue;
1019                            };
1020                            if authority == broadcast.new_origin {
1021                                // Increment nonce of broadcasting account to reflect signed
1022                                // authorization.
1023                                account.info.nonce += 1;
1024                            }
1025                        }
1026                        tx_req.set_authorization_list(active_delegations);
1027                    }
1028                    if let Some(fee_token) = self.config.fee_token {
1029                        tx_req.set_fee_token(fee_token);
1030                    }
1031                    self.broadcastable_transactions.push_back(BroadcastableTransaction {
1032                        rpc,
1033                        transaction: TransactionMaybeSigned::new(tx_req),
1034                    });
1035                    debug!(target: "cheatcodes", tx=?self.broadcastable_transactions.back().unwrap(), "broadcastable call");
1036
1037                    // Explicitly increment nonce if calls are not isolated.
1038                    if !self.config.evm_opts.isolate {
1039                        let prev = account.info.nonce;
1040                        account.info.nonce += 1;
1041                        debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce");
1042                    }
1043                } else if broadcast.single_call {
1044                    let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead";
1045                    return Some(CallOutcome {
1046                        result: InterpreterResult {
1047                            result: InstructionResult::Revert,
1048                            output: Error::encode(msg),
1049                            gas,
1050                        },
1051                        memory_offset: call.return_memory_offset.clone(),
1052                        was_precompile_called: false,
1053                        precompile_call_logs: vec![],
1054                    });
1055                }
1056            }
1057        }
1058
1059        // Record called accounts if `startStateDiffRecording` has been called
1060        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1061            // Determine if account is "initialized," ie, it has a non-zero balance, a non-zero
1062            // nonce, a non-zero KECCAK_EMPTY codehash, or non-empty code
1063            let initialized;
1064            let old_balance;
1065            let old_nonce;
1066
1067            if let Ok(acc) = ecx.journal_mut().load_account(call.target_address) {
1068                initialized = acc.data.info.exists();
1069                old_balance = acc.data.info.balance;
1070                old_nonce = acc.data.info.nonce;
1071            } else {
1072                initialized = false;
1073                old_balance = U256::ZERO;
1074                old_nonce = 0;
1075            }
1076
1077            let kind = match call.scheme {
1078                CallScheme::Call => crate::Vm::AccountAccessKind::Call,
1079                CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode,
1080                CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall,
1081                CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall,
1082            };
1083
1084            // Record this call by pushing it to a new pending vector; all subsequent calls at
1085            // that depth will be pushed to the same vector. When the call ends, the
1086            // RecordedAccountAccess (and all subsequent RecordedAccountAccesses) will be
1087            // updated with the revert status of this call, since the EVM does not mark accounts
1088            // as "warm" if the call from which they were accessed is reverted
1089            recorded_account_diffs_stack.push(vec![AccountAccess {
1090                chainInfo: crate::Vm::ChainInfo {
1091                    forkId: ecx.db().active_fork_id().unwrap_or_default(),
1092                    chainId: U256::from(ecx.cfg().chain_id()),
1093                },
1094                accessor: call.caller,
1095                account: call.bytecode_address,
1096                kind,
1097                initialized,
1098                oldBalance: old_balance,
1099                newBalance: U256::ZERO, // updated on call_end
1100                oldNonce: old_nonce,
1101                newNonce: 0, // updated on call_end
1102                value: call.call_value(),
1103                data: call.input.bytes(ecx),
1104                reverted: false,
1105                deployedCode: Bytes::new(),
1106                storageAccesses: vec![], // updated on step
1107                depth: ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1108            }]);
1109        }
1110
1111        None
1112    }
1113
1114    pub fn rng(&mut self) -> &mut impl Rng {
1115        self.test_runner().rng()
1116    }
1117
1118    pub fn test_runner(&mut self) -> &mut TestRunner {
1119        self.test_runner.get_or_insert_with(|| match self.config.seed {
1120            Some(seed) => TestRunner::new_with_rng(
1121                proptest::test_runner::Config::default(),
1122                TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1123            ),
1124            None => TestRunner::new(proptest::test_runner::Config::default()),
1125        })
1126    }
1127
1128    pub fn set_seed(&mut self, seed: U256) {
1129        self.test_runner = Some(TestRunner::new_with_rng(
1130            proptest::test_runner::Config::default(),
1131            TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()),
1132        ));
1133    }
1134
1135    /// Returns existing or set a default `ArbitraryStorage` option.
1136    /// Used by `setArbitraryStorage` cheatcode to track addresses with arbitrary storage.
1137    pub fn arbitrary_storage(&mut self) -> &mut ArbitraryStorage {
1138        self.arbitrary_storage.get_or_insert_with(ArbitraryStorage::default)
1139    }
1140
1141    /// Whether the given address has arbitrary storage.
1142    pub fn has_arbitrary_storage(&self, address: &Address) -> bool {
1143        match &self.arbitrary_storage {
1144            Some(storage) => storage.values.contains_key(address),
1145            None => false,
1146        }
1147    }
1148
1149    /// Whether the given slot of address with arbitrary storage should be overwritten.
1150    /// True if address is marked as and overwrite and if no value was previously generated for
1151    /// given slot.
1152    pub fn should_overwrite_arbitrary_storage(
1153        &self,
1154        address: &Address,
1155        storage_slot: U256,
1156    ) -> bool {
1157        match &self.arbitrary_storage {
1158            Some(storage) => {
1159                storage.overwrites.contains(address)
1160                    && storage
1161                        .values
1162                        .get(address)
1163                        .and_then(|arbitrary_values| arbitrary_values.get(&storage_slot))
1164                        .is_none()
1165            }
1166            None => false,
1167        }
1168    }
1169
1170    /// Whether the given address is a copy of an address with arbitrary storage.
1171    pub fn is_arbitrary_storage_copy(&self, address: &Address) -> bool {
1172        match &self.arbitrary_storage {
1173            Some(storage) => storage.copies.contains_key(address),
1174            None => false,
1175        }
1176    }
1177
1178    /// Returns struct definitions from the analysis, if available.
1179    pub fn struct_defs(&self) -> Option<&foundry_common::fmt::StructDefinitions> {
1180        self.analysis.as_ref().and_then(|analysis| analysis.struct_defs().ok())
1181    }
1182}
1183
1184impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for Cheatcodes<FEN> {
1185    fn initialize_interp(
1186        &mut self,
1187        interpreter: &mut Interpreter,
1188        ecx: &mut FoundryContextFor<'_, FEN>,
1189    ) {
1190        // When the first interpreter is initialized we've circumvented the balance and gas checks,
1191        // so we apply our actual block data with the correct fees and all.
1192        if let Some(block) = self.block.take() {
1193            ecx.set_block(block);
1194        }
1195        if let Some(gas_price) = self.gas_price.take() {
1196            ecx.tx_mut().set_gas_price(gas_price);
1197        }
1198
1199        // Record gas for current frame.
1200        if self.gas_metering.paused {
1201            self.gas_metering.paused_frames.push(interpreter.gas);
1202        }
1203
1204        // `expectRevert`: track the max call depth during `expectRevert`
1205        if let Some(expected) = &mut self.expected_revert {
1206            expected.max_depth = max(ecx.journal().depth(), expected.max_depth);
1207        }
1208    }
1209
1210    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1211        self.pc = interpreter.bytecode.pc();
1212
1213        if self.broadcast.is_some() {
1214            self.set_gas_limit_type(interpreter);
1215        }
1216
1217        // `pauseGasMetering`: pause / resume interpreter gas.
1218        if self.gas_metering.paused {
1219            self.meter_gas(interpreter);
1220        }
1221
1222        // `resetGasMetering`: reset interpreter gas.
1223        if self.gas_metering.reset {
1224            self.meter_gas_reset(interpreter);
1225        }
1226
1227        // `record`: record storage reads and writes.
1228        if self.recording_accesses {
1229            self.record_accesses(interpreter);
1230        }
1231
1232        // `startStateDiffRecording`: record granular ordered storage accesses.
1233        if self.recorded_account_diffs_stack.is_some() {
1234            self.record_state_diffs(interpreter, ecx);
1235        }
1236
1237        // `expectSafeMemory`: check if the current opcode is allowed to interact with memory.
1238        if !self.allowed_mem_writes.is_empty() {
1239            self.check_mem_opcodes(
1240                interpreter,
1241                ecx.journal().depth().try_into().expect("journaled state depth exceeds u64"),
1242            );
1243        }
1244
1245        // `startMappingRecording`: record SSTORE and KECCAK256.
1246        if let Some(mapping_slots) = &mut self.mapping_slots {
1247            mapping_step(mapping_slots, interpreter);
1248        }
1249
1250        // `snapshotGas*`: take a snapshot of the current gas.
1251        if self.gas_metering.recording {
1252            self.meter_gas_record(interpreter, ecx);
1253        }
1254    }
1255
1256    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1257        if self.gas_metering.paused {
1258            self.meter_gas_end(interpreter);
1259        }
1260
1261        if self.gas_metering.touched {
1262            self.meter_gas_check(interpreter);
1263        }
1264
1265        // `setArbitraryStorage` and `copyStorage`: add arbitrary values to storage.
1266        if self.arbitrary_storage.is_some() {
1267            self.arbitrary_storage_end(interpreter, ecx);
1268        }
1269    }
1270
1271    fn log(&mut self, _ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1272        if !self.expected_emits.is_empty()
1273            && let Some(err) = expect::handle_expect_emit(self, &log, None)
1274        {
1275            // Because we do not have access to the interpreter here, we cannot fail the test
1276            // immediately. In most cases the failure will still be caught on `call_end`.
1277            // In the rare case it is not, we log the error here.
1278            let _ = sh_err!("{err:?}");
1279        }
1280
1281        // `recordLogs`
1282        record_logs(&mut self.recorded_logs, &log);
1283    }
1284
1285    fn log_full(
1286        &mut self,
1287        interpreter: &mut Interpreter,
1288        _ecx: &mut FoundryContextFor<'_, FEN>,
1289        log: Log,
1290    ) {
1291        if !self.expected_emits.is_empty() {
1292            expect::handle_expect_emit(self, &log, Some(interpreter));
1293        }
1294
1295        // `recordLogs`
1296        record_logs(&mut self.recorded_logs, &log);
1297    }
1298
1299    fn call(
1300        &mut self,
1301        ecx: &mut FoundryContextFor<'_, FEN>,
1302        inputs: &mut CallInputs,
1303    ) -> Option<CallOutcome> {
1304        Self::call_with_executor(self, ecx, inputs, &mut TransparentCheatcodesExecutor)
1305    }
1306
1307    fn call_end(
1308        &mut self,
1309        ecx: &mut FoundryContextFor<'_, FEN>,
1310        call: &CallInputs,
1311        outcome: &mut CallOutcome,
1312    ) {
1313        let cheatcode_call = call.target_address == CHEATCODE_ADDRESS
1314            || call.target_address == HARDHAT_CONSOLE_ADDRESS;
1315
1316        // Clean up pranks/broadcasts if it's not a cheatcode call end. We shouldn't do
1317        // it for cheatcode calls because they are not applied for cheatcodes in the `call` hook.
1318        // This should be placed before the revert handling, because we might exit early there
1319        if !cheatcode_call {
1320            // Clean up pranks
1321            let curr_depth = ecx.journal().depth();
1322            if let Some(prank) = &self.get_prank(curr_depth)
1323                && curr_depth == prank.depth
1324            {
1325                ecx.tx_mut().set_caller(prank.prank_origin);
1326
1327                // Clean single-call prank once we have returned to the original depth
1328                if prank.single_call {
1329                    self.pranks.remove(&curr_depth);
1330                }
1331            }
1332
1333            // Clean up broadcast
1334            if let Some(broadcast) = &self.broadcast
1335                && curr_depth == broadcast.depth
1336            {
1337                ecx.tx_mut().set_caller(broadcast.original_origin);
1338
1339                // Clean single-call broadcast once we have returned to the original depth
1340                if broadcast.single_call {
1341                    let _ = self.broadcast.take();
1342                }
1343            }
1344        }
1345
1346        // Handle assume no revert cheatcode.
1347        if let Some(assume_no_revert) = &mut self.assume_no_revert {
1348            // Record current reverter address before processing the expect revert if call reverted,
1349            // expect revert is set with expected reverter address and no actual reverter set yet.
1350            if outcome.result.is_revert() && assume_no_revert.reverted_by.is_none() {
1351                assume_no_revert.reverted_by = Some(call.target_address);
1352            }
1353
1354            // allow multiple cheatcode calls at the same depth
1355            let curr_depth = ecx.journal().depth();
1356            if curr_depth <= assume_no_revert.depth && !cheatcode_call {
1357                // Discard run if we're at the same depth as cheatcode, call reverted, and no
1358                // specific reason was supplied
1359                if outcome.result.is_revert() {
1360                    let assume_no_revert = std::mem::take(&mut self.assume_no_revert).unwrap();
1361                    return match revert_handlers::handle_assume_no_revert(
1362                        &assume_no_revert,
1363                        outcome.result.result,
1364                        &outcome.result.output,
1365                        &self.config.available_artifacts,
1366                    ) {
1367                        // if result is Ok, it was an anticipated revert; return an "assume" error
1368                        // to reject this run
1369                        Ok(_) => {
1370                            outcome.result.output = Error::from(MAGIC_ASSUME).abi_encode().into();
1371                        }
1372                        // if result is Error, it was an unanticipated revert; should revert
1373                        // normally
1374                        Err(error) => {
1375                            trace!(expected=?assume_no_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1376                            outcome.result.result = InstructionResult::Revert;
1377                            outcome.result.output = error.abi_encode().into();
1378                        }
1379                    };
1380                }
1381                // Call didn't revert, reset `assume_no_revert` state.
1382                self.assume_no_revert = None;
1383            }
1384        }
1385
1386        // Handle expected reverts.
1387        if let Some(expected_revert) = &mut self.expected_revert {
1388            // Record current reverter address and call scheme before processing the expect revert
1389            // if call reverted.
1390            if outcome.result.is_revert() {
1391                // Record current reverter address if expect revert is set with expected reverter
1392                // address and no actual reverter was set yet or if we're expecting more than one
1393                // revert.
1394                if expected_revert.reverter.is_some()
1395                    && (expected_revert.reverted_by.is_none() || expected_revert.count > 1)
1396                {
1397                    expected_revert.reverted_by = Some(call.target_address);
1398                }
1399            }
1400
1401            let curr_depth = ecx.journal().depth();
1402            if curr_depth <= expected_revert.depth {
1403                let needs_processing = match expected_revert.kind {
1404                    ExpectedRevertKind::Default => !cheatcode_call,
1405                    // `pending_processing` == true means that we're in the `call_end` hook for
1406                    // `vm.expectCheatcodeRevert` and shouldn't expect revert here
1407                    ExpectedRevertKind::Cheatcode { pending_processing } => {
1408                        cheatcode_call && !pending_processing
1409                    }
1410                };
1411
1412                if needs_processing {
1413                    let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1414                    return match revert_handlers::handle_expect_revert(
1415                        cheatcode_call,
1416                        false,
1417                        self.config.internal_expect_revert,
1418                        &expected_revert,
1419                        outcome.result.result,
1420                        outcome.result.output.clone(),
1421                        &self.config.available_artifacts,
1422                    ) {
1423                        Err(error) => {
1424                            trace!(expected=?expected_revert, ?error, status=?outcome.result.result, "Expected revert mismatch");
1425                            outcome.result.result = InstructionResult::Revert;
1426                            outcome.result.output = error.abi_encode().into();
1427                        }
1428                        Ok((_, retdata)) => {
1429                            expected_revert.actual_count += 1;
1430                            if expected_revert.actual_count < expected_revert.count {
1431                                self.expected_revert = Some(expected_revert);
1432                            }
1433                            outcome.result.result = InstructionResult::Return;
1434                            outcome.result.output = retdata;
1435                        }
1436                    };
1437                }
1438
1439                // Flip `pending_processing` flag for cheatcode revert expectations, marking that
1440                // we've exited the `expectCheatcodeRevert` call scope
1441                if let ExpectedRevertKind::Cheatcode { pending_processing } =
1442                    &mut self.expected_revert.as_mut().unwrap().kind
1443                {
1444                    *pending_processing = false;
1445                }
1446            }
1447        }
1448
1449        // Exit early for calls to cheatcodes as other logic is not relevant for cheatcode
1450        // invocations
1451        if cheatcode_call {
1452            return;
1453        }
1454
1455        // Record the gas usage of the call, this allows the `lastCallGas` cheatcode to
1456        // retrieve the gas usage of the last call.
1457        let gas = outcome.result.gas;
1458        self.gas_metering.last_call_gas = Some(crate::Vm::Gas {
1459            gasLimit: gas.limit(),
1460            gasTotalUsed: gas.spent(),
1461            gasMemoryUsed: 0,
1462            gasRefunded: gas.refunded(),
1463            gasRemaining: gas.remaining(),
1464        });
1465
1466        // If `startStateDiffRecording` has been called, update the `reverted` status of the
1467        // previous call depth's recorded accesses, if any
1468        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1469            // The root call cannot be recorded.
1470            if ecx.journal().depth() > 0
1471                && let Some(mut last_recorded_depth) = recorded_account_diffs_stack.pop()
1472            {
1473                // Update the reverted status of all deeper calls if this call reverted, in
1474                // accordance with EVM behavior
1475                if outcome.result.is_revert() {
1476                    last_recorded_depth.iter_mut().for_each(|element| {
1477                        element.reverted = true;
1478                        element
1479                            .storageAccesses
1480                            .iter_mut()
1481                            .for_each(|storage_access| storage_access.reverted = true);
1482                    })
1483                }
1484
1485                if let Some(call_access) = last_recorded_depth.first_mut() {
1486                    // Assert that we're at the correct depth before recording post-call state
1487                    // changes. Depending on the depth the cheat was
1488                    // called at, there may not be any pending
1489                    // calls to update if execution has percolated up to a higher depth.
1490                    let curr_depth = ecx.journal().depth();
1491                    if call_access.depth == curr_depth as u64
1492                        && let Ok(acc) = ecx.journal_mut().load_account(call.target_address)
1493                    {
1494                        debug_assert!(access_is_call(call_access.kind));
1495                        call_access.newBalance = acc.data.info.balance;
1496                        call_access.newNonce = acc.data.info.nonce;
1497                    }
1498                    // Merge the last depth's AccountAccesses into the AccountAccesses at the
1499                    // current depth, or push them back onto the pending
1500                    // vector if higher depths were not recorded. This
1501                    // preserves ordering of accesses.
1502                    if let Some(last) = recorded_account_diffs_stack.last_mut() {
1503                        last.extend(last_recorded_depth);
1504                    } else {
1505                        recorded_account_diffs_stack.push(last_recorded_depth);
1506                    }
1507                }
1508            }
1509        }
1510
1511        // At the end of the call,
1512        // we need to check if we've found all the emits.
1513        // We know we've found all the expected emits in the right order
1514        // if the queue is fully matched.
1515        // If it's not fully matched, then either:
1516        // 1. Not enough events were emitted (we'll know this because the amount of times we
1517        // inspected events will be less than the size of the queue) 2. The wrong events
1518        // were emitted (The inspected events should match the size of the queue, but still some
1519        // events will not be matched)
1520
1521        // First, check that we're at the call depth where the emits were declared from.
1522        let should_check_emits = self
1523            .expected_emits
1524            .iter()
1525            .any(|(expected, _)| {
1526                let curr_depth = ecx.journal().depth();
1527                expected.depth == curr_depth
1528            }) &&
1529            // Ignore staticcalls
1530            !call.is_static;
1531        if should_check_emits {
1532            let expected_counts = self
1533                .expected_emits
1534                .iter()
1535                .filter_map(|(expected, count_map)| {
1536                    let count = match expected.address {
1537                        Some(emitter) => match count_map.get(&emitter) {
1538                            Some(log_count) => expected
1539                                .log
1540                                .as_ref()
1541                                .map(|l| log_count.count(l))
1542                                .unwrap_or_else(|| log_count.count_unchecked()),
1543                            None => 0,
1544                        },
1545                        None => match &expected.log {
1546                            Some(log) => count_map.values().map(|logs| logs.count(log)).sum(),
1547                            None => count_map.values().map(|logs| logs.count_unchecked()).sum(),
1548                        },
1549                    };
1550
1551                    (count != expected.count).then_some((expected, count))
1552                })
1553                .collect::<Vec<_>>();
1554
1555            // Revert if not all emits expected were matched.
1556            if let Some((expected, _)) = self
1557                .expected_emits
1558                .iter()
1559                .find(|(expected, _)| !expected.found && expected.count > 0)
1560            {
1561                outcome.result.result = InstructionResult::Revert;
1562                let error_msg = expected.mismatch_error.as_deref().unwrap_or("log != expected log");
1563                outcome.result.output = error_msg.abi_encode().into();
1564                return;
1565            }
1566
1567            if !expected_counts.is_empty() {
1568                let msg = if outcome.result.is_ok() {
1569                    let (expected, count) = expected_counts.first().unwrap();
1570                    format!("log emitted {count} times, expected {}", expected.count)
1571                } else {
1572                    "expected an emit, but the call reverted instead. \
1573                     ensure you're testing the happy path when using `expectEmit`"
1574                        .to_string()
1575                };
1576
1577                outcome.result.result = InstructionResult::Revert;
1578                outcome.result.output = Error::encode(msg);
1579                return;
1580            }
1581
1582            // All emits were found, we're good.
1583            // Clear the queue, as we expect the user to declare more events for the next call
1584            // if they wanna match further events.
1585            self.expected_emits.clear()
1586        }
1587
1588        // this will ensure we don't have false positives when trying to diagnose reverts in fork
1589        // mode
1590        let diag = self.fork_revert_diagnostic.take();
1591
1592        // if there's a revert and a previous call was diagnosed as fork related revert then we can
1593        // return a better error here
1594        if outcome.result.is_revert()
1595            && let Some(err) = diag
1596        {
1597            outcome.result.output = Error::encode(err.to_error_msg(&self.labels));
1598            return;
1599        }
1600
1601        // try to diagnose reverts in multi-fork mode where a call is made to an address that does
1602        // not exist
1603        if let TxKind::Call(test_contract) = ecx.tx().kind() {
1604            // if a call to a different contract than the original test contract returned with
1605            // `Stop` we check if the contract actually exists on the active fork
1606            if ecx.db().is_forked_mode()
1607                && outcome.result.result == InstructionResult::Stop
1608                && call.target_address != test_contract
1609            {
1610                self.fork_revert_diagnostic =
1611                    ecx.db().diagnose_revert(call.target_address, ecx.journal().evm_state());
1612            }
1613        }
1614
1615        // If the depth is 0, then this is the root call terminating
1616        if ecx.journal().depth() == 0 {
1617            // If we already have a revert, we shouldn't run the below logic as it can obfuscate an
1618            // earlier error that happened first with unrelated information about
1619            // another error when using cheatcodes.
1620            if outcome.result.is_revert() {
1621                return;
1622            }
1623
1624            // If there's not a revert, we can continue on to run the last logic for expect*
1625            // cheatcodes.
1626
1627            // Match expected calls
1628            for (address, calldatas) in &self.expected_calls {
1629                // Loop over each address, and for each address, loop over each calldata it expects.
1630                for (calldata, (expected, actual_count)) in calldatas {
1631                    // Grab the values we expect to see
1632                    let ExpectedCallData { gas, min_gas, value, count, call_type } = expected;
1633
1634                    let failed = match call_type {
1635                        // If the cheatcode was called with a `count` argument,
1636                        // we must check that the EVM performed a CALL with this calldata exactly
1637                        // `count` times.
1638                        ExpectedCallType::Count => *count != *actual_count,
1639                        // If the cheatcode was called without a `count` argument,
1640                        // we must check that the EVM performed a CALL with this calldata at least
1641                        // `count` times. The amount of times to check was
1642                        // the amount of time the cheatcode was called.
1643                        ExpectedCallType::NonCount => *count > *actual_count,
1644                    };
1645                    if failed {
1646                        let expected_values = [
1647                            Some(format!("data {}", hex::encode_prefixed(calldata))),
1648                            value.as_ref().map(|v| format!("value {v}")),
1649                            gas.map(|g| format!("gas {g}")),
1650                            min_gas.map(|g| format!("minimum gas {g}")),
1651                        ]
1652                        .into_iter()
1653                        .flatten()
1654                        .join(", ");
1655                        let but = if outcome.result.is_ok() {
1656                            let s = if *actual_count == 1 { "" } else { "s" };
1657                            format!("was called {actual_count} time{s}")
1658                        } else {
1659                            "the call reverted instead; \
1660                             ensure you're testing the happy path when using `expectCall`"
1661                                .to_string()
1662                        };
1663                        let s = if *count == 1 { "" } else { "s" };
1664                        let msg = format!(
1665                            "expected call to {address} with {expected_values} \
1666                             to be called {count} time{s}, but {but}"
1667                        );
1668                        outcome.result.result = InstructionResult::Revert;
1669                        outcome.result.output = Error::encode(msg);
1670
1671                        return;
1672                    }
1673                }
1674            }
1675
1676            // Check if we have any leftover expected emits
1677            // First, if any emits were found at the root call, then we its ok and we remove them.
1678            // For count=0 expectations, NOT being found is success, so mark them as found
1679            for (expected, _) in &mut self.expected_emits {
1680                if expected.count == 0 && !expected.found {
1681                    expected.found = true;
1682                }
1683            }
1684            self.expected_emits.retain(|(expected, _)| !expected.found);
1685            // If not empty, we got mismatched emits
1686            if !self.expected_emits.is_empty() {
1687                let msg = if outcome.result.is_ok() {
1688                    "expected an emit, but no logs were emitted afterwards. \
1689                     you might have mismatched events or not enough events were emitted"
1690                } else {
1691                    "expected an emit, but the call reverted instead. \
1692                     ensure you're testing the happy path when using `expectEmit`"
1693                };
1694                outcome.result.result = InstructionResult::Revert;
1695                outcome.result.output = Error::encode(msg);
1696                return;
1697            }
1698
1699            // Check for leftover expected creates
1700            if let Some(expected_create) = self.expected_creates.first() {
1701                let msg = format!(
1702                    "expected {} call by address {} for bytecode {} but not found",
1703                    expected_create.create_scheme,
1704                    hex::encode_prefixed(expected_create.deployer),
1705                    hex::encode_prefixed(&expected_create.bytecode),
1706                );
1707                outcome.result.result = InstructionResult::Revert;
1708                outcome.result.output = Error::encode(msg);
1709            }
1710        }
1711    }
1712
1713    fn create(
1714        &mut self,
1715        ecx: &mut FoundryContextFor<'_, FEN>,
1716        mut input: &mut CreateInputs,
1717    ) -> Option<CreateOutcome> {
1718        // Apply custom execution evm version.
1719        if let Some(spec_id) = self.execution_evm_version {
1720            ecx.cfg_mut().set_spec_and_mainnet_gas_params(spec_id);
1721        }
1722
1723        let gas = Gas::new(input.gas_limit());
1724        // Check if we should intercept this create
1725        if self.intercept_next_create_call {
1726            // Reset the flag
1727            self.intercept_next_create_call = false;
1728
1729            // Get initcode from the input
1730            let output = input.init_code();
1731
1732            // Return a revert with the initcode as error data
1733            return Some(CreateOutcome {
1734                result: InterpreterResult { result: InstructionResult::Revert, output, gas },
1735                address: None,
1736            });
1737        }
1738
1739        let curr_depth = ecx.journal().depth();
1740
1741        // Apply our prank
1742        if let Some(prank) = &self.get_prank(curr_depth)
1743            && curr_depth >= prank.depth
1744            && input.caller() == prank.prank_caller
1745        {
1746            let mut prank_applied = false;
1747
1748            // At the target depth we set `msg.sender`
1749            if curr_depth == prank.depth {
1750                // Ensure new caller is loaded and touched
1751                let _ = journaled_account(ecx, prank.new_caller);
1752                input.set_caller(prank.new_caller);
1753                prank_applied = true;
1754            }
1755
1756            // At the target depth, or deeper, we set `tx.origin`
1757            if let Some(new_origin) = prank.new_origin {
1758                ecx.tx_mut().set_caller(new_origin);
1759                prank_applied = true;
1760            }
1761
1762            // If prank applied for first time, then update
1763            if prank_applied && let Some(applied_prank) = prank.first_time_applied() {
1764                self.pranks.insert(curr_depth, applied_prank);
1765            }
1766        }
1767
1768        // Apply EIP-2930 access list
1769        self.apply_accesslist(ecx);
1770
1771        // Apply our broadcast
1772        if let Some(broadcast) = &mut self.broadcast
1773            && curr_depth >= broadcast.depth
1774            && input.caller() == broadcast.original_caller
1775        {
1776            if let Err(err) = ecx.journal_mut().load_account(broadcast.new_origin) {
1777                return Some(CreateOutcome {
1778                    result: InterpreterResult {
1779                        result: InstructionResult::Revert,
1780                        output: Error::encode(err),
1781                        gas,
1782                    },
1783                    address: None,
1784                });
1785            }
1786
1787            ecx.tx_mut().set_caller(broadcast.new_origin);
1788
1789            if curr_depth == broadcast.depth || broadcast.deploy_from_code {
1790                // Reset deploy from code flag for upcoming calls;
1791                broadcast.deploy_from_code = false;
1792
1793                input.set_caller(broadcast.new_origin);
1794
1795                let rpc = ecx.db().active_fork_url();
1796                let account = &ecx.journal().evm_state()[&broadcast.new_origin];
1797                let mut tx_req = TransactionRequestFor::<FEN>::default()
1798                    .with_from(broadcast.new_origin)
1799                    .with_kind(TxKind::Create)
1800                    .with_value(input.value())
1801                    .with_input(input.init_code())
1802                    .with_nonce(account.info.nonce);
1803                if let Some(fee_token) = self.config.fee_token {
1804                    tx_req.set_fee_token(fee_token);
1805                }
1806                self.broadcastable_transactions.push_back(BroadcastableTransaction {
1807                    rpc,
1808                    transaction: TransactionMaybeSigned::new(tx_req),
1809                });
1810
1811                input.log_debug(self, &input.scheme().unwrap_or(CreateScheme::Create));
1812            }
1813        }
1814
1815        // Allow cheatcodes from the address of the new contract
1816        let address = input.allow_cheatcodes(self, ecx);
1817
1818        // If `recordAccountAccesses` has been called, record the create
1819        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1820            recorded_account_diffs_stack.push(vec![AccountAccess {
1821                chainInfo: crate::Vm::ChainInfo {
1822                    forkId: ecx.db().active_fork_id().unwrap_or_default(),
1823                    chainId: U256::from(ecx.cfg().chain_id()),
1824                },
1825                accessor: input.caller(),
1826                account: address,
1827                kind: crate::Vm::AccountAccessKind::Create,
1828                initialized: true,
1829                oldBalance: U256::ZERO, // updated on create_end
1830                newBalance: U256::ZERO, // updated on create_end
1831                oldNonce: 0,            // new contract starts with nonce 0
1832                newNonce: 1,            // updated on create_end (contracts start with nonce 1)
1833                value: input.value(),
1834                data: input.init_code(),
1835                reverted: false,
1836                deployedCode: Bytes::new(), // updated on create_end
1837                storageAccesses: vec![],    // updated on create_end
1838                depth: curr_depth as u64,
1839            }]);
1840        }
1841
1842        None
1843    }
1844
1845    fn create_end(
1846        &mut self,
1847        ecx: &mut FoundryContextFor<'_, FEN>,
1848        call: &CreateInputs,
1849        outcome: &mut CreateOutcome,
1850    ) {
1851        let call = Some(call);
1852        let curr_depth = ecx.journal().depth();
1853
1854        // Clean up pranks
1855        if let Some(prank) = &self.get_prank(curr_depth)
1856            && curr_depth == prank.depth
1857        {
1858            ecx.tx_mut().set_caller(prank.prank_origin);
1859
1860            // Clean single-call prank once we have returned to the original depth
1861            if prank.single_call {
1862                std::mem::take(&mut self.pranks);
1863            }
1864        }
1865
1866        // Clean up broadcasts
1867        if let Some(broadcast) = &self.broadcast
1868            && curr_depth == broadcast.depth
1869        {
1870            ecx.tx_mut().set_caller(broadcast.original_origin);
1871
1872            // Clean single-call broadcast once we have returned to the original depth
1873            if broadcast.single_call {
1874                std::mem::take(&mut self.broadcast);
1875            }
1876        }
1877
1878        // Handle expected reverts
1879        if let Some(expected_revert) = &self.expected_revert
1880            && curr_depth <= expected_revert.depth
1881            && matches!(expected_revert.kind, ExpectedRevertKind::Default)
1882        {
1883            let mut expected_revert = std::mem::take(&mut self.expected_revert).unwrap();
1884            return match revert_handlers::handle_expect_revert(
1885                false,
1886                true,
1887                self.config.internal_expect_revert,
1888                &expected_revert,
1889                outcome.result.result,
1890                outcome.result.output.clone(),
1891                &self.config.available_artifacts,
1892            ) {
1893                Ok((address, retdata)) => {
1894                    expected_revert.actual_count += 1;
1895                    if expected_revert.actual_count < expected_revert.count {
1896                        self.expected_revert = Some(expected_revert.clone());
1897                    }
1898
1899                    outcome.result.result = InstructionResult::Return;
1900                    outcome.result.output = retdata;
1901                    outcome.address = address;
1902                }
1903                Err(err) => {
1904                    outcome.result.result = InstructionResult::Revert;
1905                    outcome.result.output = err.abi_encode().into();
1906                }
1907            };
1908        }
1909
1910        // If `startStateDiffRecording` has been called, update the `reverted` status of the
1911        // previous call depth's recorded accesses, if any
1912        if let Some(recorded_account_diffs_stack) = &mut self.recorded_account_diffs_stack {
1913            // The root call cannot be recorded.
1914            if curr_depth > 0
1915                && let Some(last_depth) = &mut recorded_account_diffs_stack.pop()
1916            {
1917                // Update the reverted status of all deeper calls if this call reverted, in
1918                // accordance with EVM behavior
1919                if outcome.result.is_revert() {
1920                    last_depth.iter_mut().for_each(|element| {
1921                        element.reverted = true;
1922                        element
1923                            .storageAccesses
1924                            .iter_mut()
1925                            .for_each(|storage_access| storage_access.reverted = true);
1926                    })
1927                }
1928
1929                if let Some(create_access) = last_depth.first_mut() {
1930                    // Assert that we're at the correct depth before recording post-create state
1931                    // changes. Depending on what depth the cheat was called at, there
1932                    // may not be any pending calls to update if execution has
1933                    // percolated up to a higher depth.
1934                    let depth = ecx.journal().depth();
1935                    if create_access.depth == depth as u64 {
1936                        debug_assert_eq!(
1937                            create_access.kind as u8,
1938                            crate::Vm::AccountAccessKind::Create as u8
1939                        );
1940                        if let Some(address) = outcome.address
1941                            && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1942                        {
1943                            create_access.newBalance = created_acc.data.info.balance;
1944                            create_access.newNonce = created_acc.data.info.nonce;
1945                            create_access.deployedCode = created_acc
1946                                .data
1947                                .info
1948                                .code
1949                                .clone()
1950                                .unwrap_or_default()
1951                                .original_bytes();
1952                        }
1953                    }
1954                    // Merge the last depth's AccountAccesses into the AccountAccesses at the
1955                    // current depth, or push them back onto the pending
1956                    // vector if higher depths were not recorded. This
1957                    // preserves ordering of accesses.
1958                    if let Some(last) = recorded_account_diffs_stack.last_mut() {
1959                        last.append(last_depth);
1960                    } else {
1961                        recorded_account_diffs_stack.push(last_depth.clone());
1962                    }
1963                }
1964            }
1965        }
1966
1967        // Match the create against expected_creates
1968        if !self.expected_creates.is_empty()
1969            && let (Some(address), Some(call)) = (outcome.address, call)
1970            && let Ok(created_acc) = ecx.journal_mut().load_account(address)
1971        {
1972            let bytecode = created_acc.data.info.code.clone().unwrap_or_default().original_bytes();
1973            if let Some((index, _)) =
1974                self.expected_creates.iter().find_position(|expected_create| {
1975                    expected_create.deployer == call.caller()
1976                        && expected_create.create_scheme.eq(call.scheme().into())
1977                        && expected_create.bytecode == bytecode
1978                })
1979            {
1980                self.expected_creates.swap_remove(index);
1981            }
1982        }
1983    }
1984}
1985
1986impl<FEN: FoundryEvmNetwork> InspectorExt for Cheatcodes<FEN> {
1987    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1988        if let CreateScheme::Create2 { .. } = inputs.scheme() {
1989            let target_depth = if let Some(prank) = &self.get_prank(depth) {
1990                prank.depth
1991            } else if let Some(broadcast) = &self.broadcast {
1992                broadcast.depth
1993            } else {
1994                1
1995            };
1996
1997            depth == target_depth
1998                && (self.broadcast.is_some() || self.config.always_use_create_2_factory)
1999        } else {
2000            false
2001        }
2002    }
2003
2004    fn create2_deployer(&self) -> Address {
2005        self.config.evm_opts.create2_deployer
2006    }
2007}
2008
2009impl<FEN: FoundryEvmNetwork> Cheatcodes<FEN> {
2010    #[cold]
2011    fn meter_gas(&mut self, interpreter: &mut Interpreter) {
2012        if let Some(paused_gas) = self.gas_metering.paused_frames.last() {
2013            // Keep gas constant if paused.
2014            // Make sure we record the memory changes so that memory expansion is not paused.
2015            let memory = *interpreter.gas.memory();
2016            interpreter.gas = *paused_gas;
2017            interpreter.gas.memory_mut().words_num = memory.words_num;
2018            interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost;
2019        } else {
2020            // Record frame paused gas.
2021            self.gas_metering.paused_frames.push(interpreter.gas);
2022        }
2023    }
2024
2025    #[cold]
2026    fn meter_gas_record(
2027        &mut self,
2028        interpreter: &mut Interpreter,
2029        ecx: &mut FoundryContextFor<'_, FEN>,
2030    ) {
2031        if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() {
2032            self.gas_metering.gas_records.iter_mut().for_each(|record| {
2033                let curr_depth = ecx.journal().depth();
2034                if curr_depth == record.depth {
2035                    // Skip the first opcode of the first call frame as it includes the gas cost of
2036                    // creating the snapshot.
2037                    if self.gas_metering.last_gas_used != 0 {
2038                        let gas_diff =
2039                            interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used);
2040                        record.gas_used = record.gas_used.saturating_add(gas_diff);
2041                    }
2042
2043                    // Update `last_gas_used` to the current spent gas for the next iteration to
2044                    // compare against.
2045                    self.gas_metering.last_gas_used = interpreter.gas.spent();
2046                }
2047            });
2048        }
2049    }
2050
2051    #[cold]
2052    fn meter_gas_end(&mut self, interpreter: &mut Interpreter) {
2053        // Remove recorded gas if we exit frame.
2054        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2055            && will_exit(interpreter_action)
2056        {
2057            self.gas_metering.paused_frames.pop();
2058        }
2059    }
2060
2061    #[cold]
2062    fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) {
2063        let mut gas = Gas::new(interpreter.gas.limit());
2064        gas.memory_mut().words_num = interpreter.gas.memory().words_num;
2065        gas.memory_mut().expansion_cost = interpreter.gas.memory().expansion_cost;
2066        interpreter.gas = gas;
2067        self.gas_metering.reset = false;
2068    }
2069
2070    #[cold]
2071    fn meter_gas_check(&mut self, interpreter: &mut Interpreter) {
2072        if let Some(interpreter_action) = interpreter.bytecode.action.as_ref()
2073            && will_exit(interpreter_action)
2074        {
2075            // Reset gas if spent is less than refunded.
2076            // This can happen if gas was paused / resumed or reset.
2077            // https://github.com/foundry-rs/foundry/issues/4370
2078            if interpreter.gas.spent()
2079                < u64::try_from(interpreter.gas.refunded()).unwrap_or_default()
2080            {
2081                interpreter.gas = Gas::new(interpreter.gas.limit());
2082            }
2083        }
2084    }
2085
2086    /// Generates or copies arbitrary values for storage slots.
2087    /// Invoked in inspector `step_end` (when the current opcode is not executed), if current opcode
2088    /// to execute is `SLOAD` and storage slot is cold.
2089    /// Ensures that in next step (when `SLOAD` opcode is executed) an arbitrary value is returned:
2090    /// - copies the existing arbitrary storage value (or the new generated one if no value in
2091    ///   cache) from mapped source address to the target address.
2092    /// - generates arbitrary value and saves it in target address storage.
2093    #[cold]
2094    fn arbitrary_storage_end(
2095        &mut self,
2096        interpreter: &mut Interpreter,
2097        ecx: &mut FoundryContextFor<'_, FEN>,
2098    ) {
2099        let (key, target_address) = if interpreter.bytecode.opcode() == op::SLOAD {
2100            (try_or_return!(interpreter.stack.peek(0)), interpreter.input.target_address)
2101        } else {
2102            return;
2103        };
2104
2105        let Some(value) = ecx.sload(target_address, key) else {
2106            return;
2107        };
2108
2109        if (value.is_cold && value.data.is_zero())
2110            || self.should_overwrite_arbitrary_storage(&target_address, key)
2111        {
2112            if self.has_arbitrary_storage(&target_address) {
2113                let arbitrary_value = self.rng().random();
2114                self.arbitrary_storage.as_mut().unwrap().save(
2115                    ecx,
2116                    target_address,
2117                    key,
2118                    arbitrary_value,
2119                );
2120            } else if self.is_arbitrary_storage_copy(&target_address) {
2121                let arbitrary_value = self.rng().random();
2122                self.arbitrary_storage.as_mut().unwrap().copy(
2123                    ecx,
2124                    target_address,
2125                    key,
2126                    arbitrary_value,
2127                );
2128            }
2129        }
2130    }
2131
2132    /// Records storage slots reads and writes.
2133    #[cold]
2134    fn record_accesses(&mut self, interpreter: &mut Interpreter) {
2135        let access = &mut self.accesses;
2136        match interpreter.bytecode.opcode() {
2137            op::SLOAD => {
2138                let key = try_or_return!(interpreter.stack.peek(0));
2139                access.record_read(interpreter.input.target_address, key);
2140            }
2141            op::SSTORE => {
2142                let key = try_or_return!(interpreter.stack.peek(0));
2143                access.record_write(interpreter.input.target_address, key);
2144            }
2145            _ => {}
2146        }
2147    }
2148
2149    #[cold]
2150    fn record_state_diffs(
2151        &mut self,
2152        interpreter: &mut Interpreter,
2153        ecx: &mut FoundryContextFor<'_, FEN>,
2154    ) {
2155        let Some(account_accesses) = &mut self.recorded_account_diffs_stack else { return };
2156        match interpreter.bytecode.opcode() {
2157            op::SELFDESTRUCT => {
2158                // Ensure that we're not selfdestructing a context recording was initiated on
2159                let Some(last) = account_accesses.last_mut() else { return };
2160
2161                // get previous balance, nonce and initialized status of the target account
2162                let target = try_or_return!(interpreter.stack.peek(0));
2163                let target = Address::from_word(B256::from(target));
2164                let (initialized, old_balance, old_nonce) = ecx
2165                    .journal_mut()
2166                    .load_account(target)
2167                    .map(|account| {
2168                        (
2169                            account.data.info.exists(),
2170                            account.data.info.balance,
2171                            account.data.info.nonce,
2172                        )
2173                    })
2174                    .unwrap_or_default();
2175
2176                // load balance of this account
2177                let value = ecx
2178                    .balance(interpreter.input.target_address)
2179                    .map(|b| b.data)
2180                    .unwrap_or(U256::ZERO);
2181
2182                // register access for the target account
2183                last.push(crate::Vm::AccountAccess {
2184                    chainInfo: crate::Vm::ChainInfo {
2185                        forkId: ecx.db().active_fork_id().unwrap_or_default(),
2186                        chainId: U256::from(ecx.cfg().chain_id()),
2187                    },
2188                    accessor: interpreter.input.target_address,
2189                    account: target,
2190                    kind: crate::Vm::AccountAccessKind::SelfDestruct,
2191                    initialized,
2192                    oldBalance: old_balance,
2193                    newBalance: old_balance + value,
2194                    oldNonce: old_nonce,
2195                    newNonce: old_nonce, // nonce doesn't change on selfdestruct
2196                    value,
2197                    data: Bytes::new(),
2198                    reverted: false,
2199                    deployedCode: Bytes::new(),
2200                    storageAccesses: vec![],
2201                    depth: ecx
2202                        .journal()
2203                        .depth()
2204                        .try_into()
2205                        .expect("journaled state depth exceeds u64"),
2206                });
2207            }
2208
2209            op::SLOAD => {
2210                let Some(last) = account_accesses.last_mut() else { return };
2211
2212                let key = try_or_return!(interpreter.stack.peek(0));
2213                let address = interpreter.input.target_address;
2214
2215                // Try to include present value for informational purposes, otherwise assume
2216                // it's not set (zero value)
2217                let mut present_value = U256::ZERO;
2218                // Try to load the account and the slot's present value
2219                if ecx.journal_mut().load_account(address).is_ok()
2220                    && let Some(previous) = ecx.sload(address, key)
2221                {
2222                    present_value = previous.data;
2223                }
2224                let access = crate::Vm::StorageAccess {
2225                    account: interpreter.input.target_address,
2226                    slot: key.into(),
2227                    isWrite: false,
2228                    previousValue: present_value.into(),
2229                    newValue: present_value.into(),
2230                    reverted: false,
2231                };
2232                let curr_depth =
2233                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2234                append_storage_access(last, access, curr_depth);
2235            }
2236            op::SSTORE => {
2237                let Some(last) = account_accesses.last_mut() else { return };
2238
2239                let key = try_or_return!(interpreter.stack.peek(0));
2240                let value = try_or_return!(interpreter.stack.peek(1));
2241                let address = interpreter.input.target_address;
2242                // Try to load the account and the slot's previous value, otherwise, assume it's
2243                // not set (zero value)
2244                let mut previous_value = U256::ZERO;
2245                if ecx.journal_mut().load_account(address).is_ok()
2246                    && let Some(previous) = ecx.sload(address, key)
2247                {
2248                    previous_value = previous.data;
2249                }
2250
2251                let access = crate::Vm::StorageAccess {
2252                    account: address,
2253                    slot: key.into(),
2254                    isWrite: true,
2255                    previousValue: previous_value.into(),
2256                    newValue: value.into(),
2257                    reverted: false,
2258                };
2259                let curr_depth =
2260                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2261                append_storage_access(last, access, curr_depth);
2262            }
2263
2264            // Record account accesses via the EXT family of opcodes
2265            op::EXTCODECOPY | op::EXTCODESIZE | op::EXTCODEHASH | op::BALANCE => {
2266                let kind = match interpreter.bytecode.opcode() {
2267                    op::EXTCODECOPY => crate::Vm::AccountAccessKind::Extcodecopy,
2268                    op::EXTCODESIZE => crate::Vm::AccountAccessKind::Extcodesize,
2269                    op::EXTCODEHASH => crate::Vm::AccountAccessKind::Extcodehash,
2270                    op::BALANCE => crate::Vm::AccountAccessKind::Balance,
2271                    _ => unreachable!(),
2272                };
2273                let address =
2274                    Address::from_word(B256::from(try_or_return!(interpreter.stack.peek(0))));
2275                let initialized;
2276                let balance;
2277                let nonce;
2278                if let Ok(acc) = ecx.journal_mut().load_account(address) {
2279                    initialized = acc.data.info.exists();
2280                    balance = acc.data.info.balance;
2281                    nonce = acc.data.info.nonce;
2282                } else {
2283                    initialized = false;
2284                    balance = U256::ZERO;
2285                    nonce = 0;
2286                }
2287                let curr_depth =
2288                    ecx.journal().depth().try_into().expect("journaled state depth exceeds u64");
2289                let account_access = crate::Vm::AccountAccess {
2290                    chainInfo: crate::Vm::ChainInfo {
2291                        forkId: ecx.db().active_fork_id().unwrap_or_default(),
2292                        chainId: U256::from(ecx.cfg().chain_id()),
2293                    },
2294                    accessor: interpreter.input.target_address,
2295                    account: address,
2296                    kind,
2297                    initialized,
2298                    oldBalance: balance,
2299                    newBalance: balance,
2300                    oldNonce: nonce,
2301                    newNonce: nonce, // EXT* operations don't change nonce
2302                    value: U256::ZERO,
2303                    data: Bytes::new(),
2304                    reverted: false,
2305                    deployedCode: Bytes::new(),
2306                    storageAccesses: vec![],
2307                    depth: curr_depth,
2308                };
2309                // Record the EXT* call as an account access at the current depth
2310                // (future storage accesses will be recorded in a new "Resume" context)
2311                if let Some(last) = account_accesses.last_mut() {
2312                    last.push(account_access);
2313                } else {
2314                    account_accesses.push(vec![account_access]);
2315                }
2316            }
2317            _ => {}
2318        }
2319    }
2320
2321    /// Checks to see if the current opcode can either mutate directly or expand memory.
2322    ///
2323    /// If the opcode at the current program counter is a match, check if the modified memory lies
2324    /// within the allowed ranges. If not, revert and fail the test.
2325    #[cold]
2326    fn check_mem_opcodes(&self, interpreter: &mut Interpreter, depth: u64) {
2327        let Some(ranges) = self.allowed_mem_writes.get(&depth) else {
2328            return;
2329        };
2330
2331        // The `mem_opcode_match` macro is used to match the current opcode against a list of
2332        // opcodes that can mutate memory (either directly or expansion via reading). If the
2333        // opcode is a match, the memory offsets that are being written to are checked to be
2334        // within the allowed ranges. If not, the test is failed and the transaction is
2335        // reverted. For all opcodes that can mutate memory aside from MSTORE,
2336        // MSTORE8, and MLOAD, the size and destination offset are on the stack, and
2337        // the macro expands all of these cases. For MSTORE, MSTORE8, and MLOAD, the
2338        // size of the memory write is implicit, so these cases are hard-coded.
2339        macro_rules! mem_opcode_match {
2340            ($(($opcode:ident, $offset_depth:expr, $size_depth:expr, $writes:expr)),* $(,)?) => {
2341                match interpreter.bytecode.opcode() {
2342                    ////////////////////////////////////////////////////////////////
2343                    //    OPERATIONS THAT CAN EXPAND/MUTATE MEMORY BY WRITING     //
2344                    ////////////////////////////////////////////////////////////////
2345
2346                    op::MSTORE => {
2347                        // The offset of the mstore operation is at the top of the stack.
2348                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2349
2350                        // If none of the allowed ranges contain [offset, offset + 32), memory has been
2351                        // unexpectedly mutated.
2352                        if !ranges.iter().any(|range| {
2353                            range.contains(&offset) && range.contains(&(offset + 31))
2354                        }) {
2355                            // SPECIAL CASE: When the compiler attempts to store the selector for
2356                            // `stopExpectSafeMemory`, this is allowed. It will do so at the current free memory
2357                            // pointer, which could have been updated to the exclusive upper bound during
2358                            // execution.
2359                            let value = try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>();
2360                            if value[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2361                                return
2362                            }
2363
2364                            disallowed_mem_write(offset, 32, interpreter, ranges);
2365                            return
2366                        }
2367                    }
2368                    op::MSTORE8 => {
2369                        // The offset of the mstore8 operation is at the top of the stack.
2370                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2371
2372                        // If none of the allowed ranges contain the offset, memory has been
2373                        // unexpectedly mutated.
2374                        if !ranges.iter().any(|range| range.contains(&offset)) {
2375                            disallowed_mem_write(offset, 1, interpreter, ranges);
2376                            return
2377                        }
2378                    }
2379
2380                    ////////////////////////////////////////////////////////////////
2381                    //        OPERATIONS THAT CAN EXPAND MEMORY BY READING        //
2382                    ////////////////////////////////////////////////////////////////
2383
2384                    op::MLOAD => {
2385                        // The offset of the mload operation is at the top of the stack
2386                        let offset = try_or_return!(interpreter.stack.peek(0)).saturating_to::<u64>();
2387
2388                        // If the offset being loaded is >= than the memory size, the
2389                        // memory is being expanded. If none of the allowed ranges contain
2390                        // [offset, offset + 32), memory has been unexpectedly mutated.
2391                        if offset >= interpreter.memory.size() as u64 && !ranges.iter().any(|range| {
2392                            range.contains(&offset) && range.contains(&(offset + 31))
2393                        }) {
2394                            disallowed_mem_write(offset, 32, interpreter, ranges);
2395                            return
2396                        }
2397                    }
2398
2399                    ////////////////////////////////////////////////////////////////
2400                    //          OPERATIONS WITH OFFSET AND SIZE ON STACK          //
2401                    ////////////////////////////////////////////////////////////////
2402
2403                    op::CALL => {
2404                        // The destination offset of the operation is the fifth element on the stack.
2405                        let dest_offset = try_or_return!(interpreter.stack.peek(5)).saturating_to::<u64>();
2406
2407                        // The size of the data that will be copied is the sixth element on the stack.
2408                        let size = try_or_return!(interpreter.stack.peek(6)).saturating_to::<u64>();
2409
2410                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
2411                        // memory outside of the expected ranges has been touched. If the opcode
2412                        // only reads from memory, this is okay as long as the memory is not expanded.
2413                        let fail_cond = !ranges.iter().any(|range| {
2414                            range.contains(&dest_offset) &&
2415                                range.contains(&(dest_offset + size.saturating_sub(1)))
2416                        });
2417
2418                        // If the failure condition is met, set the output buffer to a revert string
2419                        // that gives information about the allowed ranges and revert.
2420                        if fail_cond {
2421                            // SPECIAL CASE: When a call to `stopExpectSafeMemory` is performed, this is allowed.
2422                            // It allocated calldata at the current free memory pointer, and will attempt to read
2423                            // from this memory region to perform the call.
2424                            let to = Address::from_word(try_or_return!(interpreter.stack.peek(1)).to_be_bytes::<32>().into());
2425                            if to == CHEATCODE_ADDRESS {
2426                                let args_offset = try_or_return!(interpreter.stack.peek(3)).saturating_to::<usize>();
2427                                let args_size = try_or_return!(interpreter.stack.peek(4)).saturating_to::<usize>();
2428                                let memory_word = interpreter.memory.slice_len(args_offset, args_size);
2429                                if memory_word[..SELECTOR_LEN] == stopExpectSafeMemoryCall::SELECTOR {
2430                                    return
2431                                }
2432                            }
2433
2434                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
2435                            return
2436                        }
2437                    }
2438
2439                    $(op::$opcode => {
2440                        // The destination offset of the operation.
2441                        let dest_offset = try_or_return!(interpreter.stack.peek($offset_depth)).saturating_to::<u64>();
2442
2443                        // The size of the data that will be copied.
2444                        let size = try_or_return!(interpreter.stack.peek($size_depth)).saturating_to::<u64>();
2445
2446                        // If none of the allowed ranges contain [dest_offset, dest_offset + size),
2447                        // memory outside of the expected ranges has been touched. If the opcode
2448                        // only reads from memory, this is okay as long as the memory is not expanded.
2449                        let fail_cond = !ranges.iter().any(|range| {
2450                                range.contains(&dest_offset) &&
2451                                    range.contains(&(dest_offset + size.saturating_sub(1)))
2452                            }) && ($writes ||
2453                                [dest_offset, (dest_offset + size).saturating_sub(1)].into_iter().any(|offset| {
2454                                    offset >= interpreter.memory.size() as u64
2455                                })
2456                            );
2457
2458                        // If the failure condition is met, set the output buffer to a revert string
2459                        // that gives information about the allowed ranges and revert.
2460                        if fail_cond {
2461                            disallowed_mem_write(dest_offset, size, interpreter, ranges);
2462                            return
2463                        }
2464                    })*
2465
2466                    _ => {}
2467                }
2468            }
2469        }
2470
2471        // Check if the current opcode can write to memory, and if so, check if the memory
2472        // being written to is registered as safe to modify.
2473        mem_opcode_match!(
2474            (CALLDATACOPY, 0, 2, true),
2475            (CODECOPY, 0, 2, true),
2476            (RETURNDATACOPY, 0, 2, true),
2477            (EXTCODECOPY, 1, 3, true),
2478            (CALLCODE, 5, 6, true),
2479            (STATICCALL, 4, 5, true),
2480            (DELEGATECALL, 4, 5, true),
2481            (KECCAK256, 0, 1, false),
2482            (LOG0, 0, 1, false),
2483            (LOG1, 0, 1, false),
2484            (LOG2, 0, 1, false),
2485            (LOG3, 0, 1, false),
2486            (LOG4, 0, 1, false),
2487            (CREATE, 1, 2, false),
2488            (CREATE2, 1, 2, false),
2489            (RETURN, 0, 1, false),
2490            (REVERT, 0, 1, false),
2491        );
2492    }
2493
2494    #[cold]
2495    fn set_gas_limit_type(&mut self, interpreter: &mut Interpreter) {
2496        match interpreter.bytecode.opcode() {
2497            op::CREATE2 => self.dynamic_gas_limit = true,
2498            op::CALL => {
2499                // If first element of the stack is close to current remaining gas then assume
2500                // dynamic gas limit.
2501                self.dynamic_gas_limit =
2502                    try_or_return!(interpreter.stack.peek(0)) >= interpreter.gas.remaining() - 100
2503            }
2504            _ => self.dynamic_gas_limit = false,
2505        }
2506    }
2507}
2508
2509/// Helper that expands memory, stores a revert string pertaining to a disallowed memory write,
2510/// and sets the return range to the revert string's location in memory.
2511///
2512/// This will set the interpreter's next action to a return with the revert string as the output.
2513/// And trigger a revert.
2514fn disallowed_mem_write(
2515    dest_offset: u64,
2516    size: u64,
2517    interpreter: &mut Interpreter,
2518    ranges: &[Range<u64>],
2519) {
2520    let revert_string = format!(
2521        "memory write at offset 0x{:02X} of size 0x{:02X} not allowed; safe range: {}",
2522        dest_offset,
2523        size,
2524        ranges.iter().map(|r| format!("[0x{:02X}, 0x{:02X})", r.start, r.end)).join(" U ")
2525    );
2526
2527    interpreter.bytecode.set_action(InterpreterAction::new_return(
2528        InstructionResult::Revert,
2529        Bytes::from(revert_string.into_bytes()),
2530        interpreter.gas,
2531    ));
2532}
2533
2534/// Returns true if the kind of account access is a call.
2535fn access_is_call(kind: crate::Vm::AccountAccessKind) -> bool {
2536    matches!(
2537        kind,
2538        crate::Vm::AccountAccessKind::Call
2539            | crate::Vm::AccountAccessKind::StaticCall
2540            | crate::Vm::AccountAccessKind::CallCode
2541            | crate::Vm::AccountAccessKind::DelegateCall
2542    )
2543}
2544
2545/// Records a log into the recorded logs vector, if it exists.
2546fn record_logs(recorded_logs: &mut Option<Vec<Vm::Log>>, log: &Log) {
2547    if let Some(storage_recorded_logs) = recorded_logs {
2548        storage_recorded_logs.push(Vm::Log {
2549            topics: log.data.topics().to_vec(),
2550            data: log.data.data.clone(),
2551            emitter: log.address,
2552        });
2553    }
2554}
2555
2556/// Appends an AccountAccess that resumes the recording of the current context.
2557fn append_storage_access(
2558    last: &mut Vec<AccountAccess>,
2559    storage_access: crate::Vm::StorageAccess,
2560    storage_depth: u64,
2561) {
2562    // Assert that there's an existing record for the current context.
2563    if !last.is_empty() && last.first().unwrap().depth < storage_depth {
2564        // Three cases to consider:
2565        // 1. If there hasn't been a context switch since the start of this context, then add the
2566        //    storage access to the current context record.
2567        // 2. If there's an existing Resume record, then add the storage access to it.
2568        // 3. Otherwise, create a new Resume record based on the current context.
2569        if last.len() == 1 {
2570            last.first_mut().unwrap().storageAccesses.push(storage_access);
2571        } else {
2572            let last_record = last.last_mut().unwrap();
2573            if last_record.kind as u8 == crate::Vm::AccountAccessKind::Resume as u8 {
2574                last_record.storageAccesses.push(storage_access);
2575            } else {
2576                let entry = last.first().unwrap();
2577                let resume_record = crate::Vm::AccountAccess {
2578                    chainInfo: crate::Vm::ChainInfo {
2579                        forkId: entry.chainInfo.forkId,
2580                        chainId: entry.chainInfo.chainId,
2581                    },
2582                    accessor: entry.accessor,
2583                    account: entry.account,
2584                    kind: crate::Vm::AccountAccessKind::Resume,
2585                    initialized: entry.initialized,
2586                    storageAccesses: vec![storage_access],
2587                    reverted: entry.reverted,
2588                    // The remaining fields are defaults
2589                    oldBalance: U256::ZERO,
2590                    newBalance: U256::ZERO,
2591                    oldNonce: 0,
2592                    newNonce: 0,
2593                    value: U256::ZERO,
2594                    data: Bytes::new(),
2595                    deployedCode: Bytes::new(),
2596                    depth: entry.depth,
2597                };
2598                last.push(resume_record);
2599            }
2600        }
2601    }
2602}
2603
2604/// Returns the [`spec::Cheatcode`] definition for a given [`spec::CheatcodeDef`] implementor.
2605fn cheatcode_of<T: spec::CheatcodeDef>(_: &T) -> &'static spec::Cheatcode<'static> {
2606    T::CHEATCODE
2607}
2608
2609fn cheatcode_name(cheat: &spec::Cheatcode<'static>) -> &'static str {
2610    cheat.func.signature.split('(').next().unwrap()
2611}
2612
2613fn cheatcode_id(cheat: &spec::Cheatcode<'static>) -> &'static str {
2614    cheat.func.id
2615}
2616
2617fn cheatcode_signature(cheat: &spec::Cheatcode<'static>) -> &'static str {
2618    cheat.func.signature
2619}
2620
2621/// Dispatches the cheatcode call to the appropriate function.
2622fn apply_dispatch<FEN: FoundryEvmNetwork>(
2623    calls: &Vm::VmCalls,
2624    ccx: &mut CheatsCtxt<'_, '_, FEN>,
2625    executor: &mut dyn CheatcodesExecutor<FEN>,
2626) -> Result {
2627    // Extract metadata for logging/deprecation via CheatcodeDef.
2628    macro_rules! get_cheatcode {
2629        ($($variant:ident),*) => {
2630            match calls {
2631                $(Vm::VmCalls::$variant(cheat) => cheatcode_of(cheat),)*
2632            }
2633        };
2634    }
2635    let cheat = vm_calls!(get_cheatcode);
2636
2637    let _guard = debug_span!(target: "cheatcodes", "apply", id = %cheatcode_id(cheat)).entered();
2638    trace!(target: "cheatcodes", cheat = %cheatcode_signature(cheat), "applying");
2639
2640    if let spec::Status::Deprecated(replacement) = cheat.status {
2641        ccx.state.deprecated.insert(cheatcode_signature(cheat), replacement);
2642    }
2643
2644    // Monomorphized dispatch: calls apply_full directly, no trait objects.
2645    macro_rules! dispatch {
2646        ($($variant:ident),*) => {
2647            match calls {
2648                $(Vm::VmCalls::$variant(cheat) => Cheatcode::apply_full(cheat, ccx, executor),)*
2649            }
2650        };
2651    }
2652    let mut result = vm_calls!(dispatch);
2653
2654    // Format the error message to include the cheatcode name.
2655    if let Err(e) = &mut result
2656        && e.is_str()
2657    {
2658        let name = cheatcode_name(cheat);
2659        // Skip showing the cheatcode name for:
2660        // - assertions: too verbose, and can already be inferred from the error message
2661        // - `rpcUrl`: forge-std relies on it in `getChainWithUpdatedRpcUrl`
2662        if !name.contains("assert") && name != "rpcUrl" {
2663            *e = fmt_err!("vm.{name}: {e}");
2664        }
2665    }
2666
2667    trace!(
2668        target: "cheatcodes",
2669        return = %match &result {
2670            Ok(b) => hex::encode(b),
2671            Err(e) => e.to_string(),
2672        }
2673    );
2674
2675    result
2676}
2677
2678/// Helper function to check if frame execution will exit.
2679fn will_exit(action: &InterpreterAction) -> bool {
2680    match action {
2681        InterpreterAction::Return(result) => {
2682            result.result.is_ok_or_revert() || result.result.is_error()
2683        }
2684        _ => false,
2685    }
2686}