foundry_evm/inspectors/
stack.rs

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