1use super::{
2 Cheatcodes, CheatsConfig, ChiselState, CustomPrintTracer, Fuzzer, LineCoverageCollector,
3 LogCollector, RevertDiagnostic, ScriptExecutionInspector, TempoLabels, TracingInspector,
4};
5use alloy_primitives::{
6 Address, B256, Bytes, Log, TxKind, U256,
7 map::{AddressHashMap, AddressMap},
8};
9
10use foundry_cheatcodes::{CheatcodeAnalysis, CheatcodesExecutor, NestedEvmClosure, Wallets};
11use foundry_common::compile::Analysis;
12use foundry_evm_core::{
13 FoundryBlock, FoundryTransaction, InspectorExt,
14 backend::{DatabaseError, DatabaseExt, JournaledState},
15 constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH,
16 env::FoundryContextExt,
17 evm::{
18 BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
19 FoundryEvmNetwork, SpecFor, TxEnvFor, get_create2_factory_call_inputs, with_cloned_context,
20 },
21};
22use foundry_evm_coverage::HitMaps;
23use foundry_evm_networks::NetworkConfigs;
24use foundry_evm_traces::{SparsedTraceArena, TraceMode};
25use revm::{
26 Inspector,
27 context::{
28 Block, Cfg, ContextTr, JournalTr, Transaction,
29 result::{EVMError, ExecutionResult, Output},
30 },
31 context_interface::CreateScheme,
32 handler::FrameResult,
33 inspector::JournalExt,
34 interpreter::{
35 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
36 InstructionResult, Interpreter, InterpreterResult, return_ok,
37 },
38 primitives::KECCAK_EMPTY,
39 state::{Account, AccountStatus},
40};
41use revm_inspectors::edge_cov::EdgeCovInspector;
42use std::{
43 ops::{Deref, DerefMut},
44 sync::Arc,
45};
46
47#[derive(Clone, Debug)]
48#[must_use = "builders do nothing unless you call `build` on them"]
49pub struct InspectorStackBuilder<BLOCK: Clone> {
50 pub analysis: Option<Analysis>,
52 pub block: Option<BLOCK>,
57 pub gas_price: Option<u128>,
62 pub cheatcodes: Option<Arc<CheatsConfig>>,
64 pub fuzzer: Option<Fuzzer>,
66 pub trace_mode: TraceMode,
68 pub logs: Option<bool>,
73 pub line_coverage: Option<bool>,
75 pub print: Option<bool>,
77 pub chisel_state: Option<usize>,
79 pub enable_isolation: bool,
83 pub networks: NetworkConfigs,
85 pub wallets: Option<Wallets>,
87 pub create2_deployer: Address,
89}
90
91impl<BLOCK: Clone> Default for InspectorStackBuilder<BLOCK> {
92 fn default() -> Self {
93 Self {
94 analysis: None,
95 block: None,
96 gas_price: None,
97 cheatcodes: None,
98 fuzzer: None,
99 trace_mode: TraceMode::None,
100 logs: None,
101 line_coverage: None,
102 print: None,
103 chisel_state: None,
104 enable_isolation: false,
105 networks: NetworkConfigs::default(),
106 wallets: None,
107 create2_deployer: Default::default(),
108 }
109 }
110}
111
112impl<BLOCK: Clone> InspectorStackBuilder<BLOCK> {
113 #[inline]
115 pub fn new() -> Self {
116 Self::default()
117 }
118
119 #[inline]
121 pub fn set_analysis(mut self, analysis: Analysis) -> Self {
122 self.analysis = Some(analysis);
123 self
124 }
125
126 #[inline]
128 pub fn block(mut self, block: BLOCK) -> Self {
129 self.block = Some(block);
130 self
131 }
132
133 #[inline]
135 pub const fn gas_price(mut self, gas_price: u128) -> Self {
136 self.gas_price = Some(gas_price);
137 self
138 }
139
140 #[inline]
142 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
143 self.cheatcodes = Some(config);
144 self
145 }
146
147 #[inline]
149 pub fn wallets(mut self, wallets: Wallets) -> Self {
150 self.wallets = Some(wallets);
151 self
152 }
153
154 #[inline]
156 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
157 self.fuzzer = Some(fuzzer);
158 self
159 }
160
161 #[inline]
163 pub const fn chisel_state(mut self, final_pc: usize) -> Self {
164 self.chisel_state = Some(final_pc);
165 self
166 }
167
168 #[inline]
170 pub const fn logs(mut self, live_logs: bool) -> Self {
171 self.logs = Some(live_logs);
172 self
173 }
174
175 #[inline]
177 pub const fn line_coverage(mut self, yes: bool) -> Self {
178 self.line_coverage = Some(yes);
179 self
180 }
181
182 #[inline]
184 pub const fn print(mut self, yes: bool) -> Self {
185 self.print = Some(yes);
186 self
187 }
188
189 #[inline]
192 pub fn trace_mode(mut self, mode: TraceMode) -> Self {
193 if self.trace_mode < mode {
194 self.trace_mode = mode
195 }
196 self
197 }
198
199 #[inline]
202 pub const fn enable_isolation(mut self, yes: bool) -> Self {
203 self.enable_isolation = yes;
204 self
205 }
206
207 #[inline]
209 pub const fn networks(mut self, networks: NetworkConfigs) -> Self {
210 self.networks = networks;
211 self
212 }
213
214 #[inline]
215 pub const fn create2_deployer(mut self, create2_deployer: Address) -> Self {
216 self.create2_deployer = create2_deployer;
217 self
218 }
219
220 pub fn build<FEN: FoundryEvmNetwork<EvmFactory: FoundryEvmFactory<BlockEnv = BLOCK>>>(
222 self,
223 ) -> InspectorStack<FEN> {
224 let Self {
225 analysis,
226 block,
227 gas_price,
228 cheatcodes,
229 fuzzer,
230 trace_mode,
231 logs,
232 line_coverage,
233 print,
234 chisel_state,
235 enable_isolation,
236 networks,
237 wallets,
238 create2_deployer,
239 } = self;
240 let mut stack = InspectorStack::new();
241
242 if let Some(config) = cheatcodes {
244 let mut cheatcodes = Cheatcodes::new(config);
245 if let Some(analysis) = analysis {
247 stack.set_analysis(analysis.clone());
248 cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
249 }
250 if let Some(wallets) = wallets {
252 cheatcodes.set_wallets(wallets);
253 }
254 stack.set_cheatcodes(cheatcodes);
255 }
256
257 if let Some(fuzzer) = fuzzer {
258 stack.set_fuzzer(fuzzer);
259 }
260 if let Some(chisel_state) = chisel_state {
261 stack.set_chisel(chisel_state);
262 }
263 stack.collect_line_coverage(line_coverage.unwrap_or(false));
264 stack.collect_logs(logs);
265 stack.print(print.unwrap_or(false));
266 stack.tracing(trace_mode);
267
268 stack.enable_isolation(enable_isolation);
269 stack.networks(networks);
270 stack.set_create2_deployer(create2_deployer);
271
272 if networks.is_tempo() {
273 stack.inner.tempo_labels = Some(Box::default());
274 }
275
276 if let Some(block) = block {
278 stack.set_block(block);
279 }
280 if let Some(gas_price) = gas_price {
281 stack.set_gas_price(gas_price);
282 }
283
284 stack
285 }
286}
287
288#[macro_export]
291macro_rules! call_inspectors {
292 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
293 $(
294 if let Some($id) = $inspector {
295 $crate::utils::cold_path();
296 $body;
297 }
298 )+
299 };
300 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
301 $(
302 if let Some($id) = $inspector {
303 $crate::utils::cold_path();
304 if let Some(result) = $body {
305 return result;
306 }
307 }
308 )+
309 }};
310}
311
312pub struct InspectorData<FEN: FoundryEvmNetwork> {
314 pub logs: Vec<Log>,
315 pub labels: AddressHashMap<String>,
316 pub traces: Option<SparsedTraceArena>,
317 pub line_coverage: Option<HitMaps>,
318 pub edge_coverage: Option<Vec<u8>>,
319 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
320 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
321 pub reverter: Option<Address>,
322}
323
324#[derive(Debug, Clone)]
330pub struct InnerContextData {
331 original_origin: Address,
333}
334
335#[derive(Clone, Debug)]
346pub struct InspectorStack<FEN: FoundryEvmNetwork = EthEvmNetwork> {
347 #[allow(clippy::type_complexity)]
348 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
349 pub inner: InspectorStackInner,
350}
351
352#[derive(Default, Clone, Debug)]
356pub struct InspectorStackInner {
357 pub analysis: Option<Analysis>,
359
360 pub chisel_state: Option<Box<ChiselState>>,
364 pub edge_coverage: Option<Box<EdgeCovInspector>>,
365 pub fuzzer: Option<Box<Fuzzer>>,
366 pub line_coverage: Option<Box<LineCoverageCollector>>,
367 pub log_collector: Option<Box<LogCollector>>,
368 pub printer: Option<Box<CustomPrintTracer>>,
369 pub revert_diag: Option<Box<RevertDiagnostic>>,
370 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
371 pub tempo_labels: Option<Box<TempoLabels>>,
372 pub tracer: Option<Box<TracingInspector>>,
373
374 pub sancov_edges: bool,
377 pub sancov_trace_cmp: bool,
379 pub enable_isolation: bool,
380 pub networks: NetworkConfigs,
381 pub create2_deployer: Address,
382 pub in_inner_context: bool,
384 pub inner_context_data: Option<InnerContextData>,
385 pub top_frame_journal: AddressMap<Account>,
386 pub reverter: Option<Address>,
388 pub pending_create2_redirects: Vec<usize>,
391 pub pending_create2_error: Option<CreateOutcome>,
394}
395
396pub struct InspectorStackRefMut<'a, FEN: FoundryEvmNetwork = EthEvmNetwork> {
399 pub cheatcodes: Option<&'a mut Cheatcodes<FEN>>,
400 pub inner: &'a mut InspectorStackInner,
401}
402
403impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for InspectorStackInner {
404 fn with_nested_evm(
405 &mut self,
406 cheats: &mut Cheatcodes<FEN>,
407 ecx: &mut FoundryContextFor<'_, FEN>,
408 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
409 ) -> Result<(), EVMError<DatabaseError>> {
410 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
411 with_cloned_context(ecx, |db, evm_env, journal_inner| {
412 let mut evm =
413 FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
414 *evm.journal_inner_mut() = journal_inner;
415 f(&mut *evm)?;
416 let sub_inner = evm.journal_inner_mut().clone();
417 let sub_evm_env = evm.to_evm_env();
418 Ok((sub_evm_env, sub_inner))
419 })
420 }
421
422 fn with_fresh_nested_evm(
423 &mut self,
424 cheats: &mut Cheatcodes<FEN>,
425 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
426 evm_env: EvmEnvFor<FEN>,
427 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
428 ) -> Result<EvmEnvFor<FEN>, EVMError<DatabaseError>> {
429 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
430 let mut evm =
431 FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
432 f(&mut *evm)?;
433 Ok(evm.to_evm_env())
434 }
435
436 fn transact_on_db(
437 &mut self,
438 cheats: &mut Cheatcodes<FEN>,
439 ecx: &mut FoundryContextFor<'_, FEN>,
440 fork_id: Option<U256>,
441 transaction: B256,
442 ) -> eyre::Result<()> {
443 let evm_env = ecx.evm_clone();
444 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
445 let (db, inner) = ecx.db_journal_inner_mut();
446 db.transact(fork_id, transaction, evm_env, inner, &mut inspector)
447 }
448
449 fn transact_from_tx_on_db(
450 &mut self,
451 cheats: &mut Cheatcodes<FEN>,
452 ecx: &mut FoundryContextFor<'_, FEN>,
453 tx_env: TxEnvFor<FEN>,
454 ) -> eyre::Result<()> {
455 let evm_env = ecx.evm_clone();
456 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
457 let (db, inner) = ecx.db_journal_inner_mut();
458 db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
459 }
460
461 fn console_log(&mut self, msg: &str) {
462 if let Some(ref mut collector) = self.log_collector {
463 InspectorExt::console_log(&mut **collector, msg);
464 }
465 }
466
467 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
468 self.tracer.as_deref_mut()
469 }
470
471 fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
472 self.in_inner_context = enabled;
473 self.inner_context_data = enabled.then(|| InnerContextData {
474 original_origin: original_origin.expect("origin required when enabling inner ctx"),
475 });
476 }
477}
478
479impl<FEN: FoundryEvmNetwork> Default for InspectorStack<FEN> {
480 fn default() -> Self {
481 Self::new()
482 }
483}
484
485impl<FEN: FoundryEvmNetwork> InspectorStack<FEN> {
486 #[inline]
492 pub fn new() -> Self {
493 Self { cheatcodes: None, inner: InspectorStackInner::default() }
494 }
495
496 #[inline]
498 pub fn set_analysis(&mut self, analysis: Analysis) {
499 self.analysis = Some(analysis);
500 }
501
502 #[inline]
504 pub fn set_block(&mut self, block: BlockEnvFor<FEN>) {
505 if let Some(cheatcodes) = &mut self.cheatcodes {
506 cheatcodes.block = Some(block);
507 }
508 }
509
510 #[inline]
512 pub fn set_gas_price(&mut self, gas_price: u128) {
513 if let Some(cheatcodes) = &mut self.cheatcodes {
514 cheatcodes.gas_price = Some(gas_price);
515 }
516 }
517
518 #[inline]
520 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes<FEN>) {
521 self.cheatcodes = Some(cheatcodes.into());
522 }
523
524 #[inline]
526 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
527 self.fuzzer = Some(fuzzer.into());
528 }
529
530 #[inline]
532 pub fn set_chisel(&mut self, final_pc: usize) {
533 self.chisel_state = Some(ChiselState::new(final_pc).into());
534 }
535
536 #[inline]
538 pub fn collect_line_coverage(&mut self, yes: bool) {
539 self.line_coverage = yes.then(Default::default);
540 }
541
542 #[inline]
544 pub fn collect_edge_coverage(&mut self, yes: bool) {
545 self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
547 }
548
549 #[inline]
551 pub const fn collect_sancov_edges(&mut self, yes: bool) {
552 self.inner.sancov_edges = yes;
553 }
554
555 #[inline]
557 pub const fn collect_sancov_trace_cmp(&mut self, yes: bool) {
558 self.inner.sancov_trace_cmp = yes;
559 }
560
561 #[inline]
563 pub const fn enable_isolation(&mut self, yes: bool) {
564 self.inner.enable_isolation = yes;
565 }
566
567 #[inline]
569 pub const fn networks(&mut self, networks: NetworkConfigs) {
570 self.inner.networks = networks;
571 }
572
573 #[inline]
575 pub fn set_create2_deployer(&mut self, deployer: Address) {
576 self.create2_deployer = deployer;
577 }
578
579 #[inline]
584 pub fn collect_logs(&mut self, live_logs: Option<bool>) {
585 self.log_collector = live_logs.map(|live_logs| {
586 Box::new(if live_logs {
587 LogCollector::LiveLogs
588 } else {
589 LogCollector::Capture { logs: Vec::new() }
590 })
591 });
592 }
593
594 #[inline]
596 pub fn print(&mut self, yes: bool) {
597 self.printer = yes.then(Default::default);
598 }
599
600 #[inline]
603 pub fn tracing(&mut self, mode: TraceMode) {
604 self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
605
606 if let Some(config) = mode.into_config() {
607 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
608 } else {
609 self.tracer = None;
610 }
611 }
612
613 #[inline]
615 pub fn script(&mut self, script_address: Address) {
616 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
617 script_address;
618 }
619
620 #[inline(always)]
621 fn as_mut(&mut self) -> InspectorStackRefMut<'_, FEN> {
622 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
623 }
624
625 pub fn collect(self) -> InspectorData<FEN> {
627 let Self {
628 mut cheatcodes,
629 inner:
630 InspectorStackInner {
631 chisel_state,
632 line_coverage,
633 edge_coverage,
634 log_collector,
635 tempo_labels,
636 tracer,
637 reverter,
638 ..
639 },
640 } = self;
641
642 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
643 let ignored = cheatcodes
644 .as_mut()
645 .map(|cheatcodes| {
646 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
647
648 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
650 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
651 }
652
653 ignored
654 })
655 .unwrap_or_default();
656
657 SparsedTraceArena { arena, ignored }
658 });
659
660 InspectorData {
661 logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
662 labels: {
663 let mut labels = cheatcodes.as_ref().map(|c| c.labels.clone()).unwrap_or_default();
664 if let Some(tempo_labels) = tempo_labels {
665 labels.extend(tempo_labels.labels);
666 }
667 labels
668 },
669 traces,
670 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
671 edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()),
672 cheatcodes,
673 chisel_state: chisel_state.and_then(|state| state.state),
674 reverter,
675 }
676 }
677}
678
679impl<FEN: FoundryEvmNetwork> InspectorStackRefMut<'_, FEN> {
680 fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
685 let inner_context_data =
686 self.inner_context_data.as_ref().expect("should be called in inner context");
687 ecx.tx_mut().set_caller(inner_context_data.original_origin);
688 }
689
690 fn do_call_end(
691 &mut self,
692 ecx: &mut FoundryContextFor<'_, FEN>,
693 inputs: &CallInputs,
694 outcome: &mut CallOutcome,
695 ) {
696 let result = outcome.result.result;
697 call_inspectors!(
698 #[ret]
699 [
700 &mut self.fuzzer,
701 &mut self.tracer,
702 &mut self.cheatcodes,
703 &mut self.printer,
704 &mut self.revert_diag
705 ],
706 |inspector| {
707 let previous_outcome = outcome.clone();
708 inspector.call_end(ecx, inputs, outcome);
709
710 let different = outcome.result.result != result
713 || (outcome.result.result == InstructionResult::Revert
714 && outcome.output() != previous_outcome.output());
715 different.then_some(())
716 },
717 );
718
719 if result.is_revert() && self.reverter.is_none() {
721 self.reverter = Some(inputs.target_address);
722 }
723 }
724
725 fn do_create_end(
726 &mut self,
727 ecx: &mut FoundryContextFor<'_, FEN>,
728 call: &CreateInputs,
729 outcome: &mut CreateOutcome,
730 ) {
731 let result = outcome.result.result;
732 call_inspectors!(
733 #[ret]
734 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
735 |inspector| {
736 let previous_outcome = outcome.clone();
737 inspector.create_end(ecx, call, outcome);
738
739 let different = outcome.result.result != result
742 || (outcome.result.result == InstructionResult::Revert
743 && outcome.output() != previous_outcome.output());
744 different.then_some(())
745 },
746 );
747 }
748
749 fn transact_inner(
750 &mut self,
751 ecx: &mut FoundryContextFor<'_, FEN>,
752 kind: TxKind,
753 caller: Address,
754 input: Bytes,
755 gas_limit: u64,
756 value: U256,
757 ) -> (InterpreterResult, Option<Address>) {
758 let cached_evm_env = ecx.evm_clone();
759 let cached_tx_env = ecx.tx_clone();
760
761 ecx.block_mut().set_basefee(0);
762
763 let chain_id = ecx.cfg().chain_id();
764 ecx.tx_mut().set_chain_id(Some(chain_id));
765 ecx.tx_mut().set_caller(caller);
766 ecx.tx_mut().set_kind(kind);
767 ecx.tx_mut().set_data(input);
768 ecx.tx_mut().set_value(value);
769 ecx.tx_mut().set_gas_limit(gas_limit + 21000);
771
772 if !ecx.cfg().is_block_gas_limit_disabled() {
775 let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
776 ecx.tx_mut().set_gas_limit(gas_limit);
777 }
778 ecx.tx_mut().set_gas_price(0);
779
780 self.inner_context_data =
781 Some(InnerContextData { original_origin: cached_tx_env.caller() });
782 self.in_inner_context = true;
783
784 let evm_env = ecx.evm_clone();
785 let tx_env = ecx.tx_clone();
786
787 let res = self.with_inspector(|mut inspector| {
788 let (res, nested_env) = {
789 let (db, journal) = ecx.db_journal_inner_mut();
790 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(
791 db,
792 evm_env,
793 &mut inspector,
794 );
795
796 evm.journal_inner_mut().state = {
797 let mut state = journal.state.clone();
798
799 for (addr, acc_mut) in &mut state {
800 if journal.warm_addresses.is_cold(addr) {
802 acc_mut.mark_cold();
803 }
804
805 for slot_mut in acc_mut.storage.values_mut() {
807 slot_mut.is_cold = true;
808 slot_mut.original_value = slot_mut.present_value;
809 }
810 }
811
812 state
813 };
814
815 evm.journal_inner_mut().depth = 1;
817
818 let res = evm.transact_raw(tx_env);
819 let nested_evm_env = evm.to_evm_env();
820 (res, nested_evm_env)
821 };
822
823 let mut restored_evm_env = nested_env;
826 restored_evm_env.block_env.set_basefee(cached_evm_env.block_env.basefee());
827 ecx.set_evm(restored_evm_env);
828 ecx.set_tx(cached_tx_env);
829
830 res
831 });
832
833 self.in_inner_context = false;
834 self.inner_context_data = None;
835
836 let mut gas = Gas::new(gas_limit);
837
838 let Ok(res) = res else {
839 let result =
841 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
842 return (result, None);
843 };
844
845 for (addr, mut acc) in res.state {
846 let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
847 ecx.journal_mut().evm_state_mut().insert(addr, acc);
848 continue;
849 };
850
851 if acc.status.contains(AccountStatus::Cold)
853 && !acc_mut.status.contains(AccountStatus::Cold)
854 {
855 acc.status -= AccountStatus::Cold;
856 }
857 acc_mut.info = acc.info;
858 acc_mut.status |= acc.status;
859
860 for (key, val) in acc.storage {
861 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
862 acc_mut.storage.insert(key, val);
863 continue;
864 };
865 slot_mut.present_value = val.present_value;
866 slot_mut.is_cold &= val.is_cold;
867 }
868 }
869
870 let (result, address, output) = match res.result {
871 ExecutionResult::Success { reason, gas: result_gas, logs: _, output } => {
872 gas.set_refund(result_gas.final_refunded() as i64);
873 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
874 let address = match output {
875 Output::Create(_, address) => address,
876 Output::Call(_) => None,
877 };
878 (reason.into(), address, output.into_data())
879 }
880 ExecutionResult::Halt { reason, gas: result_gas, .. } => {
881 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
882 (InstructionResult::from(reason), None, Bytes::new())
883 }
884 ExecutionResult::Revert { gas: result_gas, output, .. } => {
885 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
886 (InstructionResult::Revert, None, output)
887 }
888 };
889 (InterpreterResult { result, output, gas }, address)
890 }
891
892 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_, FEN>) -> O) -> O {
895 let mut cheatcodes = self
896 .cheatcodes
897 .as_deref_mut()
898 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
899 let mut inner = std::mem::take(self.inner);
900
901 let saved_create2_redirects = std::mem::take(&mut inner.pending_create2_redirects);
904
905 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
906
907 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
908 *cheats = cheatcodes.unwrap();
909 }
910
911 inner.pending_create2_redirects = saved_create2_redirects;
912 *self.inner = inner;
913
914 out
915 }
916
917 fn top_level_frame_start(&mut self, ecx: &mut FoundryContextFor<'_, FEN>) {
919 if self.enable_isolation {
920 self.top_frame_journal.clone_from(ecx.journal().evm_state());
923 }
924 }
925
926 fn top_level_frame_end(
928 &mut self,
929 ecx: &mut FoundryContextFor<'_, FEN>,
930 result: InstructionResult,
931 ) {
932 if !result.is_revert() {
933 return;
934 }
935 if let Some(cheats) = self.cheatcodes.as_mut() {
939 cheats.on_revert(ecx);
940 }
941
942 if self.enable_isolation {
946 *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
947 }
948 }
949
950 #[inline(always)]
956 fn step_inlined(
957 &mut self,
958 interpreter: &mut Interpreter,
959 ecx: &mut FoundryContextFor<'_, FEN>,
960 ) {
961 call_inspectors!(
962 [
963 &mut self.edge_coverage,
965 &mut self.fuzzer,
966 &mut self.line_coverage,
967 &mut self.printer,
968 &mut self.revert_diag,
969 &mut self.script_execution_inspector,
970 &mut self.tracer,
971 &mut self.cheatcodes,
973 ],
974 |inspector| (**inspector).step(interpreter, ecx),
975 );
976 }
977
978 #[inline(always)]
979 fn step_end_inlined(
980 &mut self,
981 interpreter: &mut Interpreter,
982 ecx: &mut FoundryContextFor<'_, FEN>,
983 ) {
984 call_inspectors!(
985 [
986 &mut self.chisel_state,
988 &mut self.printer,
989 &mut self.revert_diag,
990 &mut self.tracer,
991 &mut self.cheatcodes,
993 ],
994 |inspector| (**inspector).step_end(interpreter, ecx),
995 );
996 }
997}
998
999impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>>
1000 for InspectorStackRefMut<'_, FEN>
1001{
1002 fn initialize_interp(
1003 &mut self,
1004 interpreter: &mut Interpreter,
1005 ecx: &mut FoundryContextFor<'_, FEN>,
1006 ) {
1007 call_inspectors!(
1008 [
1009 &mut self.line_coverage,
1010 &mut self.tracer,
1011 &mut self.cheatcodes,
1012 &mut self.script_execution_inspector,
1013 &mut self.printer
1014 ],
1015 |inspector| inspector.initialize_interp(interpreter, ecx),
1016 );
1017 }
1018
1019 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1020 self.step_inlined(interpreter, ecx);
1021 }
1022
1023 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1024 self.step_end_inlined(interpreter, ecx);
1025 }
1026
1027 #[allow(clippy::redundant_clone)]
1028 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1029 call_inspectors!(
1030 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
1031 |inspector| inspector.log(ecx, log.clone()),
1032 );
1033 }
1034
1035 #[allow(clippy::redundant_clone)]
1036 fn log_full(
1037 &mut self,
1038 interpreter: &mut Interpreter,
1039 ecx: &mut FoundryContextFor<'_, FEN>,
1040 log: Log,
1041 ) {
1042 call_inspectors!(
1043 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
1044 |inspector| inspector.log_full(interpreter, ecx, log.clone()),
1045 );
1046 }
1047
1048 fn frame_start(
1049 &mut self,
1050 ecx: &mut FoundryContextFor<'_, FEN>,
1051 frame_input: &mut FrameInput,
1052 ) -> Option<FrameResult> {
1053 if let FrameInput::Create(inputs) = frame_input
1054 && let CreateScheme::Create2 { salt } = inputs.scheme()
1055 && self.should_use_create2_factory(ecx.journal().depth(), inputs)
1056 {
1057 let gas_limit = inputs.gas_limit();
1058 let create2_deployer = self.create2_deployer();
1059
1060 let code_hash = ecx.journal_mut().load_account(create2_deployer).ok()?.info.code_hash;
1062 if code_hash == KECCAK_EMPTY {
1063 self.inner.pending_create2_error = Some(CreateOutcome {
1066 result: InterpreterResult {
1067 result: InstructionResult::Revert,
1068 output: Bytes::from(
1069 format!("missing CREATE2 deployer: {create2_deployer}").into_bytes(),
1070 ),
1071 gas: Gas::new(gas_limit),
1072 },
1073 address: None,
1074 });
1075 return None;
1076 } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
1077 self.inner.pending_create2_error = Some(CreateOutcome {
1078 result: InterpreterResult {
1079 result: InstructionResult::Revert,
1080 output: "invalid CREATE2 deployer bytecode".into(),
1081 gas: Gas::new(gas_limit),
1082 },
1083 address: None,
1084 });
1085 return None;
1086 }
1087
1088 let call_inputs =
1089 get_create2_factory_call_inputs(salt, inputs, create2_deployer, ecx.journal_mut())
1090 .ok()?;
1091
1092 self.inner.pending_create2_redirects.push(ecx.journal().depth());
1094
1095 *frame_input = FrameInput::Call(Box::new(call_inputs));
1097 }
1098
1099 None
1100 }
1101
1102 fn frame_end(
1103 &mut self,
1104 ecx: &mut FoundryContextFor<'_, FEN>,
1105 _frame_input: &FrameInput,
1106 frame_result: &mut FrameResult,
1107 ) {
1108 let depth = ecx.journal().depth();
1109 if self.inner.pending_create2_redirects.last().copied() != Some(depth) {
1110 return;
1111 }
1112
1113 self.inner.pending_create2_redirects.pop();
1114
1115 let FrameResult::Call(call) = frame_result else {
1116 debug_assert!(false, "pending CREATE2 redirect ended with non-call result");
1117 return;
1118 };
1119
1120 let address = match call.instruction_result() {
1121 return_ok!() => Address::try_from(call.output().as_ref())
1122 .map_err(|_| {
1123 call.result = InterpreterResult {
1124 result: InstructionResult::Revert,
1125 output: "invalid CREATE2 factory output".into(),
1126 gas: Gas::new(call.result.gas.limit()),
1127 };
1128 })
1129 .ok(),
1130 _ => None,
1131 };
1132
1133 *frame_result = FrameResult::Create(CreateOutcome { result: call.result.clone(), address });
1134 }
1135
1136 fn call(
1137 &mut self,
1138 ecx: &mut FoundryContextFor<'_, FEN>,
1139 call: &mut CallInputs,
1140 ) -> Option<CallOutcome> {
1141 if self.in_inner_context && ecx.journal().depth() == 1 {
1142 self.adjust_evm_data_for_inner_context(ecx);
1143 return None;
1144 }
1145
1146 if ecx.journal().depth() == 0 {
1147 self.top_level_frame_start(ecx);
1148 }
1149
1150 call_inspectors!(
1151 #[ret]
1152 [
1153 &mut self.fuzzer,
1154 &mut self.tracer,
1155 &mut self.log_collector,
1156 &mut self.printer,
1157 &mut self.revert_diag,
1158 &mut self.tempo_labels
1159 ],
1160 |inspector| {
1161 let mut out = None;
1162 if let Some(output) = inspector.call(ecx, call) {
1163 out = Some(Some(output));
1164 }
1165 out
1166 },
1167 );
1168
1169 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1170 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1172 let input_bytes = call.input.bytes(ecx);
1173 if let Some(target) = mocks
1176 .get(&input_bytes)
1177 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1178 {
1179 call.bytecode_address = *target;
1180
1181 let target = ecx
1182 .journal_mut()
1183 .load_account_with_code(*target)
1184 .expect("failed to load account");
1185 call.known_bytecode =
1186 (target.info.code_hash, target.info.code.clone().unwrap_or_default());
1187 }
1188 }
1189
1190 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1191 return Some(output);
1192 }
1193 }
1194
1195 if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1196 match call.scheme {
1197 CallScheme::Call => {
1199 let input = call.input.bytes(ecx);
1200 let (result, _) = self.transact_inner(
1201 ecx,
1202 TxKind::Call(call.target_address),
1203 call.caller,
1204 input,
1205 call.gas_limit,
1206 call.value.get(),
1207 );
1208 return Some(CallOutcome {
1209 result,
1210 memory_offset: call.return_memory_offset.clone(),
1211 was_precompile_called: true,
1212 precompile_call_logs: vec![],
1213 });
1214 }
1215 CallScheme::StaticCall => {
1217 let (_, journal_inner) = ecx.db_journal_inner_mut();
1218 let JournaledState { state, warm_addresses, .. } = journal_inner;
1219 for (addr, acc_mut) in state {
1220 if let Some(cheatcodes) = &self.cheatcodes
1222 && cheatcodes.has_arbitrary_storage(addr)
1223 {
1224 continue;
1225 }
1226
1227 if warm_addresses.is_cold(addr) {
1228 acc_mut.mark_cold();
1229 }
1230
1231 for slot_mut in acc_mut.storage.values_mut() {
1232 slot_mut.is_cold = true;
1233 }
1234 }
1235 }
1236 CallScheme::CallCode | CallScheme::DelegateCall => {}
1238 }
1239 }
1240
1241 None
1242 }
1243
1244 fn call_end(
1245 &mut self,
1246 ecx: &mut FoundryContextFor<'_, FEN>,
1247 inputs: &CallInputs,
1248 outcome: &mut CallOutcome,
1249 ) {
1250 if self.in_inner_context && ecx.journal().depth() == 1 {
1253 return;
1254 }
1255
1256 self.do_call_end(ecx, inputs, outcome);
1257
1258 if ecx.journal().depth() == 0 {
1259 self.top_level_frame_end(ecx, outcome.result.result);
1260 }
1261 }
1262
1263 fn create(
1264 &mut self,
1265 ecx: &mut FoundryContextFor<'_, FEN>,
1266 create: &mut CreateInputs,
1267 ) -> Option<CreateOutcome> {
1268 if self.in_inner_context && ecx.journal().depth() == 1 {
1269 self.adjust_evm_data_for_inner_context(ecx);
1270 return None;
1271 }
1272
1273 if ecx.journal().depth() == 0 {
1274 self.top_level_frame_start(ecx);
1275 }
1276
1277 call_inspectors!(
1278 #[ret]
1279 [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1280 |inspector| inspector.create(ecx, create).map(Some),
1281 );
1282
1283 if let Some(error) = self.inner.pending_create2_error.take() {
1286 return Some(error);
1287 }
1288
1289 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1290 && self.enable_isolation
1291 && !self.in_inner_context
1292 && ecx.journal().depth() == 1
1293 {
1294 let (result, address) = self.transact_inner(
1295 ecx,
1296 TxKind::Create,
1297 create.caller(),
1298 create.init_code().clone(),
1299 create.gas_limit(),
1300 create.value(),
1301 );
1302 return Some(CreateOutcome { result, address });
1303 }
1304
1305 None
1306 }
1307
1308 fn create_end(
1309 &mut self,
1310 ecx: &mut FoundryContextFor<'_, FEN>,
1311 call: &CreateInputs,
1312 outcome: &mut CreateOutcome,
1313 ) {
1314 if self.in_inner_context && ecx.journal().depth() == 1 {
1317 return;
1318 }
1319
1320 self.do_create_end(ecx, call, outcome);
1321
1322 if ecx.journal().depth() == 0 {
1323 self.top_level_frame_end(ecx, outcome.result.result);
1324 }
1325 }
1326
1327 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1328 call_inspectors!([&mut self.printer], |inspector| {
1329 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1330 inspector, contract, target, value,
1331 )
1332 });
1333 }
1334}
1335
1336impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStackRefMut<'_, FEN> {
1337 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1338 call_inspectors!(
1339 #[ret]
1340 [&mut self.cheatcodes],
1341 |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1342 );
1343
1344 false
1345 }
1346
1347 fn console_log(&mut self, msg: &str) {
1348 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1349 inspector, msg
1350 ));
1351 }
1352
1353 fn get_networks(&self) -> NetworkConfigs {
1354 self.inner.networks
1355 }
1356
1357 fn create2_deployer(&self) -> Address {
1358 self.inner.create2_deployer
1359 }
1360}
1361
1362impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for InspectorStack<FEN> {
1363 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1364 self.as_mut().step_inlined(interpreter, ecx)
1365 }
1366
1367 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1368 self.as_mut().step_end_inlined(interpreter, ecx)
1369 }
1370
1371 fn call(
1372 &mut self,
1373 context: &mut FoundryContextFor<'_, FEN>,
1374 inputs: &mut CallInputs,
1375 ) -> Option<CallOutcome> {
1376 self.as_mut().call(context, inputs)
1377 }
1378
1379 fn call_end(
1380 &mut self,
1381 context: &mut FoundryContextFor<'_, FEN>,
1382 inputs: &CallInputs,
1383 outcome: &mut CallOutcome,
1384 ) {
1385 self.as_mut().call_end(context, inputs, outcome)
1386 }
1387
1388 fn create(
1389 &mut self,
1390 context: &mut FoundryContextFor<'_, FEN>,
1391 create: &mut CreateInputs,
1392 ) -> Option<CreateOutcome> {
1393 self.as_mut().create(context, create)
1394 }
1395
1396 fn create_end(
1397 &mut self,
1398 context: &mut FoundryContextFor<'_, FEN>,
1399 call: &CreateInputs,
1400 outcome: &mut CreateOutcome,
1401 ) {
1402 self.as_mut().create_end(context, call, outcome)
1403 }
1404
1405 fn initialize_interp(
1406 &mut self,
1407 interpreter: &mut Interpreter,
1408 ecx: &mut FoundryContextFor<'_, FEN>,
1409 ) {
1410 self.as_mut().initialize_interp(interpreter, ecx)
1411 }
1412
1413 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1414 self.as_mut().log(ecx, log)
1415 }
1416
1417 fn log_full(
1418 &mut self,
1419 interpreter: &mut Interpreter,
1420 ecx: &mut FoundryContextFor<'_, FEN>,
1421 log: Log,
1422 ) {
1423 self.as_mut().log_full(interpreter, ecx, log)
1424 }
1425
1426 fn frame_start(
1427 &mut self,
1428 context: &mut FoundryContextFor<'_, FEN>,
1429 frame_input: &mut FrameInput,
1430 ) -> Option<FrameResult> {
1431 self.as_mut().frame_start(context, frame_input)
1432 }
1433
1434 fn frame_end(
1435 &mut self,
1436 context: &mut FoundryContextFor<'_, FEN>,
1437 frame_input: &FrameInput,
1438 frame_result: &mut FrameResult,
1439 ) {
1440 self.as_mut().frame_end(context, frame_input, frame_result)
1441 }
1442
1443 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1444 call_inspectors!([&mut self.inner.printer], |inspector| {
1445 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1446 inspector, contract, target, value,
1447 )
1448 });
1449 }
1450}
1451
1452impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStack<FEN> {
1453 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1454 self.as_mut().should_use_create2_factory(depth, inputs)
1455 }
1456
1457 fn get_networks(&self) -> NetworkConfigs {
1458 self.networks
1459 }
1460
1461 fn create2_deployer(&self) -> Address {
1462 self.create2_deployer
1463 }
1464}
1465
1466impl<'a, FEN: FoundryEvmNetwork> Deref for InspectorStackRefMut<'a, FEN> {
1467 type Target = &'a mut InspectorStackInner;
1468
1469 fn deref(&self) -> &Self::Target {
1470 &self.inner
1471 }
1472}
1473
1474impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStackRefMut<'_, FEN> {
1475 fn deref_mut(&mut self) -> &mut Self::Target {
1476 &mut self.inner
1477 }
1478}
1479
1480impl<FEN: FoundryEvmNetwork> Deref for InspectorStack<FEN> {
1481 type Target = InspectorStackInner;
1482
1483 fn deref(&self) -> &Self::Target {
1484 &self.inner
1485 }
1486}
1487
1488impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStack<FEN> {
1489 fn deref_mut(&mut self) -> &mut Self::Target {
1490 &mut self.inner
1491 }
1492}