1use super::{
2 Cheatcodes, CheatsConfig, ChiselState, CustomPrintTracer, Fuzzer, LineCoverageCollector,
3 LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector,
4};
5use alloy_evm::{Evm, eth::EthEvmContext};
6use alloy_primitives::{
7 Address, Bytes, Log, TxKind, U256,
8 map::{AddressHashMap, HashMap},
9};
10use foundry_cheatcodes::{CheatcodesExecutor, Wallets};
11use foundry_evm_core::{
12 ContextExt, Env, InspectorExt,
13 backend::{DatabaseExt, JournaledState},
14 evm::new_evm_with_inspector,
15};
16use foundry_evm_coverage::HitMaps;
17use foundry_evm_traces::{SparsedTraceArena, TraceMode};
18use revm::{
19 Inspector,
20 context::{
21 BlockEnv,
22 result::{ExecutionResult, Output},
23 },
24 context_interface::CreateScheme,
25 interpreter::{
26 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult,
27 Interpreter, InterpreterResult,
28 },
29 state::{Account, AccountStatus},
30};
31use revm_inspectors::edge_cov::EdgeCovInspector;
32use std::{
33 ops::{Deref, DerefMut},
34 sync::Arc,
35};
36
37#[derive(Clone, Debug, Default)]
38#[must_use = "builders do nothing unless you call `build` on them"]
39pub struct InspectorStackBuilder {
40 pub block: Option<BlockEnv>,
45 pub gas_price: Option<u128>,
50 pub cheatcodes: Option<Arc<CheatsConfig>>,
52 pub fuzzer: Option<Fuzzer>,
54 pub trace_mode: TraceMode,
56 pub logs: Option<bool>,
58 pub line_coverage: Option<bool>,
60 pub print: Option<bool>,
62 pub chisel_state: Option<usize>,
64 pub enable_isolation: bool,
68 pub odyssey: bool,
70 pub wallets: Option<Wallets>,
72 pub create2_deployer: Address,
74}
75
76impl InspectorStackBuilder {
77 #[inline]
79 pub fn new() -> Self {
80 Self::default()
81 }
82
83 #[inline]
85 pub fn block(mut self, block: BlockEnv) -> Self {
86 self.block = Some(block);
87 self
88 }
89
90 #[inline]
92 pub fn gas_price(mut self, gas_price: u128) -> Self {
93 self.gas_price = Some(gas_price);
94 self
95 }
96
97 #[inline]
99 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
100 self.cheatcodes = Some(config);
101 self
102 }
103
104 #[inline]
106 pub fn wallets(mut self, wallets: Wallets) -> Self {
107 self.wallets = Some(wallets);
108 self
109 }
110
111 #[inline]
113 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
114 self.fuzzer = Some(fuzzer);
115 self
116 }
117
118 #[inline]
120 pub fn chisel_state(mut self, final_pc: usize) -> Self {
121 self.chisel_state = Some(final_pc);
122 self
123 }
124
125 #[inline]
127 pub fn logs(mut self, yes: bool) -> Self {
128 self.logs = Some(yes);
129 self
130 }
131
132 #[inline]
134 pub fn line_coverage(mut self, yes: bool) -> Self {
135 self.line_coverage = Some(yes);
136 self
137 }
138
139 #[inline]
141 pub fn print(mut self, yes: bool) -> Self {
142 self.print = Some(yes);
143 self
144 }
145
146 #[inline]
149 pub fn trace_mode(mut self, mode: TraceMode) -> Self {
150 if self.trace_mode < mode {
151 self.trace_mode = mode
152 }
153 self
154 }
155
156 #[inline]
159 pub fn enable_isolation(mut self, yes: bool) -> Self {
160 self.enable_isolation = yes;
161 self
162 }
163
164 #[inline]
167 pub fn odyssey(mut self, yes: bool) -> Self {
168 self.odyssey = yes;
169 self
170 }
171
172 #[inline]
173 pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
174 self.create2_deployer = create2_deployer;
175 self
176 }
177
178 pub fn build(self) -> InspectorStack {
180 let Self {
181 block,
182 gas_price,
183 cheatcodes,
184 fuzzer,
185 trace_mode,
186 logs,
187 line_coverage,
188 print,
189 chisel_state,
190 enable_isolation,
191 odyssey,
192 wallets,
193 create2_deployer,
194 } = self;
195 let mut stack = InspectorStack::new();
196
197 if let Some(config) = cheatcodes {
199 let mut cheatcodes = Cheatcodes::new(config);
200 if let Some(wallets) = wallets {
202 cheatcodes.set_wallets(wallets);
203 }
204 stack.set_cheatcodes(cheatcodes);
205 }
206
207 if let Some(fuzzer) = fuzzer {
208 stack.set_fuzzer(fuzzer);
209 }
210 if let Some(chisel_state) = chisel_state {
211 stack.set_chisel(chisel_state);
212 }
213 stack.collect_line_coverage(line_coverage.unwrap_or(false));
214 stack.collect_logs(logs.unwrap_or(true));
215 stack.print(print.unwrap_or(false));
216 stack.tracing(trace_mode);
217
218 stack.enable_isolation(enable_isolation);
219 stack.odyssey(odyssey);
220 stack.set_create2_deployer(create2_deployer);
221
222 if let Some(block) = block {
224 stack.set_block(&block);
225 }
226 if let Some(gas_price) = gas_price {
227 stack.set_gas_price(gas_price);
228 }
229
230 stack
231 }
232}
233
234#[macro_export]
237macro_rules! call_inspectors {
238 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
239 $(
240 if let Some($id) = $inspector {
241 $crate::utils::cold_path();
242 $body;
243 }
244 )+
245 };
246 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
247 $(
248 if let Some($id) = $inspector {
249 $crate::utils::cold_path();
250 if let Some(result) = $body {
251 return result;
252 }
253 }
254 )+
255 }};
256}
257
258pub struct InspectorData {
260 pub logs: Vec<Log>,
261 pub labels: AddressHashMap<String>,
262 pub traces: Option<SparsedTraceArena>,
263 pub line_coverage: Option<HitMaps>,
264 pub edge_coverage: Option<Vec<u8>>,
265 pub cheatcodes: Option<Box<Cheatcodes>>,
266 pub chisel_state: Option<(Vec<U256>, Vec<u8>, Option<InstructionResult>)>,
267 pub reverter: Option<Address>,
268}
269
270#[derive(Debug, Clone)]
276pub struct InnerContextData {
277 original_origin: Address,
279}
280
281#[derive(Clone, Debug, Default)]
292pub struct InspectorStack {
293 pub cheatcodes: Option<Box<Cheatcodes>>,
294 pub inner: InspectorStackInner,
295}
296
297#[derive(Default, Clone, Debug)]
301pub struct InspectorStackInner {
302 pub chisel_state: Option<Box<ChiselState>>,
306 pub edge_coverage: Option<Box<EdgeCovInspector>>,
307 pub fuzzer: Option<Box<Fuzzer>>,
308 pub line_coverage: Option<Box<LineCoverageCollector>>,
309 pub log_collector: Option<Box<LogCollector>>,
310 pub printer: Option<Box<CustomPrintTracer>>,
311 pub revert_diag: Option<Box<RevertDiagnostic>>,
312 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
313 pub tracer: Option<Box<TracingInspector>>,
314
315 pub enable_isolation: bool,
317 pub odyssey: bool,
318 pub create2_deployer: Address,
319 pub in_inner_context: bool,
321 pub inner_context_data: Option<InnerContextData>,
322 pub top_frame_journal: HashMap<Address, Account>,
323 pub reverter: Option<Address>,
325}
326
327pub struct InspectorStackRefMut<'a> {
331 pub cheatcodes: Option<&'a mut Cheatcodes>,
332 pub inner: &'a mut InspectorStackInner,
333}
334
335impl CheatcodesExecutor for InspectorStackInner {
336 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
337 Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
338 }
339
340 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
341 self.tracer.as_deref_mut()
342 }
343}
344
345impl InspectorStack {
346 #[inline]
352 pub fn new() -> Self {
353 Self::default()
354 }
355
356 pub fn log_status(&self) {
358 trace!(enabled=%{
359 let mut enabled = Vec::with_capacity(16);
360 macro_rules! push {
361 ($($id:ident),* $(,)?) => {
362 $(
363 if self.$id.is_some() {
364 enabled.push(stringify!($id));
365 }
366 )*
367 };
368 }
369 push!(cheatcodes, chisel_state, line_coverage, fuzzer, log_collector, printer, tracer);
370 if self.enable_isolation {
371 enabled.push("isolation");
372 }
373 format!("[{}]", enabled.join(", "))
374 });
375 }
376
377 #[inline]
379 pub fn set_env(&mut self, env: &Env) {
380 self.set_block(&env.evm_env.block_env);
381 self.set_gas_price(env.tx.gas_price);
382 }
383
384 #[inline]
386 pub fn set_block(&mut self, block: &BlockEnv) {
387 if let Some(cheatcodes) = &mut self.cheatcodes {
388 cheatcodes.block = Some(block.clone());
389 }
390 }
391
392 #[inline]
394 pub fn set_gas_price(&mut self, gas_price: u128) {
395 if let Some(cheatcodes) = &mut self.cheatcodes {
396 cheatcodes.gas_price = Some(gas_price);
397 }
398 }
399
400 #[inline]
402 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
403 self.cheatcodes = Some(cheatcodes.into());
404 }
405
406 #[inline]
408 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
409 self.fuzzer = Some(fuzzer.into());
410 }
411
412 #[inline]
414 pub fn set_chisel(&mut self, final_pc: usize) {
415 self.chisel_state = Some(ChiselState::new(final_pc).into());
416 }
417
418 #[inline]
420 pub fn collect_line_coverage(&mut self, yes: bool) {
421 self.line_coverage = yes.then(Default::default);
422 }
423
424 #[inline]
426 pub fn collect_edge_coverage(&mut self, yes: bool) {
427 self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
429 }
430
431 #[inline]
433 pub fn enable_isolation(&mut self, yes: bool) {
434 self.enable_isolation = yes;
435 }
436
437 #[inline]
439 pub fn odyssey(&mut self, yes: bool) {
440 self.odyssey = yes;
441 }
442
443 #[inline]
445 pub fn set_create2_deployer(&mut self, deployer: Address) {
446 self.create2_deployer = deployer;
447 }
448
449 #[inline]
451 pub fn collect_logs(&mut self, yes: bool) {
452 self.log_collector = yes.then(Default::default);
453 }
454
455 #[inline]
457 pub fn print(&mut self, yes: bool) {
458 self.printer = yes.then(Default::default);
459 }
460
461 #[inline]
464 pub fn tracing(&mut self, mode: TraceMode) {
465 self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
466
467 if let Some(config) = mode.into_config() {
468 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
469 } else {
470 self.tracer = None;
471 }
472 }
473
474 #[inline]
476 pub fn script(&mut self, script_address: Address) {
477 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
478 script_address;
479 }
480
481 #[inline(always)]
482 fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
483 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
484 }
485
486 #[inline]
488 pub fn as_inspector(&mut self) -> impl InspectorExt + '_ {
489 self
490 }
491
492 pub fn collect(self) -> InspectorData {
494 let Self {
495 mut cheatcodes,
496 inner:
497 InspectorStackInner {
498 chisel_state,
499 line_coverage,
500 edge_coverage,
501 log_collector,
502 tracer,
503 reverter,
504 ..
505 },
506 } = self;
507
508 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
509 let ignored = cheatcodes
510 .as_mut()
511 .map(|cheatcodes| {
512 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
513
514 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
516 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
517 }
518
519 ignored
520 })
521 .unwrap_or_default();
522
523 SparsedTraceArena { arena, ignored }
524 });
525
526 InspectorData {
527 logs: log_collector.map(|logs| logs.logs).unwrap_or_default(),
528 labels: cheatcodes
529 .as_ref()
530 .map(|cheatcodes| cheatcodes.labels.clone())
531 .unwrap_or_default(),
532 traces,
533 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
534 edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()),
535 cheatcodes,
536 chisel_state: chisel_state.and_then(|state| state.state),
537 reverter,
538 }
539 }
540}
541
542impl InspectorStackRefMut<'_> {
543 fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
548 let inner_context_data =
549 self.inner_context_data.as_ref().expect("should be called in inner context");
550 ecx.tx.caller = inner_context_data.original_origin;
551 }
552
553 fn do_call_end(
554 &mut self,
555 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
556 inputs: &CallInputs,
557 outcome: &mut CallOutcome,
558 ) -> CallOutcome {
559 let result = outcome.result.result;
560 call_inspectors!(
561 #[ret]
562 [
563 &mut self.fuzzer,
564 &mut self.tracer,
565 &mut self.cheatcodes,
566 &mut self.printer,
567 &mut self.revert_diag
568 ],
569 |inspector| {
570 let previous_outcome = outcome.clone();
571 inspector.call_end(ecx, inputs, outcome);
572
573 let different = outcome.result.result != result
576 || (outcome.result.result == InstructionResult::Revert
577 && outcome.output() != previous_outcome.output());
578 different.then_some(outcome.clone())
579 },
580 );
581
582 if result.is_revert() && self.reverter.is_none() {
584 self.reverter = Some(inputs.target_address);
585 }
586
587 outcome.clone()
588 }
589
590 fn do_create_end(
591 &mut self,
592 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
593 call: &CreateInputs,
594 outcome: &mut CreateOutcome,
595 ) -> CreateOutcome {
596 let result = outcome.result.result;
597 call_inspectors!(
598 #[ret]
599 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
600 |inspector| {
601 let previous_outcome = outcome.clone();
602 inspector.create_end(ecx, call, outcome);
603
604 let different = outcome.result.result != result
607 || (outcome.result.result == InstructionResult::Revert
608 && outcome.output() != previous_outcome.output());
609 different.then_some(outcome.clone())
610 },
611 );
612
613 outcome.clone()
614 }
615
616 fn transact_inner(
617 &mut self,
618 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
619 kind: TxKind,
620 caller: Address,
621 input: Bytes,
622 gas_limit: u64,
623 value: U256,
624 ) -> (InterpreterResult, Option<Address>) {
625 let cached_env = Env::from(ecx.cfg.clone(), ecx.block.clone(), ecx.tx.clone());
626
627 ecx.block.basefee = 0;
628 ecx.tx.chain_id = Some(ecx.cfg.chain_id);
629 ecx.tx.caller = caller;
630 ecx.tx.kind = kind;
631 ecx.tx.data = input;
632 ecx.tx.value = value;
633 ecx.tx.gas_limit = gas_limit + 21000;
635
636 if !ecx.cfg.disable_block_gas_limit {
639 ecx.tx.gas_limit = std::cmp::min(ecx.tx.gas_limit, ecx.block.gas_limit);
640 }
641 ecx.tx.gas_price = 0;
642
643 self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller });
644 self.in_inner_context = true;
645
646 let res = self.with_inspector(|inspector| {
647 let (db, journal, env) = ecx.as_db_env_and_journal();
648 let mut evm = new_evm_with_inspector(db, env.to_owned(), inspector);
649
650 evm.journaled_state.state = {
651 let mut state = journal.state.clone();
652
653 for (addr, acc_mut) in &mut state {
654 if journal.warm_addresses.is_cold(addr) {
656 acc_mut.mark_cold();
657 }
658
659 for slot_mut in acc_mut.storage.values_mut() {
661 slot_mut.is_cold = true;
662 slot_mut.original_value = slot_mut.present_value;
663 }
664 }
665
666 state
667 };
668
669 evm.journaled_state.depth = 1;
671
672 let res = evm.transact(env.tx.clone());
673
674 *env.cfg = evm.cfg.clone();
676 *env.block = evm.block.clone();
677
678 *env.tx = cached_env.tx;
679 env.block.basefee = cached_env.evm_env.block_env.basefee;
680
681 res
682 });
683
684 self.in_inner_context = false;
685 self.inner_context_data = None;
686
687 let mut gas = Gas::new(gas_limit);
688
689 let Ok(res) = res else {
690 let result =
692 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
693 return (result, None);
694 };
695
696 for (addr, mut acc) in res.state {
697 let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else {
698 ecx.journaled_state.state.insert(addr, acc);
699 continue;
700 };
701
702 if acc.status.contains(AccountStatus::Cold)
704 && !acc_mut.status.contains(AccountStatus::Cold)
705 {
706 acc.status -= AccountStatus::Cold;
707 }
708 acc_mut.info = acc.info;
709 acc_mut.status |= acc.status;
710
711 for (key, val) in acc.storage {
712 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
713 acc_mut.storage.insert(key, val);
714 continue;
715 };
716 slot_mut.present_value = val.present_value;
717 slot_mut.is_cold &= val.is_cold;
718 }
719 }
720
721 let (result, address, output) = match res.result {
722 ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
723 gas.set_refund(gas_refunded as i64);
724 let _ = gas.record_cost(gas_used);
725 let address = match output {
726 Output::Create(_, address) => address,
727 Output::Call(_) => None,
728 };
729 (reason.into(), address, output.into_data())
730 }
731 ExecutionResult::Halt { reason, gas_used } => {
732 let _ = gas.record_cost(gas_used);
733 (reason.into(), None, Bytes::new())
734 }
735 ExecutionResult::Revert { gas_used, output } => {
736 let _ = gas.record_cost(gas_used);
737 (InstructionResult::Revert, None, output)
738 }
739 };
740 (InterpreterResult { result, output, gas }, address)
741 }
742
743 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_>) -> O) -> O {
746 let mut cheatcodes = self
747 .cheatcodes
748 .as_deref_mut()
749 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
750 let mut inner = std::mem::take(self.inner);
751
752 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
753
754 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
755 *cheats = cheatcodes.unwrap();
756 }
757
758 *self.inner = inner;
759
760 out
761 }
762
763 fn top_level_frame_start(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
765 if self.enable_isolation {
766 self.top_frame_journal.clone_from(&ecx.journaled_state.state);
769 }
770 }
771
772 fn top_level_frame_end(
774 &mut self,
775 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
776 result: InstructionResult,
777 ) {
778 if !result.is_revert() {
779 return;
780 }
781 if let Some(cheats) = self.cheatcodes.as_mut() {
785 cheats.on_revert(ecx);
786 }
787
788 if self.enable_isolation {
792 ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal);
793 }
794 }
795
796 #[inline(always)]
802 fn step_inlined(
803 &mut self,
804 interpreter: &mut Interpreter,
805 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
806 ) {
807 call_inspectors!(
808 [
809 &mut self.edge_coverage,
811 &mut self.fuzzer,
812 &mut self.line_coverage,
813 &mut self.printer,
814 &mut self.revert_diag,
815 &mut self.script_execution_inspector,
816 &mut self.tracer,
817 &mut self.cheatcodes,
819 ],
820 |inspector| (**inspector).step(interpreter, ecx),
821 );
822 }
823
824 #[inline(always)]
825 fn step_end_inlined(
826 &mut self,
827 interpreter: &mut Interpreter,
828 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
829 ) {
830 call_inspectors!(
831 [
832 &mut self.chisel_state,
834 &mut self.printer,
835 &mut self.revert_diag,
836 &mut self.tracer,
837 &mut self.cheatcodes,
839 ],
840 |inspector| (**inspector).step_end(interpreter, ecx),
841 );
842 }
843}
844
845impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStackRefMut<'_> {
846 fn initialize_interp(
847 &mut self,
848 interpreter: &mut Interpreter,
849 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
850 ) {
851 call_inspectors!(
852 [
853 &mut self.line_coverage,
854 &mut self.tracer,
855 &mut self.cheatcodes,
856 &mut self.script_execution_inspector,
857 &mut self.printer
858 ],
859 |inspector| inspector.initialize_interp(interpreter, ecx),
860 );
861 }
862
863 fn step(
864 &mut self,
865 interpreter: &mut Interpreter,
866 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
867 ) {
868 self.step_inlined(interpreter, ecx);
869 }
870
871 fn step_end(
872 &mut self,
873 interpreter: &mut Interpreter,
874 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
875 ) {
876 self.step_end_inlined(interpreter, ecx);
877 }
878
879 #[allow(clippy::redundant_clone)]
880 fn log(
881 &mut self,
882 interpreter: &mut Interpreter,
883 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
884 log: Log,
885 ) {
886 call_inspectors!(
887 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
888 |inspector| inspector.log(interpreter, ecx, log.clone()),
889 );
890 }
891
892 fn call(
893 &mut self,
894 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
895 call: &mut CallInputs,
896 ) -> Option<CallOutcome> {
897 if self.in_inner_context && ecx.journaled_state.depth == 1 {
898 self.adjust_evm_data_for_inner_context(ecx);
899 return None;
900 }
901
902 if ecx.journaled_state.depth == 0 {
903 self.top_level_frame_start(ecx);
904 }
905
906 call_inspectors!(
907 #[ret]
908 [
909 &mut self.fuzzer,
910 &mut self.tracer,
911 &mut self.log_collector,
912 &mut self.printer,
913 &mut self.revert_diag
914 ],
915 |inspector| {
916 let mut out = None;
917 if let Some(output) = inspector.call(ecx, call) {
918 out = Some(Some(output));
919 }
920 out
921 },
922 );
923
924 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
925 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.target_address) {
927 if let Some(target) = mocks.get(&call.input.bytes(ecx)).or_else(|| {
930 call.input.bytes(ecx).get(..4).and_then(|selector| mocks.get(selector))
931 }) {
932 call.bytecode_address = *target;
933 }
934 }
935
936 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
937 return Some(output);
938 }
939 }
940
941 if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 {
942 match call.scheme {
943 CallScheme::Call => {
945 let input = call.input.bytes(ecx);
946 let (result, _) = self.transact_inner(
947 ecx,
948 TxKind::Call(call.target_address),
949 call.caller,
950 input,
951 call.gas_limit,
952 call.value.get(),
953 );
954 return Some(CallOutcome {
955 result,
956 memory_offset: call.return_memory_offset.clone(),
957 });
958 }
959 CallScheme::StaticCall => {
961 let JournaledState { state, warm_addresses, .. } =
962 &mut ecx.journaled_state.inner;
963 for (addr, acc_mut) in state {
964 if let Some(cheatcodes) = &self.cheatcodes
966 && cheatcodes.has_arbitrary_storage(addr)
967 {
968 continue;
969 }
970
971 if warm_addresses.is_cold(addr) {
972 acc_mut.mark_cold();
973 }
974
975 for slot_mut in acc_mut.storage.values_mut() {
976 slot_mut.is_cold = true;
977 }
978 }
979 }
980 CallScheme::CallCode | CallScheme::DelegateCall => {}
982 }
983 }
984
985 None
986 }
987
988 fn call_end(
989 &mut self,
990 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
991 inputs: &CallInputs,
992 outcome: &mut CallOutcome,
993 ) {
994 if self.in_inner_context && ecx.journaled_state.depth == 1 {
997 return;
998 }
999
1000 self.do_call_end(ecx, inputs, outcome);
1001
1002 if ecx.journaled_state.depth == 0 {
1003 self.top_level_frame_end(ecx, outcome.result.result);
1004 }
1005 }
1006
1007 fn create(
1008 &mut self,
1009 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1010 create: &mut CreateInputs,
1011 ) -> Option<CreateOutcome> {
1012 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1013 self.adjust_evm_data_for_inner_context(ecx);
1014 return None;
1015 }
1016
1017 if ecx.journaled_state.depth == 0 {
1018 self.top_level_frame_start(ecx);
1019 }
1020
1021 call_inspectors!(
1022 #[ret]
1023 [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1024 |inspector| inspector.create(ecx, create).map(Some),
1025 );
1026
1027 if !matches!(create.scheme, CreateScheme::Create2 { .. })
1028 && self.enable_isolation
1029 && !self.in_inner_context
1030 && ecx.journaled_state.depth == 1
1031 {
1032 let (result, address) = self.transact_inner(
1033 ecx,
1034 TxKind::Create,
1035 create.caller,
1036 create.init_code.clone(),
1037 create.gas_limit,
1038 create.value,
1039 );
1040 return Some(CreateOutcome { result, address });
1041 }
1042
1043 None
1044 }
1045
1046 fn create_end(
1047 &mut self,
1048 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1049 call: &CreateInputs,
1050 outcome: &mut CreateOutcome,
1051 ) {
1052 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1055 return;
1056 }
1057
1058 self.do_create_end(ecx, call, outcome);
1059
1060 if ecx.journaled_state.depth == 0 {
1061 self.top_level_frame_end(ecx, outcome.result.result);
1062 }
1063 }
1064}
1065
1066impl InspectorExt for InspectorStackRefMut<'_> {
1067 fn should_use_create2_factory(
1068 &mut self,
1069 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1070 inputs: &CreateInputs,
1071 ) -> bool {
1072 call_inspectors!(
1073 #[ret]
1074 [&mut self.cheatcodes],
1075 |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) },
1076 );
1077
1078 false
1079 }
1080
1081 fn console_log(&mut self, msg: &str) {
1082 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1083 inspector, msg
1084 ));
1085 }
1086
1087 fn is_odyssey(&self) -> bool {
1088 self.inner.odyssey
1089 }
1090
1091 fn create2_deployer(&self) -> Address {
1092 self.inner.create2_deployer
1093 }
1094}
1095
1096impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStack {
1097 fn step(
1098 &mut self,
1099 interpreter: &mut Interpreter,
1100 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1101 ) {
1102 self.as_mut().step_inlined(interpreter, ecx)
1103 }
1104
1105 fn step_end(
1106 &mut self,
1107 interpreter: &mut Interpreter,
1108 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1109 ) {
1110 self.as_mut().step_end_inlined(interpreter, ecx)
1111 }
1112
1113 fn call(
1114 &mut self,
1115 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1116 inputs: &mut CallInputs,
1117 ) -> Option<CallOutcome> {
1118 self.as_mut().call(context, inputs)
1119 }
1120
1121 fn call_end(
1122 &mut self,
1123 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1124 inputs: &CallInputs,
1125 outcome: &mut CallOutcome,
1126 ) {
1127 self.as_mut().call_end(context, inputs, outcome)
1128 }
1129
1130 fn create(
1131 &mut self,
1132 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1133 create: &mut CreateInputs,
1134 ) -> Option<CreateOutcome> {
1135 self.as_mut().create(context, create)
1136 }
1137
1138 fn create_end(
1139 &mut self,
1140 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1141 call: &CreateInputs,
1142 outcome: &mut CreateOutcome,
1143 ) {
1144 self.as_mut().create_end(context, call, outcome)
1145 }
1146
1147 fn initialize_interp(
1148 &mut self,
1149 interpreter: &mut Interpreter,
1150 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1151 ) {
1152 self.as_mut().initialize_interp(interpreter, ecx)
1153 }
1154
1155 fn log(
1156 &mut self,
1157 interpreter: &mut Interpreter,
1158 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1159 log: Log,
1160 ) {
1161 self.as_mut().log(interpreter, ecx, log)
1162 }
1163
1164 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1165 self.as_mut().selfdestruct(contract, target, value);
1166 }
1167}
1168
1169impl InspectorExt for InspectorStack {
1170 fn should_use_create2_factory(
1171 &mut self,
1172 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1173 inputs: &CreateInputs,
1174 ) -> bool {
1175 self.as_mut().should_use_create2_factory(ecx, inputs)
1176 }
1177
1178 fn is_odyssey(&self) -> bool {
1179 self.odyssey
1180 }
1181
1182 fn create2_deployer(&self) -> Address {
1183 self.create2_deployer
1184 }
1185}
1186
1187impl<'a> Deref for InspectorStackRefMut<'a> {
1188 type Target = &'a mut InspectorStackInner;
1189
1190 fn deref(&self) -> &Self::Target {
1191 &self.inner
1192 }
1193}
1194
1195impl DerefMut for InspectorStackRefMut<'_> {
1196 fn deref_mut(&mut self) -> &mut Self::Target {
1197 &mut self.inner
1198 }
1199}
1200
1201impl Deref for InspectorStack {
1202 type Target = InspectorStackInner;
1203
1204 fn deref(&self) -> &Self::Target {
1205 &self.inner
1206 }
1207}
1208
1209impl DerefMut for InspectorStack {
1210 fn deref_mut(&mut self) -> &mut Self::Target {
1211 &mut self.inner
1212 }
1213}