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