1use super::{
2 Cheatcodes, CheatsConfig, ChiselState, CustomPrintTracer, Fuzzer, LineCoverageCollector,
3 LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector,
4};
5use alloy_evm::{Evm, eth::EthEvmContext};
6use alloy_primitives::{
7 Address, Bytes, Log, TxKind, U256,
8 map::{AddressHashMap, HashMap},
9};
10use foundry_cheatcodes::{CheatcodeAnalysis, CheatcodesExecutor, Wallets};
11use foundry_common::compile::Analysis;
12use foundry_compilers::ProjectPathsConfig;
13use foundry_evm_core::{
14 ContextExt, Env, InspectorExt,
15 backend::{DatabaseExt, JournaledState},
16 evm::new_evm_with_inspector,
17};
18use foundry_evm_coverage::HitMaps;
19use foundry_evm_networks::NetworkConfigs;
20use foundry_evm_traces::{SparsedTraceArena, TraceMode};
21use revm::{
22 Inspector,
23 context::{
24 BlockEnv, ContextTr,
25 result::{ExecutionResult, Output},
26 },
27 context_interface::CreateScheme,
28 interpreter::{
29 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult,
30 Interpreter, InterpreterResult,
31 },
32 state::{Account, AccountStatus},
33};
34use revm_inspectors::edge_cov::EdgeCovInspector;
35use std::{
36 ops::{Deref, DerefMut},
37 sync::Arc,
38};
39
40#[derive(Clone, Debug, Default)]
41#[must_use = "builders do nothing unless you call `build` on them"]
42pub struct InspectorStackBuilder {
43 pub analysis: Option<Analysis>,
45 pub block: Option<BlockEnv>,
50 pub gas_price: Option<u128>,
55 pub cheatcodes: Option<Arc<CheatsConfig>>,
57 pub fuzzer: Option<Fuzzer>,
59 pub trace_mode: TraceMode,
61 pub logs: Option<bool>,
66 pub line_coverage: Option<bool>,
68 pub print: Option<bool>,
70 pub chisel_state: Option<usize>,
72 pub enable_isolation: bool,
76 pub networks: NetworkConfigs,
78 pub wallets: Option<Wallets>,
80 pub create2_deployer: Address,
82}
83
84impl InspectorStackBuilder {
85 #[inline]
87 pub fn new() -> Self {
88 Self::default()
89 }
90
91 #[inline]
93 pub fn set_analysis(mut self, analysis: Analysis) -> Self {
94 self.analysis = Some(analysis);
95 self
96 }
97
98 #[inline]
100 pub fn block(mut self, block: BlockEnv) -> Self {
101 self.block = Some(block);
102 self
103 }
104
105 #[inline]
107 pub fn gas_price(mut self, gas_price: u128) -> Self {
108 self.gas_price = Some(gas_price);
109 self
110 }
111
112 #[inline]
114 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
115 self.cheatcodes = Some(config);
116 self
117 }
118
119 #[inline]
121 pub fn wallets(mut self, wallets: Wallets) -> Self {
122 self.wallets = Some(wallets);
123 self
124 }
125
126 #[inline]
128 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
129 self.fuzzer = Some(fuzzer);
130 self
131 }
132
133 #[inline]
135 pub fn chisel_state(mut self, final_pc: usize) -> Self {
136 self.chisel_state = Some(final_pc);
137 self
138 }
139
140 #[inline]
142 pub fn logs(mut self, live_logs: bool) -> Self {
143 self.logs = Some(live_logs);
144 self
145 }
146
147 #[inline]
149 pub fn line_coverage(mut self, yes: bool) -> Self {
150 self.line_coverage = Some(yes);
151 self
152 }
153
154 #[inline]
156 pub fn print(mut self, yes: bool) -> Self {
157 self.print = Some(yes);
158 self
159 }
160
161 #[inline]
164 pub fn trace_mode(mut self, mode: TraceMode) -> Self {
165 if self.trace_mode < mode {
166 self.trace_mode = mode
167 }
168 self
169 }
170
171 #[inline]
174 pub fn enable_isolation(mut self, yes: bool) -> Self {
175 self.enable_isolation = yes;
176 self
177 }
178
179 #[inline]
181 pub fn networks(mut self, networks: NetworkConfigs) -> Self {
182 self.networks = networks;
183 self
184 }
185
186 #[inline]
187 pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
188 self.create2_deployer = create2_deployer;
189 self
190 }
191
192 pub fn build(self) -> InspectorStack {
194 let Self {
195 analysis,
196 block,
197 gas_price,
198 cheatcodes,
199 fuzzer,
200 trace_mode,
201 logs,
202 line_coverage,
203 print,
204 chisel_state,
205 enable_isolation,
206 networks,
207 wallets,
208 create2_deployer,
209 } = self;
210 let mut stack = InspectorStack::new();
211
212 if let Some(config) = cheatcodes {
214 let mut cheatcodes = Cheatcodes::new(config);
215 if let Some(analysis) = analysis {
217 stack.set_analysis(analysis.clone());
218 cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
219 }
220 if let Some(wallets) = wallets {
222 cheatcodes.set_wallets(wallets);
223 }
224 stack.set_cheatcodes(cheatcodes);
225 }
226
227 if let Some(fuzzer) = fuzzer {
228 stack.set_fuzzer(fuzzer);
229 }
230 if let Some(chisel_state) = chisel_state {
231 stack.set_chisel(chisel_state);
232 }
233 stack.collect_line_coverage(line_coverage.unwrap_or(false));
234 stack.collect_logs(logs);
235 stack.print(print.unwrap_or(false));
236 stack.tracing(trace_mode);
237
238 stack.enable_isolation(enable_isolation);
239 stack.networks(networks);
240 stack.set_create2_deployer(create2_deployer);
241
242 if let Some(block) = block {
244 stack.set_block(&block);
245 }
246 if let Some(gas_price) = gas_price {
247 stack.set_gas_price(gas_price);
248 }
249
250 stack
251 }
252}
253
254#[macro_export]
257macro_rules! call_inspectors {
258 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
259 $(
260 if let Some($id) = $inspector {
261 $crate::utils::cold_path();
262 $body;
263 }
264 )+
265 };
266 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
267 $(
268 if let Some($id) = $inspector {
269 $crate::utils::cold_path();
270 if let Some(result) = $body {
271 return result;
272 }
273 }
274 )+
275 }};
276}
277
278pub struct InspectorData {
280 pub logs: Vec<Log>,
281 pub labels: AddressHashMap<String>,
282 pub traces: Option<SparsedTraceArena>,
283 pub line_coverage: Option<HitMaps>,
284 pub edge_coverage: Option<Vec<u8>>,
285 pub cheatcodes: Option<Box<Cheatcodes>>,
286 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
287 pub reverter: Option<Address>,
288}
289
290#[derive(Debug, Clone)]
296pub struct InnerContextData {
297 original_origin: Address,
299}
300
301#[derive(Clone, Debug, Default)]
312pub struct InspectorStack {
313 pub cheatcodes: Option<Box<Cheatcodes>>,
314 pub inner: InspectorStackInner,
315}
316
317impl InspectorStack {
318 pub fn paths_config(&self) -> Option<&ProjectPathsConfig> {
319 self.cheatcodes.as_ref().map(|c| &c.config.paths)
320 }
321}
322
323#[derive(Default, Clone, Debug)]
327pub struct InspectorStackInner {
328 pub analysis: Option<Analysis>,
330
331 pub chisel_state: Option<Box<ChiselState>>,
335 pub edge_coverage: Option<Box<EdgeCovInspector>>,
336 pub fuzzer: Option<Box<Fuzzer>>,
337 pub line_coverage: Option<Box<LineCoverageCollector>>,
338 pub log_collector: Option<Box<LogCollector>>,
339 pub printer: Option<Box<CustomPrintTracer>>,
340 pub revert_diag: Option<Box<RevertDiagnostic>>,
341 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
342 pub tracer: Option<Box<TracingInspector>>,
343
344 pub enable_isolation: bool,
346 pub networks: NetworkConfigs,
347 pub create2_deployer: Address,
348 pub in_inner_context: bool,
350 pub inner_context_data: Option<InnerContextData>,
351 pub top_frame_journal: HashMap<Address, Account>,
352 pub reverter: Option<Address>,
354}
355
356pub struct InspectorStackRefMut<'a> {
360 pub cheatcodes: Option<&'a mut Cheatcodes>,
361 pub inner: &'a mut InspectorStackInner,
362}
363
364impl CheatcodesExecutor for InspectorStackInner {
365 fn get_inspector<'a>(&'a mut self, cheats: &'a mut Cheatcodes) -> Box<dyn InspectorExt + 'a> {
366 Box::new(InspectorStackRefMut { cheatcodes: Some(cheats), inner: self })
367 }
368
369 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
370 self.tracer.as_deref_mut()
371 }
372
373 fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
374 self.in_inner_context = enabled;
375 self.inner_context_data = if enabled {
376 Some(InnerContextData {
377 original_origin: original_origin.expect("origin required when enabling inner ctx"),
378 })
379 } else {
380 None
381 };
382 }
383}
384
385impl InspectorStack {
386 #[inline]
392 pub fn new() -> Self {
393 Self::default()
394 }
395
396 pub fn log_status(&self) {
398 trace!(enabled=%{
399 let mut enabled = Vec::with_capacity(16);
400 macro_rules! push {
401 ($($id:ident),* $(,)?) => {
402 $(
403 if self.$id.is_some() {
404 enabled.push(stringify!($id));
405 }
406 )*
407 };
408 }
409 push!(cheatcodes, chisel_state, line_coverage, fuzzer, log_collector, printer, tracer);
410 if self.enable_isolation {
411 enabled.push("isolation");
412 }
413 format!("[{}]", enabled.join(", "))
414 });
415 }
416
417 #[inline]
419 pub fn set_analysis(&mut self, analysis: Analysis) {
420 self.analysis = Some(analysis);
421 }
422
423 #[inline]
425 pub fn set_env(&mut self, env: &Env) {
426 self.set_block(&env.evm_env.block_env);
427 self.set_gas_price(env.tx.gas_price);
428 }
429
430 #[inline]
432 pub fn set_block(&mut self, block: &BlockEnv) {
433 if let Some(cheatcodes) = &mut self.cheatcodes {
434 cheatcodes.block = Some(block.clone());
435 }
436 }
437
438 #[inline]
440 pub fn set_gas_price(&mut self, gas_price: u128) {
441 if let Some(cheatcodes) = &mut self.cheatcodes {
442 cheatcodes.gas_price = Some(gas_price);
443 }
444 }
445
446 #[inline]
448 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
449 self.cheatcodes = Some(cheatcodes.into());
450 }
451
452 #[inline]
454 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
455 self.fuzzer = Some(fuzzer.into());
456 }
457
458 #[inline]
460 pub fn set_chisel(&mut self, final_pc: usize) {
461 self.chisel_state = Some(ChiselState::new(final_pc).into());
462 }
463
464 #[inline]
466 pub fn collect_line_coverage(&mut self, yes: bool) {
467 self.line_coverage = yes.then(Default::default);
468 }
469
470 #[inline]
472 pub fn collect_edge_coverage(&mut self, yes: bool) {
473 self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
475 }
476
477 #[inline]
479 pub fn enable_isolation(&mut self, yes: bool) {
480 self.enable_isolation = yes;
481 }
482
483 #[inline]
485 pub fn networks(&mut self, networks: NetworkConfigs) {
486 self.networks = networks;
487 }
488
489 #[inline]
491 pub fn set_create2_deployer(&mut self, deployer: Address) {
492 self.create2_deployer = deployer;
493 }
494
495 #[inline]
500 pub fn collect_logs(&mut self, live_logs: Option<bool>) {
501 self.log_collector = live_logs.map(|live_logs| {
502 Box::new(if live_logs {
503 LogCollector::LiveLogs
504 } else {
505 LogCollector::Capture { logs: Vec::new() }
506 })
507 });
508 }
509
510 #[inline]
512 pub fn print(&mut self, yes: bool) {
513 self.printer = yes.then(Default::default);
514 }
515
516 #[inline]
519 pub fn tracing(&mut self, mode: TraceMode) {
520 self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
521
522 if let Some(config) = mode.into_config() {
523 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
524 } else {
525 self.tracer = None;
526 }
527 }
528
529 #[inline]
531 pub fn script(&mut self, script_address: Address) {
532 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
533 script_address;
534 }
535
536 #[inline(always)]
537 fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
538 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
539 }
540
541 #[inline]
543 pub fn as_inspector(&mut self) -> impl InspectorExt + '_ {
544 self
545 }
546
547 pub fn collect(self) -> InspectorData {
549 let Self {
550 mut cheatcodes,
551 inner:
552 InspectorStackInner {
553 chisel_state,
554 line_coverage,
555 edge_coverage,
556 log_collector,
557 tracer,
558 reverter,
559 ..
560 },
561 } = self;
562
563 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
564 let ignored = cheatcodes
565 .as_mut()
566 .map(|cheatcodes| {
567 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
568
569 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
571 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
572 }
573
574 ignored
575 })
576 .unwrap_or_default();
577
578 SparsedTraceArena { arena, ignored }
579 });
580
581 InspectorData {
582 logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
583 labels: cheatcodes
584 .as_ref()
585 .map(|cheatcodes| cheatcodes.labels.clone())
586 .unwrap_or_default(),
587 traces,
588 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
589 edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()),
590 cheatcodes,
591 chisel_state: chisel_state.and_then(|state| state.state),
592 reverter,
593 }
594 }
595}
596
597impl InspectorStackRefMut<'_> {
598 fn adjust_evm_data_for_inner_context(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
603 let inner_context_data =
604 self.inner_context_data.as_ref().expect("should be called in inner context");
605 ecx.tx.caller = inner_context_data.original_origin;
606 }
607
608 fn do_call_end(
609 &mut self,
610 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
611 inputs: &CallInputs,
612 outcome: &mut CallOutcome,
613 ) -> CallOutcome {
614 let result = outcome.result.result;
615 call_inspectors!(
616 #[ret]
617 [
618 &mut self.fuzzer,
619 &mut self.tracer,
620 &mut self.cheatcodes,
621 &mut self.printer,
622 &mut self.revert_diag
623 ],
624 |inspector| {
625 let previous_outcome = outcome.clone();
626 inspector.call_end(ecx, inputs, outcome);
627
628 let different = outcome.result.result != result
631 || (outcome.result.result == InstructionResult::Revert
632 && outcome.output() != previous_outcome.output());
633 different.then_some(outcome.clone())
634 },
635 );
636
637 if result.is_revert() && self.reverter.is_none() {
639 self.reverter = Some(inputs.target_address);
640 }
641
642 outcome.clone()
643 }
644
645 fn do_create_end(
646 &mut self,
647 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
648 call: &CreateInputs,
649 outcome: &mut CreateOutcome,
650 ) -> CreateOutcome {
651 let result = outcome.result.result;
652 call_inspectors!(
653 #[ret]
654 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
655 |inspector| {
656 let previous_outcome = outcome.clone();
657 inspector.create_end(ecx, call, outcome);
658
659 let different = outcome.result.result != result
662 || (outcome.result.result == InstructionResult::Revert
663 && outcome.output() != previous_outcome.output());
664 different.then_some(outcome.clone())
665 },
666 );
667
668 outcome.clone()
669 }
670
671 fn transact_inner(
672 &mut self,
673 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
674 kind: TxKind,
675 caller: Address,
676 input: Bytes,
677 gas_limit: u64,
678 value: U256,
679 ) -> (InterpreterResult, Option<Address>) {
680 let cached_env = Env::from(ecx.cfg().clone(), ecx.block().clone(), ecx.tx().clone());
681
682 ecx.block.basefee = 0;
683 ecx.tx.chain_id = Some(ecx.cfg().chain_id);
684 ecx.tx.caller = caller;
685 ecx.tx.kind = kind;
686 ecx.tx.data = input;
687 ecx.tx.value = value;
688 ecx.tx.gas_limit = gas_limit + 21000;
690
691 if !ecx.cfg().disable_block_gas_limit {
694 ecx.tx.gas_limit = std::cmp::min(ecx.tx.gas_limit, ecx.block().gas_limit);
695 }
696 ecx.tx.gas_price = 0;
697
698 self.inner_context_data = Some(InnerContextData { original_origin: cached_env.tx.caller });
699 self.in_inner_context = true;
700
701 let res = self.with_inspector(|inspector| {
702 let (db, journal, env) = ecx.as_db_env_and_journal();
703 let mut evm = new_evm_with_inspector(db, env.to_owned(), inspector);
704
705 evm.journaled_state.state = {
706 let mut state = journal.state.clone();
707
708 for (addr, acc_mut) in &mut state {
709 if journal.warm_addresses.is_cold(addr) {
711 acc_mut.mark_cold();
712 }
713
714 for slot_mut in acc_mut.storage.values_mut() {
716 slot_mut.is_cold = true;
717 slot_mut.original_value = slot_mut.present_value;
718 }
719 }
720
721 state
722 };
723
724 evm.journaled_state.depth = 1;
726
727 let res = evm.transact(env.tx.clone());
728
729 *env.cfg = evm.cfg.clone();
731 *env.block = evm.block.clone();
732
733 *env.tx = cached_env.tx;
734 env.block.basefee = cached_env.evm_env.block_env.basefee;
735
736 res
737 });
738
739 self.in_inner_context = false;
740 self.inner_context_data = None;
741
742 let mut gas = Gas::new(gas_limit);
743
744 let Ok(res) = res else {
745 let result =
747 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
748 return (result, None);
749 };
750
751 for (addr, mut acc) in res.state {
752 let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else {
753 ecx.journaled_state.state.insert(addr, acc);
754 continue;
755 };
756
757 if acc.status.contains(AccountStatus::Cold)
759 && !acc_mut.status.contains(AccountStatus::Cold)
760 {
761 acc.status -= AccountStatus::Cold;
762 }
763 acc_mut.info = acc.info;
764 acc_mut.status |= acc.status;
765
766 for (key, val) in acc.storage {
767 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
768 acc_mut.storage.insert(key, val);
769 continue;
770 };
771 slot_mut.present_value = val.present_value;
772 slot_mut.is_cold &= val.is_cold;
773 }
774 }
775
776 let (result, address, output) = match res.result {
777 ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
778 gas.set_refund(gas_refunded as i64);
779 let _ = gas.record_cost(gas_used);
780 let address = match output {
781 Output::Create(_, address) => address,
782 Output::Call(_) => None,
783 };
784 (reason.into(), address, output.into_data())
785 }
786 ExecutionResult::Halt { reason, gas_used } => {
787 let _ = gas.record_cost(gas_used);
788 (reason.into(), None, Bytes::new())
789 }
790 ExecutionResult::Revert { gas_used, output } => {
791 let _ = gas.record_cost(gas_used);
792 (InstructionResult::Revert, None, output)
793 }
794 };
795 (InterpreterResult { result, output, gas }, address)
796 }
797
798 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_>) -> O) -> O {
801 let mut cheatcodes = self
802 .cheatcodes
803 .as_deref_mut()
804 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
805 let mut inner = std::mem::take(self.inner);
806
807 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
808
809 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
810 *cheats = cheatcodes.unwrap();
811 }
812
813 *self.inner = inner;
814
815 out
816 }
817
818 fn top_level_frame_start(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>) {
820 if self.enable_isolation {
821 self.top_frame_journal.clone_from(&ecx.journaled_state.state);
824 }
825 }
826
827 fn top_level_frame_end(
829 &mut self,
830 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
831 result: InstructionResult,
832 ) {
833 if !result.is_revert() {
834 return;
835 }
836 if let Some(cheats) = self.cheatcodes.as_mut() {
840 cheats.on_revert(ecx);
841 }
842
843 if self.enable_isolation {
847 ecx.journaled_state.state = std::mem::take(&mut self.top_frame_journal);
848 }
849 }
850
851 #[inline(always)]
857 fn step_inlined(
858 &mut self,
859 interpreter: &mut Interpreter,
860 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
861 ) {
862 call_inspectors!(
863 [
864 &mut self.edge_coverage,
866 &mut self.fuzzer,
867 &mut self.line_coverage,
868 &mut self.printer,
869 &mut self.revert_diag,
870 &mut self.script_execution_inspector,
871 &mut self.tracer,
872 &mut self.cheatcodes,
874 ],
875 |inspector| (**inspector).step(interpreter, ecx),
876 );
877 }
878
879 #[inline(always)]
880 fn step_end_inlined(
881 &mut self,
882 interpreter: &mut Interpreter,
883 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
884 ) {
885 call_inspectors!(
886 [
887 &mut self.chisel_state,
889 &mut self.printer,
890 &mut self.revert_diag,
891 &mut self.tracer,
892 &mut self.cheatcodes,
894 ],
895 |inspector| (**inspector).step_end(interpreter, ecx),
896 );
897 }
898}
899
900impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStackRefMut<'_> {
901 fn initialize_interp(
902 &mut self,
903 interpreter: &mut Interpreter,
904 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
905 ) {
906 call_inspectors!(
907 [
908 &mut self.line_coverage,
909 &mut self.tracer,
910 &mut self.cheatcodes,
911 &mut self.script_execution_inspector,
912 &mut self.printer
913 ],
914 |inspector| inspector.initialize_interp(interpreter, ecx),
915 );
916 }
917
918 fn step(
919 &mut self,
920 interpreter: &mut Interpreter,
921 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
922 ) {
923 self.step_inlined(interpreter, ecx);
924 }
925
926 fn step_end(
927 &mut self,
928 interpreter: &mut Interpreter,
929 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
930 ) {
931 self.step_end_inlined(interpreter, ecx);
932 }
933
934 #[allow(clippy::redundant_clone)]
935 fn log(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>, log: Log) {
936 call_inspectors!(
937 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
938 |inspector| inspector.log(ecx, log.clone()),
939 );
940 }
941
942 #[allow(clippy::redundant_clone)]
943 fn log_full(
944 &mut self,
945 interpreter: &mut Interpreter,
946 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
947 log: Log,
948 ) {
949 call_inspectors!(
950 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
951 |inspector| inspector.log_full(interpreter, ecx, log.clone()),
952 );
953 }
954
955 fn call(
956 &mut self,
957 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
958 call: &mut CallInputs,
959 ) -> Option<CallOutcome> {
960 if self.in_inner_context && ecx.journaled_state.depth == 1 {
961 self.adjust_evm_data_for_inner_context(ecx);
962 return None;
963 }
964
965 if ecx.journaled_state.depth == 0 {
966 self.top_level_frame_start(ecx);
967 }
968
969 call_inspectors!(
970 #[ret]
971 [
972 &mut self.fuzzer,
973 &mut self.tracer,
974 &mut self.log_collector,
975 &mut self.printer,
976 &mut self.revert_diag
977 ],
978 |inspector| {
979 let mut out = None;
980 if let Some(output) = inspector.call(ecx, call) {
981 out = Some(Some(output));
982 }
983 out
984 },
985 );
986
987 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
988 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
990 let input_bytes = call.input.bytes(ecx);
991 if let Some(target) = mocks
994 .get(&input_bytes)
995 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
996 {
997 call.bytecode_address = *target;
998 call.known_bytecode = None;
999 }
1000 }
1001
1002 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1003 return Some(output);
1004 }
1005 }
1006
1007 if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 {
1008 match call.scheme {
1009 CallScheme::Call => {
1011 let input = call.input.bytes(ecx);
1012 let (result, _) = self.transact_inner(
1013 ecx,
1014 TxKind::Call(call.target_address),
1015 call.caller,
1016 input,
1017 call.gas_limit,
1018 call.value.get(),
1019 );
1020 return Some(CallOutcome {
1021 result,
1022 memory_offset: call.return_memory_offset.clone(),
1023 was_precompile_called: true,
1024 precompile_call_logs: vec![],
1025 });
1026 }
1027 CallScheme::StaticCall => {
1029 let JournaledState { state, warm_addresses, .. } =
1030 &mut ecx.journaled_state.inner;
1031 for (addr, acc_mut) in state {
1032 if let Some(cheatcodes) = &self.cheatcodes
1034 && cheatcodes.has_arbitrary_storage(addr)
1035 {
1036 continue;
1037 }
1038
1039 if warm_addresses.is_cold(addr) {
1040 acc_mut.mark_cold();
1041 }
1042
1043 for slot_mut in acc_mut.storage.values_mut() {
1044 slot_mut.is_cold = true;
1045 }
1046 }
1047 }
1048 CallScheme::CallCode | CallScheme::DelegateCall => {}
1050 }
1051 }
1052
1053 None
1054 }
1055
1056 fn call_end(
1057 &mut self,
1058 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1059 inputs: &CallInputs,
1060 outcome: &mut CallOutcome,
1061 ) {
1062 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1065 return;
1066 }
1067
1068 self.do_call_end(ecx, inputs, outcome);
1069
1070 if ecx.journaled_state.depth == 0 {
1071 self.top_level_frame_end(ecx, outcome.result.result);
1072 }
1073 }
1074
1075 fn create(
1076 &mut self,
1077 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1078 create: &mut CreateInputs,
1079 ) -> Option<CreateOutcome> {
1080 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1081 self.adjust_evm_data_for_inner_context(ecx);
1082 return None;
1083 }
1084
1085 if ecx.journaled_state.depth == 0 {
1086 self.top_level_frame_start(ecx);
1087 }
1088
1089 call_inspectors!(
1090 #[ret]
1091 [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1092 |inspector| inspector.create(ecx, create).map(Some),
1093 );
1094
1095 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1096 && self.enable_isolation
1097 && !self.in_inner_context
1098 && ecx.journaled_state.depth == 1
1099 {
1100 let (result, address) = self.transact_inner(
1101 ecx,
1102 TxKind::Create,
1103 create.caller(),
1104 create.init_code().clone(),
1105 create.gas_limit(),
1106 create.value(),
1107 );
1108 return Some(CreateOutcome { result, address });
1109 }
1110
1111 None
1112 }
1113
1114 fn create_end(
1115 &mut self,
1116 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1117 call: &CreateInputs,
1118 outcome: &mut CreateOutcome,
1119 ) {
1120 if self.in_inner_context && ecx.journaled_state.depth == 1 {
1123 return;
1124 }
1125
1126 self.do_create_end(ecx, call, outcome);
1127
1128 if ecx.journaled_state.depth == 0 {
1129 self.top_level_frame_end(ecx, outcome.result.result);
1130 }
1131 }
1132
1133 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1134 call_inspectors!([&mut self.printer], |inspector| Inspector::<
1135 EthEvmContext<&mut dyn DatabaseExt>,
1136 >::selfdestruct(
1137 inspector, contract, target, value,
1138 ));
1139 }
1140}
1141
1142impl InspectorExt for InspectorStackRefMut<'_> {
1143 fn should_use_create2_factory(
1144 &mut self,
1145 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1146 inputs: &CreateInputs,
1147 ) -> bool {
1148 call_inspectors!(
1149 #[ret]
1150 [&mut self.cheatcodes],
1151 |inspector| { inspector.should_use_create2_factory(ecx, inputs).then_some(true) },
1152 );
1153
1154 false
1155 }
1156
1157 fn console_log(&mut self, msg: &str) {
1158 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1159 inspector, msg
1160 ));
1161 }
1162
1163 fn get_networks(&self) -> NetworkConfigs {
1164 self.inner.networks
1165 }
1166
1167 fn create2_deployer(&self) -> Address {
1168 self.inner.create2_deployer
1169 }
1170}
1171
1172impl Inspector<EthEvmContext<&mut dyn DatabaseExt>> for InspectorStack {
1173 fn step(
1174 &mut self,
1175 interpreter: &mut Interpreter,
1176 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1177 ) {
1178 self.as_mut().step_inlined(interpreter, ecx)
1179 }
1180
1181 fn step_end(
1182 &mut self,
1183 interpreter: &mut Interpreter,
1184 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1185 ) {
1186 self.as_mut().step_end_inlined(interpreter, ecx)
1187 }
1188
1189 fn call(
1190 &mut self,
1191 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1192 inputs: &mut CallInputs,
1193 ) -> Option<CallOutcome> {
1194 self.as_mut().call(context, inputs)
1195 }
1196
1197 fn call_end(
1198 &mut self,
1199 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1200 inputs: &CallInputs,
1201 outcome: &mut CallOutcome,
1202 ) {
1203 self.as_mut().call_end(context, inputs, outcome)
1204 }
1205
1206 fn create(
1207 &mut self,
1208 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1209 create: &mut CreateInputs,
1210 ) -> Option<CreateOutcome> {
1211 self.as_mut().create(context, create)
1212 }
1213
1214 fn create_end(
1215 &mut self,
1216 context: &mut EthEvmContext<&mut dyn DatabaseExt>,
1217 call: &CreateInputs,
1218 outcome: &mut CreateOutcome,
1219 ) {
1220 self.as_mut().create_end(context, call, outcome)
1221 }
1222
1223 fn initialize_interp(
1224 &mut self,
1225 interpreter: &mut Interpreter,
1226 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1227 ) {
1228 self.as_mut().initialize_interp(interpreter, ecx)
1229 }
1230
1231 fn log(&mut self, ecx: &mut EthEvmContext<&mut dyn DatabaseExt>, log: Log) {
1232 self.as_mut().log(ecx, log)
1233 }
1234
1235 fn log_full(
1236 &mut self,
1237 interpreter: &mut Interpreter,
1238 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1239 log: Log,
1240 ) {
1241 self.as_mut().log_full(interpreter, ecx, log)
1242 }
1243
1244 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1245 self.as_mut().selfdestruct(contract, target, value);
1246 }
1247}
1248
1249impl InspectorExt for InspectorStack {
1250 fn should_use_create2_factory(
1251 &mut self,
1252 ecx: &mut EthEvmContext<&mut dyn DatabaseExt>,
1253 inputs: &CreateInputs,
1254 ) -> bool {
1255 self.as_mut().should_use_create2_factory(ecx, inputs)
1256 }
1257
1258 fn get_networks(&self) -> NetworkConfigs {
1259 self.networks
1260 }
1261
1262 fn create2_deployer(&self) -> Address {
1263 self.create2_deployer
1264 }
1265}
1266
1267impl<'a> Deref for InspectorStackRefMut<'a> {
1268 type Target = &'a mut InspectorStackInner;
1269
1270 fn deref(&self) -> &Self::Target {
1271 &self.inner
1272 }
1273}
1274
1275impl DerefMut for InspectorStackRefMut<'_> {
1276 fn deref_mut(&mut self) -> &mut Self::Target {
1277 &mut self.inner
1278 }
1279}
1280
1281impl Deref for InspectorStack {
1282 type Target = InspectorStackInner;
1283
1284 fn deref(&self) -> &Self::Target {
1285 &self.inner
1286 }
1287}
1288
1289impl DerefMut for InspectorStack {
1290 fn deref_mut(&mut self) -> &mut Self::Target {
1291 &mut self.inner
1292 }
1293}