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