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