foundry_evm/inspectors/
stack.rs

1use super::{
2    Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, CustomPrintTracer, Fuzzer,
3    LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector,
4};
5use alloy_evm::{eth::EthEvmContext, Evm};
6use alloy_primitives::{
7    map::{AddressHashMap, HashMap},
8    Address, Bytes, Log, TxKind, U256,
9};
10use foundry_cheatcodes::{CheatcodesExecutor, Wallets};
11use foundry_evm_core::{
12    backend::{DatabaseExt, JournaledState},
13    evm::new_evm_with_inspector,
14    ContextExt, Env, InspectorExt,
15};
16use foundry_evm_coverage::HitMaps;
17use foundry_evm_traces::{SparsedTraceArena, TraceMode};
18use revm::{
19    context::{
20        result::{ExecutionResult, Output},
21        BlockEnv,
22    },
23    context_interface::CreateScheme,
24    interpreter::{
25        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, EOFCreateInputs,
26        EOFCreateKind, Gas, InstructionResult, Interpreter, InterpreterResult,
27    },
28    state::{Account, AccountStatus},
29    Inspector,
30};
31use std::{
32    ops::{Deref, DerefMut},
33    sync::Arc,
34};
35
36#[derive(Clone, Debug, Default)]
37#[must_use = "builders do nothing unless you call `build` on them"]
38pub struct InspectorStackBuilder {
39    /// The block environment.
40    ///
41    /// Used in the cheatcode handler to overwrite the block environment separately from the
42    /// execution block environment.
43    pub block: Option<BlockEnv>,
44    /// The gas price.
45    ///
46    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
47    /// in the execution environment.
48    pub gas_price: Option<u128>,
49    /// The cheatcodes config.
50    pub cheatcodes: Option<Arc<CheatsConfig>>,
51    /// The fuzzer inspector and its state, if it exists.
52    pub fuzzer: Option<Fuzzer>,
53    /// Whether to enable tracing and revert diagnostics.
54    pub trace_mode: TraceMode,
55    /// Whether logs should be collected.
56    pub logs: Option<bool>,
57    /// Whether coverage info should be collected.
58    pub coverage: Option<bool>,
59    /// Whether to print all opcode traces into the console. Useful for debugging the EVM.
60    pub print: Option<bool>,
61    /// The chisel state inspector.
62    pub chisel_state: Option<usize>,
63    /// Whether to enable call isolation.
64    /// In isolation mode all top-level calls are executed as a separate transaction in a separate
65    /// EVM context, enabling more precise gas accounting and transaction state changes.
66    pub enable_isolation: bool,
67    /// Whether to enable Odyssey features.
68    pub odyssey: bool,
69    /// The wallets to set in the cheatcodes context.
70    pub wallets: Option<Wallets>,
71    /// The CREATE2 deployer address.
72    pub create2_deployer: Address,
73}
74
75impl InspectorStackBuilder {
76    /// Create a new inspector stack builder.
77    #[inline]
78    pub fn new() -> Self {
79        Self::default()
80    }
81
82    /// Set the block environment.
83    #[inline]
84    pub fn block(mut self, block: BlockEnv) -> Self {
85        self.block = Some(block);
86        self
87    }
88
89    /// Set the gas price.
90    #[inline]
91    pub fn gas_price(mut self, gas_price: u128) -> Self {
92        self.gas_price = Some(gas_price);
93        self
94    }
95
96    /// Enable cheatcodes with the given config.
97    #[inline]
98    pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
99        self.cheatcodes = Some(config);
100        self
101    }
102
103    /// Set the wallets.
104    #[inline]
105    pub fn wallets(mut self, wallets: Wallets) -> Self {
106        self.wallets = Some(wallets);
107        self
108    }
109
110    /// Set the fuzzer inspector.
111    #[inline]
112    pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
113        self.fuzzer = Some(fuzzer);
114        self
115    }
116
117    /// Set the Chisel inspector.
118    #[inline]
119    pub fn chisel_state(mut self, final_pc: usize) -> Self {
120        self.chisel_state = Some(final_pc);
121        self
122    }
123
124    /// Set whether to collect logs.
125    #[inline]
126    pub fn logs(mut self, yes: bool) -> Self {
127        self.logs = Some(yes);
128        self
129    }
130
131    /// Set whether to collect coverage information.
132    #[inline]
133    pub fn coverage(mut self, yes: bool) -> Self {
134        self.coverage = Some(yes);
135        self
136    }
137
138    /// Set whether to enable the trace printer.
139    #[inline]
140    pub fn print(mut self, yes: bool) -> Self {
141        self.print = Some(yes);
142        self
143    }
144
145    /// Set whether to enable the tracer.
146    /// Revert diagnostic inspector is activated when `mode != TraceMode::None`
147    #[inline]
148    pub fn trace_mode(mut self, mode: TraceMode) -> Self {
149        if self.trace_mode < mode {
150            self.trace_mode = mode
151        }
152        self
153    }
154
155    /// Set whether to enable the call isolation.
156    /// For description of call isolation, see [`InspectorStack::enable_isolation`].
157    #[inline]
158    pub fn enable_isolation(mut self, yes: bool) -> Self {
159        self.enable_isolation = yes;
160        self
161    }
162
163    /// Set whether to enable Odyssey features.
164    /// For description of call isolation, see [`InspectorStack::enable_isolation`].
165    #[inline]
166    pub fn odyssey(mut self, yes: bool) -> Self {
167        self.odyssey = yes;
168        self
169    }
170
171    #[inline]
172    pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
173        self.create2_deployer = create2_deployer;
174        self
175    }
176
177    /// Builds the stack of inspectors to use when transacting/committing on the EVM.
178    pub fn build(self) -> InspectorStack {
179        let Self {
180            block,
181            gas_price,
182            cheatcodes,
183            fuzzer,
184            trace_mode,
185            logs,
186            coverage,
187            print,
188            chisel_state,
189            enable_isolation,
190            odyssey,
191            wallets,
192            create2_deployer,
193        } = self;
194        let mut stack = InspectorStack::new();
195
196        // inspectors
197        if let Some(config) = cheatcodes {
198            let mut cheatcodes = Cheatcodes::new(config);
199            // Set wallets if they are provided
200            if let Some(wallets) = wallets {
201                cheatcodes.set_wallets(wallets);
202            }
203            stack.set_cheatcodes(cheatcodes);
204        }
205
206        if let Some(fuzzer) = fuzzer {
207            stack.set_fuzzer(fuzzer);
208        }
209        if let Some(chisel_state) = chisel_state {
210            stack.set_chisel(chisel_state);
211        }
212
213        stack.collect_coverage(coverage.unwrap_or(false));
214        stack.collect_logs(logs.unwrap_or(true));
215        stack.print(print.unwrap_or(false));
216        stack.tracing(trace_mode);
217
218        stack.enable_isolation(enable_isolation);
219        stack.odyssey(odyssey);
220        stack.set_create2_deployer(create2_deployer);
221
222        // environment, must come after all of the inspectors
223        if let Some(block) = block {
224            stack.set_block(&block);
225        }
226        if let Some(gas_price) = gas_price {
227            stack.set_gas_price(gas_price);
228        }
229
230        stack
231    }
232}
233
234/// Helper macro to call the same method on multiple inspectors without resorting to dynamic
235/// dispatch.
236#[macro_export]
237macro_rules! call_inspectors {
238    ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {
239        $(
240            if let Some($id) = $inspector {
241                ({ #[inline(always)] #[cold] || $call })();
242            }
243        )+
244    };
245    (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $call:expr $(,)?) => {{
246        $(
247            if let Some($id) = $inspector {
248                if let Some(result) = ({ #[inline(always)] #[cold] || $call })() {
249                    return result;
250                }
251            }
252        )+
253    }};
254}
255
256/// The collected results of [`InspectorStack`].
257pub struct InspectorData {
258    pub logs: Vec<Log>,
259    pub labels: AddressHashMap<String>,
260    pub traces: Option<SparsedTraceArena>,
261    pub coverage: Option<HitMaps>,
262    pub cheatcodes: Option<Cheatcodes>,
263    pub chisel_state: Option<(Vec<U256>, Vec<u8>, InstructionResult)>,
264}
265
266/// Contains data about the state of outer/main EVM which created and invoked the inner EVM context.
267/// Used to adjust EVM state while in inner context.
268///
269/// We need this to avoid breaking changes due to EVM behavior differences in isolated vs
270/// non-isolated mode. For descriptions and workarounds for those changes see: <https://github.com/foundry-rs/foundry/pull/7186#issuecomment-1959102195>
271#[derive(Debug, Clone)]
272pub struct InnerContextData {
273    /// Origin of the transaction in the outer EVM context.
274    original_origin: Address,
275}
276
277/// An inspector that calls multiple inspectors in sequence.
278///
279/// If a call to an inspector returns a value other than [InstructionResult::Continue] (or
280/// equivalent) the remaining inspectors are not called.
281///
282/// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling
283/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. This gives
284/// us ability to create and execute separate EVM frames from inside cheatcodes while still having
285/// access to entire stack of inspectors and correctly handling traces, logs, debugging info
286/// collection, etc.
287#[derive(Clone, Debug, Default)]
288pub struct InspectorStack {
289    pub cheatcodes: Option<Cheatcodes>,
290    pub inner: InspectorStackInner,
291}
292
293/// All used inpectors besides [Cheatcodes].
294///
295/// See [`InspectorStack`].
296#[derive(Default, Clone, Debug)]
297pub struct InspectorStackInner {
298    pub chisel_state: Option<ChiselState>,
299    pub coverage: Option<CoverageCollector>,
300    pub fuzzer: Option<Fuzzer>,
301    pub log_collector: Option<LogCollector>,
302    pub printer: Option<CustomPrintTracer>,
303    pub tracer: Option<TracingInspector>,
304    pub script_execution_inspector: Option<ScriptExecutionInspector>,
305    pub enable_isolation: bool,
306    pub odyssey: bool,
307    pub create2_deployer: Address,
308    pub revert_diag: Option<RevertDiagnostic>,
309
310    /// Flag marking if we are in the inner EVM context.
311    pub in_inner_context: bool,
312    pub inner_context_data: Option<InnerContextData>,
313    pub top_frame_journal: HashMap<Address, Account>,
314}
315
316/// Struct keeping mutable references to both parts of [InspectorStack] and implementing
317/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_mut] or via
318/// [CheatcodesExecutor::get_inspector] method implemented for [InspectorStackInner].
319pub struct InspectorStackRefMut<'a> {
320    pub cheatcodes: Option<&'a mut Cheatcodes>,
321    pub inner: &'a mut InspectorStackInner,
322}
323
324impl CheatcodesExecutor for InspectorStackInner {
325    fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
326        Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
327    }
328
329    fn tracing_inspector(&mut self) -> Option<&mut Option<TracingInspector>> {
330        Some(&mut self.tracer)
331    }
332}
333
334impl InspectorStack {
335    /// Creates a new inspector stack.
336    ///
337    /// Note that the stack is empty by default, and you must add inspectors to it.
338    /// This is done by calling the `set_*` methods on the stack directly, or by building the stack
339    /// with [`InspectorStack`].
340    #[inline]
341    pub fn new() -> Self {
342        Self::default()
343    }
344
345    /// Logs the status of the inspectors.
346    pub fn log_status(&self) {
347        trace!(enabled=%{
348            let mut enabled = Vec::with_capacity(16);
349            macro_rules! push {
350                ($($id:ident),* $(,)?) => {
351                    $(
352                        if self.$id.is_some() {
353                            enabled.push(stringify!($id));
354                        }
355                    )*
356                };
357            }
358            push!(cheatcodes, chisel_state, coverage, fuzzer, log_collector, printer, tracer);
359            if self.enable_isolation {
360                enabled.push("isolation");
361            }
362            format!("[{}]", enabled.join(", "))
363        });
364    }
365
366    /// Set variables from an environment for the relevant inspectors.
367    #[inline]
368    pub fn set_env(&mut self, env: &Env) {
369        self.set_block(&env.evm_env.block_env);
370        self.set_gas_price(env.tx.gas_price);
371    }
372
373    /// Sets the block for the relevant inspectors.
374    #[inline]
375    pub fn set_block(&mut self, block: &BlockEnv) {
376        if let Some(cheatcodes) = &mut self.cheatcodes {
377            cheatcodes.block = Some(block.clone());
378        }
379    }
380
381    /// Sets the gas price for the relevant inspectors.
382    #[inline]
383    pub fn set_gas_price(&mut self, gas_price: u128) {
384        if let Some(cheatcodes) = &mut self.cheatcodes {
385            cheatcodes.gas_price = Some(gas_price);
386        }
387    }
388
389    /// Set the cheatcodes inspector.
390    #[inline]
391    pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
392        self.cheatcodes = Some(cheatcodes);
393    }
394
395    /// Set the fuzzer inspector.
396    #[inline]
397    pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
398        self.fuzzer = Some(fuzzer);
399    }
400
401    /// Set the Chisel inspector.
402    #[inline]
403    pub fn set_chisel(&mut self, final_pc: usize) {
404        self.chisel_state = Some(ChiselState::new(final_pc));
405    }
406
407    /// Set whether to enable the coverage collector.
408    #[inline]
409    pub fn collect_coverage(&mut self, yes: bool) {
410        self.coverage = yes.then(Default::default);
411    }
412
413    /// Set whether to enable call isolation.
414    #[inline]
415    pub fn enable_isolation(&mut self, yes: bool) {
416        self.enable_isolation = yes;
417    }
418
419    /// Set whether to enable call isolation.
420    #[inline]
421    pub fn odyssey(&mut self, yes: bool) {
422        self.odyssey = yes;
423    }
424
425    /// Set the CREATE2 deployer address.
426    #[inline]
427    pub fn set_create2_deployer(&mut self, deployer: Address) {
428        self.create2_deployer = deployer;
429    }
430
431    /// Set whether to enable the log collector.
432    #[inline]
433    pub fn collect_logs(&mut self, yes: bool) {
434        self.log_collector = yes.then(Default::default);
435    }
436
437    /// Set whether to enable the trace printer.
438    #[inline]
439    pub fn print(&mut self, yes: bool) {
440        self.printer = yes.then(Default::default);
441    }
442
443    /// Set whether to enable the tracer.
444    /// Revert diagnostic inspector is activated when `mode != TraceMode::None`
445    #[inline]
446    pub fn tracing(&mut self, mode: TraceMode) {
447        if mode.is_none() {
448            self.revert_diag = None;
449        } else {
450            self.revert_diag = Some(RevertDiagnostic::default());
451        }
452
453        if let Some(config) = mode.into_config() {
454            *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
455        } else {
456            self.tracer = None;
457        }
458    }
459
460    /// Set whether to enable script execution inspector.
461    #[inline]
462    pub fn script(&mut self, script_address: Address) {
463        self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
464            script_address;
465    }
466
467    /// Collects all the data gathered during inspection into a single struct.
468    #[inline]
469    pub fn collect(self) -> InspectorData {
470        let Self {
471            mut cheatcodes,
472            inner: InspectorStackInner { chisel_state, coverage, log_collector, tracer, .. },
473        } = self;
474
475        let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
476            let ignored = cheatcodes
477                .as_mut()
478                .map(|cheatcodes| {
479                    let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
480
481                    // If the last pause call was not resumed, ignore the rest of the trace
482                    if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
483                        ignored.insert(last_pause_call, (arena.nodes().len(), 0));
484                    }
485
486                    ignored
487                })
488                .unwrap_or_default();
489
490            SparsedTraceArena { arena, ignored }
491        });
492
493        InspectorData {
494            logs: log_collector.map(|logs| logs.logs).unwrap_or_default(),
495            labels: cheatcodes
496                .as_ref()
497                .map(|cheatcodes| cheatcodes.labels.clone())
498                .unwrap_or_default(),
499            traces,
500            coverage: coverage.map(|coverage| coverage.finish()),
501            cheatcodes,
502            chisel_state: chisel_state.and_then(|state| state.state),
503        }
504    }
505
506    #[inline(always)]
507    fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
508        InspectorStackRefMut { cheatcodes: self.cheatcodes.as_mut(), inner: &mut self.inner }
509    }
510}
511
512impl InspectorStackRefMut<'_> {
513    /// Adjusts the EVM data for the inner EVM context.
514    /// Should be called on the top-level call of inner context (depth == 0 &&
515    /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility
516    /// Updates tx.origin to the value before entering inner context
517    fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
518        let inner_context_data =
519            self.inner_context_data.as_ref().expect("should be called in inner context");
520        ecx.tx.caller = inner_context_data.original_origin;
521    }
522
523    fn do_call_end(
524        &mut self,
525        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
526        inputs: &CallInputs,
527        outcome: &mut CallOutcome,
528    ) -> CallOutcome {
529        let result = outcome.result.result;
530        call_inspectors!(
531            #[ret]
532            [
533                &mut self.fuzzer,
534                &mut self.tracer,
535                &mut self.cheatcodes,
536                &mut self.printer,
537                &mut self.revert_diag
538            ],
539            |inspector| {
540                let previous_outcome = outcome.clone();
541                inspector.call_end(ecx, inputs, outcome);
542
543                // If the inspector returns a different status or a revert with a non-empty message,
544                // we assume it wants to tell us something
545                let different = outcome.result.result != result ||
546                    (outcome.result.result == InstructionResult::Revert &&
547                        outcome.output() != previous_outcome.output());
548                different.then_some(outcome.clone())
549            },
550        );
551
552        outcome.clone()
553    }
554
555    fn do_create_end(
556        &mut self,
557        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
558        call: &CreateInputs,
559        outcome: &mut CreateOutcome,
560    ) -> CreateOutcome {
561        let result = outcome.result.result;
562        call_inspectors!(
563            #[ret]
564            [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
565            |inspector| {
566                let previous_outcome = outcome.clone();
567                inspector.create_end(ecx, call, outcome);
568
569                // If the inspector returns a different status or a revert with a non-empty message,
570                // we assume it wants to tell us something
571                let different = outcome.result.result != result ||
572                    (outcome.result.result == InstructionResult::Revert &&
573                        outcome.output() != previous_outcome.output());
574                different.then_some(outcome.clone())
575            },
576        );
577
578        outcome.clone()
579    }
580
581    fn do_eofcreate_end(
582        &mut self,
583        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
584        call: &EOFCreateInputs,
585        outcome: &mut CreateOutcome,
586    ) -> CreateOutcome {
587        let result = outcome.result.result;
588        call_inspectors!(
589            #[ret]
590            [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
591            |inspector| {
592                let previous_outcome = outcome.clone();
593                inspector.eofcreate_end(ecx, call, outcome);
594
595                // If the inspector returns a different status or a revert with a non-empty message,
596                // we assume it wants to tell us something
597                let different = outcome.result.result != result ||
598                    (outcome.result.result == InstructionResult::Revert &&
599                        outcome.output() != previous_outcome.output());
600                different.then_some(outcome.clone())
601            },
602        );
603
604        outcome.clone()
605    }
606
607    fn transact_inner(
608        &mut self,
609        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
610        kind: TxKind,
611        caller: Address,
612        input: Bytes,
613        gas_limit: u64,
614        value: U256,
615    ) -> (InterpreterResult, Option<Address>) {
616        let cached_env = Env::from(ecx.cfg.clone(), ecx.block.clone(), ecx.tx.clone());
617
618        ecx.block.basefee = 0;
619        ecx.tx.chain_id = Some(ecx.cfg.chain_id);
620        ecx.tx.caller = caller;
621        ecx.tx.kind = kind;
622        ecx.tx.data = input;
623        ecx.tx.value = value;
624        // Add 21000 to the gas limit to account for the base cost of transaction.
625        ecx.tx.gas_limit = gas_limit + 21000;
626
627        // If we haven't disabled gas limit checks, ensure that transaction gas limit will not
628        // exceed block gas limit.
629        if !ecx.cfg.disable_block_gas_limit {
630            ecx.tx.gas_limit = std::cmp::min(ecx.tx.gas_limit, ecx.block.gas_limit);
631        }
632        ecx.tx.gas_price = 0;
633
634        self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller });
635        self.in_inner_context = true;
636
637        let res = self.with_stack(|inspector| {
638            let (db, journal, env) = ecx.as_db_env_and_journal();
639            let mut evm = new_evm_with_inspector(db, env.to_owned(), inspector);
640
641            evm.journaled_state.state = {
642                let mut state = journal.state.clone();
643
644                for (addr, acc_mut) in &mut state {
645                    // mark all accounts cold, besides preloaded addresses
646                    if !journal.warm_preloaded_addresses.contains(addr) {
647                        acc_mut.mark_cold();
648                    }
649
650                    // mark all slots cold
651                    for slot_mut in acc_mut.storage.values_mut() {
652                        slot_mut.is_cold = true;
653                        slot_mut.original_value = slot_mut.present_value;
654                    }
655                }
656
657                state
658            };
659
660            // set depth to 1 to make sure traces are collected correctly
661            evm.journaled_state.depth = 1;
662
663            let res = evm.transact(env.tx.clone());
664
665            // need to reset the env in case it was modified via cheatcodes during execution
666            *env.cfg = evm.cfg.clone();
667            *env.block = evm.block.clone();
668
669            *env.tx = cached_env.tx;
670            env.block.basefee = cached_env.evm_env.block_env.basefee;
671
672            res
673        });
674
675        self.in_inner_context = false;
676        self.inner_context_data = None;
677
678        let mut gas = Gas::new(gas_limit);
679
680        let Ok(res) = res else {
681            // Should we match, encode and propagate error as a revert reason?
682            let result =
683                InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
684            return (result, None);
685        };
686
687        for (addr, mut acc) in res.state {
688            let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else {
689                ecx.journaled_state.state.insert(addr, acc);
690                continue
691            };
692
693            // make sure accounts that were warmed earlier do not become cold
694            if acc.status.contains(AccountStatus::Cold) &&
695                !acc_mut.status.contains(AccountStatus::Cold)
696            {
697                acc.status -= AccountStatus::Cold;
698            }
699            acc_mut.info = acc.info;
700            acc_mut.status |= acc.status;
701
702            for (key, val) in acc.storage {
703                let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
704                    acc_mut.storage.insert(key, val);
705                    continue
706                };
707                slot_mut.present_value = val.present_value;
708                slot_mut.is_cold &= val.is_cold;
709            }
710        }
711
712        let (result, address, output) = match res.result {
713            ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
714                gas.set_refund(gas_refunded as i64);
715                let _ = gas.record_cost(gas_used);
716                let address = match output {
717                    Output::Create(_, address) => address,
718                    Output::Call(_) => None,
719                };
720                (reason.into(), address, output.into_data())
721            }
722            ExecutionResult::Halt { reason, gas_used } => {
723                let _ = gas.record_cost(gas_used);
724                (reason.into(), None, Bytes::new())
725            }
726            ExecutionResult::Revert { gas_used, output } => {
727                let _ = gas.record_cost(gas_used);
728                (InstructionResult::Revert, None, output)
729            }
730        };
731        (InterpreterResult { result, output, gas }, address)
732    }
733
734    /// Moves out of references, constructs an [`InspectorStack`] and runs the given closure with
735    /// it.
736    fn with_stack<O>(&mut self, f: impl FnOnce(&mut InspectorStack) -> O) -> O {
737        let mut stack = InspectorStack {
738            cheatcodes: self
739                .cheatcodes
740                .as_deref_mut()
741                .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone()))),
742            inner: std::mem::take(self.inner),
743        };
744
745        let out = f(&mut stack);
746
747        if let Some(cheats) = self.cheatcodes.as_deref_mut() {
748            *cheats = stack.cheatcodes.take().unwrap();
749        }
750
751        *self.inner = stack.inner;
752
753        out
754    }
755
756    /// Invoked at the beginning of a new top-level (0 depth) frame.
757    fn top_level_frame_start(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
758        if self.enable_isolation {
759            // If we're in isolation mode, we need to keep track of the state at the beginning of
760            // the frame to be able to roll back on revert
761            self.top_frame_journal = ecx.journaled_state.state.clone();
762        }
763    }
764
765    /// Invoked at the end of root frame.
766    fn top_level_frame_end(
767        &mut self,
768        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
769        result: InstructionResult,
770    ) {
771        if !result.is_revert() {
772            return;
773        }
774        // Encountered a revert, since cheatcodes may have altered the evm state in such a way
775        // that violates some constraints, e.g. `deal`, we need to manually roll back on revert
776        // before revm reverts the state itself
777        if let Some(cheats) = self.cheatcodes.as_mut() {
778            cheats.on_revert(ecx);
779        }
780
781        // If we're in isolation mode, we need to rollback to state before the root frame was
782        // created We can't rely on revm's journal because it doesn't account for changes
783        // made by isolated calls
784        if self.enable_isolation {
785            ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal);
786        }
787    }
788}
789
790impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStackRefMut<'_> {
791    fn initialize_interp(
792        &mut self,
793        interpreter: &mut Interpreter,
794        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
795    ) {
796        call_inspectors!(
797            [
798                &mut self.coverage,
799                &mut self.tracer,
800                &mut self.cheatcodes,
801                &mut self.script_execution_inspector,
802                &mut self.printer
803            ],
804            |inspector| inspector.initialize_interp(interpreter, ecx),
805        );
806    }
807
808    fn step(
809        &mut self,
810        interpreter: &mut Interpreter,
811        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
812    ) {
813        call_inspectors!(
814            [
815                &mut self.fuzzer,
816                &mut self.tracer,
817                &mut self.coverage,
818                &mut self.cheatcodes,
819                &mut self.script_execution_inspector,
820                &mut self.printer,
821                &mut self.revert_diag
822            ],
823            |inspector| inspector.step(interpreter, ecx),
824        );
825    }
826
827    fn step_end(
828        &mut self,
829        interpreter: &mut Interpreter,
830        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
831    ) {
832        call_inspectors!(
833            [
834                &mut self.tracer,
835                &mut self.cheatcodes,
836                &mut self.chisel_state,
837                &mut self.printer,
838                &mut self.revert_diag
839            ],
840            |inspector| inspector.step_end(interpreter, ecx),
841        );
842    }
843
844    fn log(
845        &mut self,
846        interpreter: &mut Interpreter,
847        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
848        log: Log,
849    ) {
850        call_inspectors!(
851            [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
852            |inspector| inspector.log(interpreter, ecx, log.clone()),
853        );
854    }
855
856    fn call(
857        &mut self,
858        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
859        call: &mut CallInputs,
860    ) -> Option<CallOutcome> {
861        if self.in_inner_context && ecx.journaled_state.depth == 1 {
862            self.adjust_evm_data_for_inner_context(ecx);
863            return None;
864        }
865
866        if ecx.journaled_state.depth == 0 {
867            self.top_level_frame_start(ecx);
868        }
869
870        call_inspectors!(
871            #[ret]
872            [
873                &mut self.fuzzer,
874                &mut self.tracer,
875                &mut self.log_collector,
876                &mut self.printer,
877                &mut self.revert_diag
878            ],
879            |inspector| {
880                let mut out = None;
881                if let Some(output) = inspector.call(ecx, call) {
882                    if output.result.result != InstructionResult::Continue {
883                        out = Some(Some(output));
884                    }
885                }
886                out
887            },
888        );
889
890        if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
891            // Handle mocked functions, replace bytecode address with mock if matched.
892            if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) {
893                // Check if any mock function set for call data or if catch-all mock function set
894                // for selector.
895                if let Some(target) = mocks.get(&call.input.bytes(ecx)).or_else(|| {
896                    call.input.bytes(ecx).get(..4).and_then(|selector| mocks.get(selector))
897                }) {
898                    call.bytecode_address = *target;
899                }
900            }
901
902            if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
903                if output.result.result != InstructionResult::Continue {
904                    return Some(output);
905                }
906            }
907        }
908
909        if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 {
910            match call.scheme {
911                // Isolate CALLs
912                CallScheme::Call | CallScheme::ExtCall => {
913                    let input = call.input.bytes(ecx);
914                    let (result, _) = self.transact_inner(
915                        ecx,
916                        TxKind::Call(call.target_address),
917                        call.caller,
918                        input,
919                        call.gas_limit,
920                        call.value.get(),
921                    );
922                    return Some(CallOutcome {
923                        result,
924                        memory_offset: call.return_memory_offset.clone(),
925                    });
926                }
927                // Mark accounts and storage cold before STATICCALLs
928                CallScheme::StaticCall | CallScheme::ExtStaticCall => {
929                    let JournaledState { state, warm_preloaded_addresses, .. } =
930                        &mut ecx.journaled_state.inner;
931                    for (addr, acc_mut) in state {
932                        // Do not mark accounts and storage cold accounts with arbitrary storage.
933                        if let Some(cheatcodes) = &self.cheatcodes {
934                            if cheatcodes.has_arbitrary_storage(addr) {
935                                continue;
936                            }
937                        }
938
939                        if !warm_preloaded_addresses.contains(addr) {
940                            acc_mut.mark_cold();
941                        }
942
943                        for slot_mut in acc_mut.storage.values_mut() {
944                            slot_mut.is_cold = true;
945                        }
946                    }
947                }
948                // Process other variants as usual
949                CallScheme::CallCode | CallScheme::DelegateCall | CallScheme::ExtDelegateCall => {}
950            }
951        }
952
953        None
954    }
955
956    fn call_end(
957        &mut self,
958        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
959        inputs: &CallInputs,
960        outcome: &mut CallOutcome,
961    ) {
962        // We are processing inner context outputs in the outer context, so need to avoid processing
963        // twice.
964        if self.in_inner_context && ecx.journaled_state.depth == 1 {
965            return;
966        }
967
968        self.do_call_end(ecx, inputs, outcome);
969
970        if ecx.journaled_state.depth == 0 {
971            self.top_level_frame_end(ecx, outcome.result.result);
972        }
973    }
974
975    fn create(
976        &mut self,
977        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
978        create: &mut CreateInputs,
979    ) -> Option<CreateOutcome> {
980        if self.in_inner_context && ecx.journaled_state.depth == 1 {
981            self.adjust_evm_data_for_inner_context(ecx);
982            return None;
983        }
984
985        if ecx.journaled_state.depth == 0 {
986            self.top_level_frame_start(ecx);
987        }
988
989        call_inspectors!(
990            #[ret]
991            [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
992            |inspector| inspector.create(ecx, create).map(Some),
993        );
994
995        if !matches!(create.scheme, CreateScheme::Create2 { .. }) &&
996            self.enable_isolation &&
997            !self.in_inner_context &&
998            ecx.journaled_state.depth == 1
999        {
1000            let (result, address) = self.transact_inner(
1001                ecx,
1002                TxKind::Create,
1003                create.caller,
1004                create.init_code.clone(),
1005                create.gas_limit,
1006                create.value,
1007            );
1008            return Some(CreateOutcome { result, address });
1009        }
1010
1011        None
1012    }
1013
1014    fn create_end(
1015        &mut self,
1016        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1017        call: &CreateInputs,
1018        outcome: &mut CreateOutcome,
1019    ) {
1020        // We are processing inner context outputs in the outer context, so need to avoid processing
1021        // twice.
1022        if self.in_inner_context && ecx.journaled_state.depth == 1 {
1023            return;
1024        }
1025
1026        self.do_create_end(ecx, call, outcome);
1027
1028        if ecx.journaled_state.depth == 0 {
1029            self.top_level_frame_end(ecx, outcome.result.result);
1030        }
1031    }
1032
1033    fn eofcreate(
1034        &mut self,
1035        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1036        create: &mut EOFCreateInputs,
1037    ) -> Option<CreateOutcome> {
1038        if self.in_inner_context && ecx.journaled_state.depth == 1 {
1039            self.adjust_evm_data_for_inner_context(ecx);
1040            return None;
1041        }
1042
1043        if ecx.journaled_state.depth == 0 {
1044            self.top_level_frame_start(ecx);
1045        }
1046
1047        call_inspectors!(
1048            #[ret]
1049            [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes],
1050            |inspector| inspector.eofcreate(ecx, create).map(Some),
1051        );
1052
1053        if matches!(create.kind, EOFCreateKind::Tx { .. }) &&
1054            self.enable_isolation &&
1055            !self.in_inner_context &&
1056            ecx.journaled_state.depth == 1
1057        {
1058            let init_code = match &mut create.kind {
1059                EOFCreateKind::Tx { initdata } => initdata.clone(),
1060                EOFCreateKind::Opcode { .. } => unreachable!(),
1061            };
1062
1063            let (result, address) = self.transact_inner(
1064                ecx,
1065                TxKind::Create,
1066                create.caller,
1067                init_code,
1068                create.gas_limit,
1069                create.value,
1070            );
1071            return Some(CreateOutcome { result, address });
1072        }
1073
1074        None
1075    }
1076
1077    fn eofcreate_end(
1078        &mut self,
1079        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1080        call: &EOFCreateInputs,
1081        outcome: &mut CreateOutcome,
1082    ) {
1083        // We are processing inner context outputs in the outer context, so need to avoid processing
1084        // twice.
1085        if self.in_inner_context && ecx.journaled_state.depth == 1 {
1086            return;
1087        }
1088
1089        self.do_eofcreate_end(ecx, call, outcome);
1090
1091        if ecx.journaled_state.depth == 0 {
1092            self.top_level_frame_end(ecx, outcome.result.result);
1093        }
1094    }
1095
1096    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1097        call_inspectors!([&mut self.tracer, &mut self.printer], |inspector| {
1098            Inspector::<EthEvmContext<&mut dyn DatabaseExt>>::selfdestruct(
1099                inspector, contract, target, value,
1100            )
1101        });
1102    }
1103}
1104
1105impl InspectorExt for InspectorStackRefMut<'_> {
1106    fn should_use_create2_factory(
1107        &mut self,
1108        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1109        inputs: &CreateInputs,
1110    ) -> bool {
1111        call_inspectors!(
1112            #[ret]
1113            [&mut self.cheatcodes],
1114            |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) },
1115        );
1116
1117        false
1118    }
1119
1120    fn console_log(&mut self, msg: &str) {
1121        call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1122            inspector, msg
1123        ));
1124    }
1125
1126    fn is_odyssey(&self) -> bool {
1127        self.inner.odyssey
1128    }
1129
1130    fn create2_deployer(&self) -> Address {
1131        self.inner.create2_deployer
1132    }
1133}
1134
1135impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStack {
1136    #[inline]
1137    fn step(
1138        &mut self,
1139        interpreter: &mut Interpreter,
1140        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1141    ) {
1142        self.as_mut().step(interpreter, ecx)
1143    }
1144
1145    #[inline]
1146    fn step_end(
1147        &mut self,
1148        interpreter: &mut Interpreter,
1149        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1150    ) {
1151        self.as_mut().step_end(interpreter, ecx)
1152    }
1153
1154    fn call(
1155        &mut self,
1156        context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1157        inputs: &mut CallInputs,
1158    ) -> Option<CallOutcome> {
1159        self.as_mut().call(context, inputs)
1160    }
1161
1162    fn call_end(
1163        &mut self,
1164        context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1165        inputs: &CallInputs,
1166        outcome: &mut CallOutcome,
1167    ) {
1168        self.as_mut().call_end(context, inputs, outcome)
1169    }
1170
1171    fn create(
1172        &mut self,
1173        context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1174        create: &mut CreateInputs,
1175    ) -> Option<CreateOutcome> {
1176        self.as_mut().create(context, create)
1177    }
1178
1179    fn create_end(
1180        &mut self,
1181        context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1182        call: &CreateInputs,
1183        outcome: &mut CreateOutcome,
1184    ) {
1185        self.as_mut().create_end(context, call, outcome)
1186    }
1187
1188    fn eofcreate(
1189        &mut self,
1190        context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1191        create: &mut EOFCreateInputs,
1192    ) -> Option<CreateOutcome> {
1193        self.as_mut().eofcreate(context, create)
1194    }
1195
1196    fn eofcreate_end(
1197        &mut self,
1198        context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1199        call: &EOFCreateInputs,
1200        outcome: &mut CreateOutcome,
1201    ) {
1202        self.as_mut().eofcreate_end(context, call, outcome)
1203    }
1204
1205    fn initialize_interp(
1206        &mut self,
1207        interpreter: &mut Interpreter,
1208        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1209    ) {
1210        self.as_mut().initialize_interp(interpreter, ecx)
1211    }
1212
1213    fn log(
1214        &mut self,
1215        interpreter: &mut Interpreter,
1216        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1217        log: Log,
1218    ) {
1219        self.as_mut().log(interpreter, ecx, log)
1220    }
1221
1222    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1223        self.as_mut().selfdestruct(contract, target, value);
1224    }
1225}
1226
1227impl InspectorExt for InspectorStack {
1228    fn should_use_create2_factory(
1229        &mut self,
1230        ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1231        inputs: &CreateInputs,
1232    ) -> bool {
1233        self.as_mut().should_use_create2_factory(ecx, inputs)
1234    }
1235
1236    fn is_odyssey(&self) -> bool {
1237        self.odyssey
1238    }
1239
1240    fn create2_deployer(&self) -> Address {
1241        self.create2_deployer
1242    }
1243}
1244
1245impl<'a> Deref for InspectorStackRefMut<'a> {
1246    type Target = &'a mut InspectorStackInner;
1247
1248    fn deref(&self) -> &Self::Target {
1249        &self.inner
1250    }
1251}
1252
1253impl DerefMut for InspectorStackRefMut<'_> {
1254    fn deref_mut(&mut self) -> &mut Self::Target {
1255        &mut self.inner
1256    }
1257}
1258
1259impl Deref for InspectorStack {
1260    type Target = InspectorStackInner;
1261
1262    fn deref(&self) -> &Self::Target {
1263        &self.inner
1264    }
1265}
1266
1267impl DerefMut for InspectorStack {
1268    fn deref_mut(&mut self) -> &mut Self::Target {
1269        &mut self.inner
1270    }
1271}