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