Skip to main content

foundry_evm/inspectors/
stack.rs

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