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 env::FoundryContextExt,
16 evm::{
17 BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
18 FoundryEvmNetwork, IntoNestedEvm, NestedEvm, SpecFor, TxEnvFor, with_cloned_context,
19 },
20};
21use foundry_evm_coverage::HitMaps;
22use foundry_evm_networks::NetworkConfigs;
23use foundry_evm_traces::{SparsedTraceArena, TraceMode};
24use revm::{
25 Inspector,
26 context::{
27 Block, Cfg, ContextTr, JournalTr, Transaction,
28 result::{EVMError, ExecutionResult, Output},
29 },
30 context_interface::CreateScheme,
31 inspector::JournalExt,
32 interpreter::{
33 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult,
34 Interpreter, InterpreterResult,
35 },
36 state::{Account, AccountStatus},
37};
38use revm_inspectors::edge_cov::EdgeCovInspector;
39use std::{
40 ops::{Deref, DerefMut},
41 sync::Arc,
42};
43
44#[derive(Clone, Debug)]
45#[must_use = "builders do nothing unless you call `build` on them"]
46pub struct InspectorStackBuilder<BLOCK: Clone> {
47 pub analysis: Option<Analysis>,
49 pub block: Option<BLOCK>,
54 pub gas_price: Option<u128>,
59 pub cheatcodes: Option<Arc<CheatsConfig>>,
61 pub fuzzer: Option<Fuzzer>,
63 pub trace_mode: TraceMode,
65 pub logs: Option<bool>,
70 pub line_coverage: Option<bool>,
72 pub print: Option<bool>,
74 pub chisel_state: Option<usize>,
76 pub enable_isolation: bool,
80 pub networks: NetworkConfigs,
82 pub wallets: Option<Wallets>,
84 pub create2_deployer: Address,
86}
87
88impl<BLOCK: Clone> Default for InspectorStackBuilder<BLOCK> {
89 fn default() -> Self {
90 Self {
91 analysis: None,
92 block: None,
93 gas_price: None,
94 cheatcodes: None,
95 fuzzer: None,
96 trace_mode: TraceMode::None,
97 logs: None,
98 line_coverage: None,
99 print: None,
100 chisel_state: None,
101 enable_isolation: false,
102 networks: NetworkConfigs::default(),
103 wallets: None,
104 create2_deployer: Default::default(),
105 }
106 }
107}
108
109impl<BLOCK: Clone> InspectorStackBuilder<BLOCK> {
110 #[inline]
112 pub fn new() -> Self {
113 Self::default()
114 }
115
116 #[inline]
118 pub fn set_analysis(mut self, analysis: Analysis) -> Self {
119 self.analysis = Some(analysis);
120 self
121 }
122
123 #[inline]
125 pub fn block(mut self, block: BLOCK) -> Self {
126 self.block = Some(block);
127 self
128 }
129
130 #[inline]
132 pub fn gas_price(mut self, gas_price: u128) -> Self {
133 self.gas_price = Some(gas_price);
134 self
135 }
136
137 #[inline]
139 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
140 self.cheatcodes = Some(config);
141 self
142 }
143
144 #[inline]
146 pub fn wallets(mut self, wallets: Wallets) -> Self {
147 self.wallets = Some(wallets);
148 self
149 }
150
151 #[inline]
153 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
154 self.fuzzer = Some(fuzzer);
155 self
156 }
157
158 #[inline]
160 pub fn chisel_state(mut self, final_pc: usize) -> Self {
161 self.chisel_state = Some(final_pc);
162 self
163 }
164
165 #[inline]
167 pub fn logs(mut self, live_logs: bool) -> Self {
168 self.logs = Some(live_logs);
169 self
170 }
171
172 #[inline]
174 pub fn line_coverage(mut self, yes: bool) -> Self {
175 self.line_coverage = Some(yes);
176 self
177 }
178
179 #[inline]
181 pub fn print(mut self, yes: bool) -> Self {
182 self.print = Some(yes);
183 self
184 }
185
186 #[inline]
189 pub fn trace_mode(mut self, mode: TraceMode) -> Self {
190 if self.trace_mode < mode {
191 self.trace_mode = mode
192 }
193 self
194 }
195
196 #[inline]
199 pub fn enable_isolation(mut self, yes: bool) -> Self {
200 self.enable_isolation = yes;
201 self
202 }
203
204 #[inline]
206 pub fn networks(mut self, networks: NetworkConfigs) -> Self {
207 self.networks = networks;
208 self
209 }
210
211 #[inline]
212 pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
213 self.create2_deployer = create2_deployer;
214 self
215 }
216
217 pub fn build<FEN: FoundryEvmNetwork<EvmFactory: FoundryEvmFactory<BlockEnv = BLOCK>>>(
219 self,
220 ) -> InspectorStack<FEN> {
221 let Self {
222 analysis,
223 block,
224 gas_price,
225 cheatcodes,
226 fuzzer,
227 trace_mode,
228 logs,
229 line_coverage,
230 print,
231 chisel_state,
232 enable_isolation,
233 networks,
234 wallets,
235 create2_deployer,
236 } = self;
237 let mut stack = InspectorStack::new();
238
239 if let Some(config) = cheatcodes {
241 let mut cheatcodes = Cheatcodes::new(config);
242 if let Some(analysis) = analysis {
244 stack.set_analysis(analysis.clone());
245 cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
246 }
247 if let Some(wallets) = wallets {
249 cheatcodes.set_wallets(wallets);
250 }
251 stack.set_cheatcodes(cheatcodes);
252 }
253
254 if let Some(fuzzer) = fuzzer {
255 stack.set_fuzzer(fuzzer);
256 }
257 if let Some(chisel_state) = chisel_state {
258 stack.set_chisel(chisel_state);
259 }
260 stack.collect_line_coverage(line_coverage.unwrap_or(false));
261 stack.collect_logs(logs);
262 stack.print(print.unwrap_or(false));
263 stack.tracing(trace_mode);
264
265 stack.enable_isolation(enable_isolation);
266 stack.networks(networks);
267 stack.set_create2_deployer(create2_deployer);
268
269 if networks.is_tempo() {
270 stack.inner.tempo_labels = Some(Box::default());
271 }
272
273 if let Some(block) = block {
275 stack.set_block(block);
276 }
277 if let Some(gas_price) = gas_price {
278 stack.set_gas_price(gas_price);
279 }
280
281 stack
282 }
283}
284
285#[macro_export]
288macro_rules! call_inspectors {
289 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
290 $(
291 if let Some($id) = $inspector {
292 $crate::utils::cold_path();
293 $body;
294 }
295 )+
296 };
297 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
298 $(
299 if let Some($id) = $inspector {
300 $crate::utils::cold_path();
301 if let Some(result) = $body {
302 return result;
303 }
304 }
305 )+
306 }};
307}
308
309pub struct InspectorData<FEN: FoundryEvmNetwork> {
311 pub logs: Vec<Log>,
312 pub labels: AddressHashMap<String>,
313 pub traces: Option<SparsedTraceArena>,
314 pub line_coverage: Option<HitMaps>,
315 pub edge_coverage: Option<Vec<u8>>,
316 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
317 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
318 pub reverter: Option<Address>,
319}
320
321#[derive(Debug, Clone)]
327pub struct InnerContextData {
328 original_origin: Address,
330}
331
332#[derive(Clone, Debug)]
343pub struct InspectorStack<FEN: FoundryEvmNetwork = EthEvmNetwork> {
344 #[allow(clippy::type_complexity)]
345 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
346 pub inner: InspectorStackInner,
347}
348
349#[derive(Default, Clone, Debug)]
353pub struct InspectorStackInner {
354 pub analysis: Option<Analysis>,
356
357 pub chisel_state: Option<Box<ChiselState>>,
361 pub edge_coverage: Option<Box<EdgeCovInspector>>,
362 pub fuzzer: Option<Box<Fuzzer>>,
363 pub line_coverage: Option<Box<LineCoverageCollector>>,
364 pub log_collector: Option<Box<LogCollector>>,
365 pub printer: Option<Box<CustomPrintTracer>>,
366 pub revert_diag: Option<Box<RevertDiagnostic>>,
367 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
368 pub tempo_labels: Option<Box<TempoLabels>>,
369 pub tracer: Option<Box<TracingInspector>>,
370
371 pub enable_isolation: bool,
373 pub networks: NetworkConfigs,
374 pub create2_deployer: Address,
375 pub in_inner_context: bool,
377 pub inner_context_data: Option<InnerContextData>,
378 pub top_frame_journal: AddressMap<Account>,
379 pub reverter: Option<Address>,
381}
382
383pub struct InspectorStackRefMut<'a, FEN: FoundryEvmNetwork = EthEvmNetwork> {
386 pub cheatcodes: Option<&'a mut Cheatcodes<FEN>>,
387 pub inner: &'a mut InspectorStackInner,
388}
389
390impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for InspectorStackInner {
391 fn with_nested_evm(
392 &mut self,
393 cheats: &mut Cheatcodes<FEN>,
394 ecx: &mut FoundryContextFor<'_, FEN>,
395 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
396 ) -> Result<(), EVMError<DatabaseError>> {
397 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
398 with_cloned_context(ecx, |db, evm_env, journal_inner| {
399 let mut evm = FEN::EvmFactory::default()
400 .create_foundry_evm_with_inspector(db, evm_env, &mut inspector)
401 .into_nested_evm();
402 *evm.journal_inner_mut() = journal_inner;
403 f(&mut evm)?;
404 let sub_inner = evm.journal_inner_mut().clone();
405 let sub_evm_env = evm.to_evm_env();
406 Ok((sub_evm_env, sub_inner))
407 })
408 }
409
410 fn with_fresh_nested_evm(
411 &mut self,
412 cheats: &mut Cheatcodes<FEN>,
413 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
414 evm_env: EvmEnvFor<FEN>,
415 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
416 ) -> Result<EvmEnvFor<FEN>, EVMError<DatabaseError>> {
417 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
418 let mut evm = FEN::EvmFactory::default()
419 .create_foundry_evm_with_inspector(db, evm_env, &mut inspector)
420 .into_nested_evm();
421 f(&mut evm)?;
422 Ok(evm.to_evm_env())
423 }
424
425 fn transact_on_db(
426 &mut self,
427 cheats: &mut Cheatcodes<FEN>,
428 ecx: &mut FoundryContextFor<'_, FEN>,
429 fork_id: Option<U256>,
430 transaction: B256,
431 ) -> eyre::Result<()> {
432 let evm_env = ecx.evm_clone();
433 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
434 let (db, inner) = ecx.db_journal_inner_mut();
435 db.transact(fork_id, transaction, evm_env, inner, &mut inspector)
436 }
437
438 fn transact_from_tx_on_db(
439 &mut self,
440 cheats: &mut Cheatcodes<FEN>,
441 ecx: &mut FoundryContextFor<'_, FEN>,
442 tx_env: TxEnvFor<FEN>,
443 ) -> eyre::Result<()> {
444 let evm_env = ecx.evm_clone();
445 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
446 let (db, inner) = ecx.db_journal_inner_mut();
447 db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
448 }
449
450 fn console_log(&mut self, msg: &str) {
451 if let Some(ref mut collector) = self.log_collector {
452 InspectorExt::console_log(&mut **collector, msg);
453 }
454 }
455
456 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
457 self.tracer.as_deref_mut()
458 }
459
460 fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
461 self.in_inner_context = enabled;
462 self.inner_context_data = enabled.then(|| InnerContextData {
463 original_origin: original_origin.expect("origin required when enabling inner ctx"),
464 });
465 }
466}
467
468impl<FEN: FoundryEvmNetwork> Default for InspectorStack<FEN> {
469 fn default() -> Self {
470 Self::new()
471 }
472}
473
474impl<FEN: FoundryEvmNetwork> InspectorStack<FEN> {
475 #[inline]
481 pub fn new() -> Self {
482 Self { cheatcodes: None, inner: InspectorStackInner::default() }
483 }
484
485 #[inline]
487 pub fn set_analysis(&mut self, analysis: Analysis) {
488 self.analysis = Some(analysis);
489 }
490
491 #[inline]
493 pub fn set_block(&mut self, block: BlockEnvFor<FEN>) {
494 if let Some(cheatcodes) = &mut self.cheatcodes {
495 cheatcodes.block = Some(block);
496 }
497 }
498
499 #[inline]
501 pub fn set_gas_price(&mut self, gas_price: u128) {
502 if let Some(cheatcodes) = &mut self.cheatcodes {
503 cheatcodes.gas_price = Some(gas_price);
504 }
505 }
506
507 #[inline]
509 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes<FEN>) {
510 self.cheatcodes = Some(cheatcodes.into());
511 }
512
513 #[inline]
515 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
516 self.fuzzer = Some(fuzzer.into());
517 }
518
519 #[inline]
521 pub fn set_chisel(&mut self, final_pc: usize) {
522 self.chisel_state = Some(ChiselState::new(final_pc).into());
523 }
524
525 #[inline]
527 pub fn collect_line_coverage(&mut self, yes: bool) {
528 self.line_coverage = yes.then(Default::default);
529 }
530
531 #[inline]
533 pub fn collect_edge_coverage(&mut self, yes: bool) {
534 self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
536 }
537
538 #[inline]
540 pub fn enable_isolation(&mut self, yes: bool) {
541 self.inner.enable_isolation = yes;
542 }
543
544 #[inline]
546 pub fn networks(&mut self, networks: NetworkConfigs) {
547 self.inner.networks = networks;
548 }
549
550 #[inline]
552 pub fn set_create2_deployer(&mut self, deployer: Address) {
553 self.create2_deployer = deployer;
554 }
555
556 #[inline]
561 pub fn collect_logs(&mut self, live_logs: Option<bool>) {
562 self.log_collector = live_logs.map(|live_logs| {
563 Box::new(if live_logs {
564 LogCollector::LiveLogs
565 } else {
566 LogCollector::Capture { logs: Vec::new() }
567 })
568 });
569 }
570
571 #[inline]
573 pub fn print(&mut self, yes: bool) {
574 self.printer = yes.then(Default::default);
575 }
576
577 #[inline]
580 pub fn tracing(&mut self, mode: TraceMode) {
581 self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
582
583 if let Some(config) = mode.into_config() {
584 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
585 } else {
586 self.tracer = None;
587 }
588 }
589
590 #[inline]
592 pub fn script(&mut self, script_address: Address) {
593 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
594 script_address;
595 }
596
597 #[inline(always)]
598 fn as_mut(&mut self) -> InspectorStackRefMut<'_, FEN> {
599 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
600 }
601
602 pub fn collect(self) -> InspectorData<FEN> {
604 let Self {
605 mut cheatcodes,
606 inner:
607 InspectorStackInner {
608 chisel_state,
609 line_coverage,
610 edge_coverage,
611 log_collector,
612 tempo_labels,
613 tracer,
614 reverter,
615 ..
616 },
617 } = self;
618
619 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
620 let ignored = cheatcodes
621 .as_mut()
622 .map(|cheatcodes| {
623 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
624
625 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
627 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
628 }
629
630 ignored
631 })
632 .unwrap_or_default();
633
634 SparsedTraceArena { arena, ignored }
635 });
636
637 InspectorData {
638 logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
639 labels: {
640 let mut labels = cheatcodes.as_ref().map(|c| c.labels.clone()).unwrap_or_default();
641 if let Some(tempo_labels) = tempo_labels {
642 labels.extend(tempo_labels.labels);
643 }
644 labels
645 },
646 traces,
647 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
648 edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()),
649 cheatcodes,
650 chisel_state: chisel_state.and_then(|state| state.state),
651 reverter,
652 }
653 }
654}
655
656impl<FEN: FoundryEvmNetwork> InspectorStackRefMut<'_, FEN> {
657 fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
662 let inner_context_data =
663 self.inner_context_data.as_ref().expect("should be called in inner context");
664 ecx.tx_mut().set_caller(inner_context_data.original_origin);
665 }
666
667 fn do_call_end(
668 &mut self,
669 ecx: &mut FoundryContextFor<'_, FEN>,
670 inputs: &CallInputs,
671 outcome: &mut CallOutcome,
672 ) {
673 let result = outcome.result.result;
674 call_inspectors!(
675 #[ret]
676 [
677 &mut self.fuzzer,
678 &mut self.tracer,
679 &mut self.cheatcodes,
680 &mut self.printer,
681 &mut self.revert_diag
682 ],
683 |inspector| {
684 let previous_outcome = outcome.clone();
685 inspector.call_end(ecx, inputs, outcome);
686
687 let different = outcome.result.result != result
690 || (outcome.result.result == InstructionResult::Revert
691 && outcome.output() != previous_outcome.output());
692 different.then_some(())
693 },
694 );
695
696 if result.is_revert() && self.reverter.is_none() {
698 self.reverter = Some(inputs.target_address);
699 }
700 }
701
702 fn do_create_end(
703 &mut self,
704 ecx: &mut FoundryContextFor<'_, FEN>,
705 call: &CreateInputs,
706 outcome: &mut CreateOutcome,
707 ) {
708 let result = outcome.result.result;
709 call_inspectors!(
710 #[ret]
711 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
712 |inspector| {
713 let previous_outcome = outcome.clone();
714 inspector.create_end(ecx, call, outcome);
715
716 let different = outcome.result.result != result
719 || (outcome.result.result == InstructionResult::Revert
720 && outcome.output() != previous_outcome.output());
721 different.then_some(())
722 },
723 );
724 }
725
726 fn transact_inner(
727 &mut self,
728 ecx: &mut FoundryContextFor<'_, FEN>,
729 kind: TxKind,
730 caller: Address,
731 input: Bytes,
732 gas_limit: u64,
733 value: U256,
734 ) -> (InterpreterResult, Option<Address>) {
735 let cached_evm_env = ecx.evm_clone();
736 let cached_tx_env = ecx.tx_clone();
737
738 ecx.block_mut().set_basefee(0);
739
740 let chain_id = ecx.cfg().chain_id();
741 ecx.tx_mut().set_chain_id(Some(chain_id));
742 ecx.tx_mut().set_caller(caller);
743 ecx.tx_mut().set_kind(kind);
744 ecx.tx_mut().set_data(input);
745 ecx.tx_mut().set_value(value);
746 ecx.tx_mut().set_gas_limit(gas_limit + 21000);
748
749 if !ecx.cfg().is_block_gas_limit_disabled() {
752 let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
753 ecx.tx_mut().set_gas_limit(gas_limit);
754 }
755 ecx.tx_mut().set_gas_price(0);
756
757 self.inner_context_data =
758 Some(InnerContextData { original_origin: cached_tx_env.caller() });
759 self.in_inner_context = true;
760
761 let evm_env = ecx.evm_clone();
762 let tx_env = ecx.tx_clone();
763
764 let res = self.with_inspector(|mut inspector| {
765 let (res, nested_env) = {
766 let (db, journal) = ecx.db_journal_inner_mut();
767 let mut evm = FEN::EvmFactory::default()
768 .create_foundry_evm_with_inspector(db, evm_env, &mut inspector)
769 .into_nested_evm();
770
771 evm.journal_inner_mut().state = {
772 let mut state = journal.state.clone();
773
774 for (addr, acc_mut) in &mut state {
775 if journal.warm_addresses.is_cold(addr) {
777 acc_mut.mark_cold();
778 }
779
780 for slot_mut in acc_mut.storage.values_mut() {
782 slot_mut.is_cold = true;
783 slot_mut.original_value = slot_mut.present_value;
784 }
785 }
786
787 state
788 };
789
790 evm.journal_inner_mut().depth = 1;
792
793 let res = evm.transact_raw(tx_env);
794 let nested_evm_env = evm.to_evm_env();
795 (res, nested_evm_env)
796 };
797
798 let mut restored_evm_env = nested_env;
801 restored_evm_env.block_env.set_basefee(cached_evm_env.block_env.basefee());
802 ecx.set_evm(restored_evm_env);
803 ecx.set_tx(cached_tx_env);
804
805 res
806 });
807
808 self.in_inner_context = false;
809 self.inner_context_data = None;
810
811 let mut gas = Gas::new(gas_limit);
812
813 let Ok(res) = res else {
814 let result =
816 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
817 return (result, None);
818 };
819
820 for (addr, mut acc) in res.state {
821 let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
822 ecx.journal_mut().evm_state_mut().insert(addr, acc);
823 continue;
824 };
825
826 if acc.status.contains(AccountStatus::Cold)
828 && !acc_mut.status.contains(AccountStatus::Cold)
829 {
830 acc.status -= AccountStatus::Cold;
831 }
832 acc_mut.info = acc.info;
833 acc_mut.status |= acc.status;
834
835 for (key, val) in acc.storage {
836 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
837 acc_mut.storage.insert(key, val);
838 continue;
839 };
840 slot_mut.present_value = val.present_value;
841 slot_mut.is_cold &= val.is_cold;
842 }
843 }
844
845 let (result, address, output) = match res.result {
846 ExecutionResult::Success { reason, gas: result_gas, logs: _, output } => {
847 gas.set_refund(result_gas.final_refunded() as i64);
848 let _ = gas.record_cost(result_gas.used());
849 let address = match output {
850 Output::Create(_, address) => address,
851 Output::Call(_) => None,
852 };
853 (reason.into(), address, output.into_data())
854 }
855 ExecutionResult::Halt { reason, gas: result_gas, .. } => {
856 let _ = gas.record_cost(result_gas.used());
857 (InstructionResult::from(reason), None, Bytes::new())
858 }
859 ExecutionResult::Revert { gas: result_gas, output, .. } => {
860 let _ = gas.record_cost(result_gas.used());
861 (InstructionResult::Revert, None, output)
862 }
863 };
864 (InterpreterResult { result, output, gas }, address)
865 }
866
867 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_, FEN>) -> O) -> O {
870 let mut cheatcodes = self
871 .cheatcodes
872 .as_deref_mut()
873 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
874 let mut inner = std::mem::take(self.inner);
875
876 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
877
878 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
879 *cheats = cheatcodes.unwrap();
880 }
881
882 *self.inner = inner;
883
884 out
885 }
886
887 fn top_level_frame_start(&mut self, ecx: &mut FoundryContextFor<'_, FEN>) {
889 if self.enable_isolation {
890 self.top_frame_journal.clone_from(ecx.journal().evm_state());
893 }
894 }
895
896 fn top_level_frame_end(
898 &mut self,
899 ecx: &mut FoundryContextFor<'_, FEN>,
900 result: InstructionResult,
901 ) {
902 if !result.is_revert() {
903 return;
904 }
905 if let Some(cheats) = self.cheatcodes.as_mut() {
909 cheats.on_revert(ecx);
910 }
911
912 if self.enable_isolation {
916 *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
917 }
918 }
919
920 #[inline(always)]
926 fn step_inlined(
927 &mut self,
928 interpreter: &mut Interpreter,
929 ecx: &mut FoundryContextFor<'_, FEN>,
930 ) {
931 call_inspectors!(
932 [
933 &mut self.edge_coverage,
935 &mut self.fuzzer,
936 &mut self.line_coverage,
937 &mut self.printer,
938 &mut self.revert_diag,
939 &mut self.script_execution_inspector,
940 &mut self.tracer,
941 &mut self.cheatcodes,
943 ],
944 |inspector| (**inspector).step(interpreter, ecx),
945 );
946 }
947
948 #[inline(always)]
949 fn step_end_inlined(
950 &mut self,
951 interpreter: &mut Interpreter,
952 ecx: &mut FoundryContextFor<'_, FEN>,
953 ) {
954 call_inspectors!(
955 [
956 &mut self.chisel_state,
958 &mut self.printer,
959 &mut self.revert_diag,
960 &mut self.tracer,
961 &mut self.cheatcodes,
963 ],
964 |inspector| (**inspector).step_end(interpreter, ecx),
965 );
966 }
967}
968
969impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>>
970 for InspectorStackRefMut<'_, FEN>
971{
972 fn initialize_interp(
973 &mut self,
974 interpreter: &mut Interpreter,
975 ecx: &mut FoundryContextFor<'_, FEN>,
976 ) {
977 call_inspectors!(
978 [
979 &mut self.line_coverage,
980 &mut self.tracer,
981 &mut self.cheatcodes,
982 &mut self.script_execution_inspector,
983 &mut self.printer
984 ],
985 |inspector| inspector.initialize_interp(interpreter, ecx),
986 );
987 }
988
989 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
990 self.step_inlined(interpreter, ecx);
991 }
992
993 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
994 self.step_end_inlined(interpreter, ecx);
995 }
996
997 #[allow(clippy::redundant_clone)]
998 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
999 call_inspectors!(
1000 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
1001 |inspector| inspector.log(ecx, log.clone()),
1002 );
1003 }
1004
1005 #[allow(clippy::redundant_clone)]
1006 fn log_full(
1007 &mut self,
1008 interpreter: &mut Interpreter,
1009 ecx: &mut FoundryContextFor<'_, FEN>,
1010 log: Log,
1011 ) {
1012 call_inspectors!(
1013 [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
1014 |inspector| inspector.log_full(interpreter, ecx, log.clone()),
1015 );
1016 }
1017
1018 fn call(
1019 &mut self,
1020 ecx: &mut FoundryContextFor<'_, FEN>,
1021 call: &mut CallInputs,
1022 ) -> Option<CallOutcome> {
1023 if self.in_inner_context && ecx.journal().depth() == 1 {
1024 self.adjust_evm_data_for_inner_context(ecx);
1025 return None;
1026 }
1027
1028 if ecx.journal().depth() == 0 {
1029 self.top_level_frame_start(ecx);
1030 }
1031
1032 call_inspectors!(
1033 #[ret]
1034 [
1035 &mut self.fuzzer,
1036 &mut self.tracer,
1037 &mut self.log_collector,
1038 &mut self.printer,
1039 &mut self.revert_diag,
1040 &mut self.tempo_labels
1041 ],
1042 |inspector| {
1043 let mut out = None;
1044 if let Some(output) = inspector.call(ecx, call) {
1045 out = Some(Some(output));
1046 }
1047 out
1048 },
1049 );
1050
1051 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1052 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1054 let input_bytes = call.input.bytes(ecx);
1055 if let Some(target) = mocks
1058 .get(&input_bytes)
1059 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1060 {
1061 call.bytecode_address = *target;
1062 call.known_bytecode = None;
1063 }
1064 }
1065
1066 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1067 return Some(output);
1068 }
1069 }
1070
1071 if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1072 match call.scheme {
1073 CallScheme::Call => {
1075 let input = call.input.bytes(ecx);
1076 let (result, _) = self.transact_inner(
1077 ecx,
1078 TxKind::Call(call.target_address),
1079 call.caller,
1080 input,
1081 call.gas_limit,
1082 call.value.get(),
1083 );
1084 return Some(CallOutcome {
1085 result,
1086 memory_offset: call.return_memory_offset.clone(),
1087 was_precompile_called: true,
1088 precompile_call_logs: vec![],
1089 });
1090 }
1091 CallScheme::StaticCall => {
1093 let (_, journal_inner) = ecx.db_journal_inner_mut();
1094 let JournaledState { state, warm_addresses, .. } = journal_inner;
1095 for (addr, acc_mut) in state {
1096 if let Some(cheatcodes) = &self.cheatcodes
1098 && cheatcodes.has_arbitrary_storage(addr)
1099 {
1100 continue;
1101 }
1102
1103 if warm_addresses.is_cold(addr) {
1104 acc_mut.mark_cold();
1105 }
1106
1107 for slot_mut in acc_mut.storage.values_mut() {
1108 slot_mut.is_cold = true;
1109 }
1110 }
1111 }
1112 CallScheme::CallCode | CallScheme::DelegateCall => {}
1114 }
1115 }
1116
1117 None
1118 }
1119
1120 fn call_end(
1121 &mut self,
1122 ecx: &mut FoundryContextFor<'_, FEN>,
1123 inputs: &CallInputs,
1124 outcome: &mut CallOutcome,
1125 ) {
1126 if self.in_inner_context && ecx.journal().depth() == 1 {
1129 return;
1130 }
1131
1132 self.do_call_end(ecx, inputs, outcome);
1133
1134 if ecx.journal().depth() == 0 {
1135 self.top_level_frame_end(ecx, outcome.result.result);
1136 }
1137 }
1138
1139 fn create(
1140 &mut self,
1141 ecx: &mut FoundryContextFor<'_, FEN>,
1142 create: &mut CreateInputs,
1143 ) -> Option<CreateOutcome> {
1144 if self.in_inner_context && ecx.journal().depth() == 1 {
1145 self.adjust_evm_data_for_inner_context(ecx);
1146 return None;
1147 }
1148
1149 if ecx.journal().depth() == 0 {
1150 self.top_level_frame_start(ecx);
1151 }
1152
1153 call_inspectors!(
1154 #[ret]
1155 [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1156 |inspector| inspector.create(ecx, create).map(Some),
1157 );
1158
1159 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1160 && self.enable_isolation
1161 && !self.in_inner_context
1162 && ecx.journal().depth() == 1
1163 {
1164 let (result, address) = self.transact_inner(
1165 ecx,
1166 TxKind::Create,
1167 create.caller(),
1168 create.init_code().clone(),
1169 create.gas_limit(),
1170 create.value(),
1171 );
1172 return Some(CreateOutcome { result, address });
1173 }
1174
1175 None
1176 }
1177
1178 fn create_end(
1179 &mut self,
1180 ecx: &mut FoundryContextFor<'_, FEN>,
1181 call: &CreateInputs,
1182 outcome: &mut CreateOutcome,
1183 ) {
1184 if self.in_inner_context && ecx.journal().depth() == 1 {
1187 return;
1188 }
1189
1190 self.do_create_end(ecx, call, outcome);
1191
1192 if ecx.journal().depth() == 0 {
1193 self.top_level_frame_end(ecx, outcome.result.result);
1194 }
1195 }
1196
1197 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1198 call_inspectors!([&mut self.printer], |inspector| {
1199 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1200 inspector, contract, target, value,
1201 )
1202 });
1203 }
1204}
1205
1206impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStackRefMut<'_, FEN> {
1207 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1208 call_inspectors!(
1209 #[ret]
1210 [&mut self.cheatcodes],
1211 |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1212 );
1213
1214 false
1215 }
1216
1217 fn console_log(&mut self, msg: &str) {
1218 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1219 inspector, msg
1220 ));
1221 }
1222
1223 fn get_networks(&self) -> NetworkConfigs {
1224 self.inner.networks
1225 }
1226
1227 fn create2_deployer(&self) -> Address {
1228 self.inner.create2_deployer
1229 }
1230}
1231
1232impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for InspectorStack<FEN> {
1233 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1234 self.as_mut().step_inlined(interpreter, ecx)
1235 }
1236
1237 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1238 self.as_mut().step_end_inlined(interpreter, ecx)
1239 }
1240
1241 fn call(
1242 &mut self,
1243 context: &mut FoundryContextFor<'_, FEN>,
1244 inputs: &mut CallInputs,
1245 ) -> Option<CallOutcome> {
1246 self.as_mut().call(context, inputs)
1247 }
1248
1249 fn call_end(
1250 &mut self,
1251 context: &mut FoundryContextFor<'_, FEN>,
1252 inputs: &CallInputs,
1253 outcome: &mut CallOutcome,
1254 ) {
1255 self.as_mut().call_end(context, inputs, outcome)
1256 }
1257
1258 fn create(
1259 &mut self,
1260 context: &mut FoundryContextFor<'_, FEN>,
1261 create: &mut CreateInputs,
1262 ) -> Option<CreateOutcome> {
1263 self.as_mut().create(context, create)
1264 }
1265
1266 fn create_end(
1267 &mut self,
1268 context: &mut FoundryContextFor<'_, FEN>,
1269 call: &CreateInputs,
1270 outcome: &mut CreateOutcome,
1271 ) {
1272 self.as_mut().create_end(context, call, outcome)
1273 }
1274
1275 fn initialize_interp(
1276 &mut self,
1277 interpreter: &mut Interpreter,
1278 ecx: &mut FoundryContextFor<'_, FEN>,
1279 ) {
1280 self.as_mut().initialize_interp(interpreter, ecx)
1281 }
1282
1283 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1284 self.as_mut().log(ecx, log)
1285 }
1286
1287 fn log_full(
1288 &mut self,
1289 interpreter: &mut Interpreter,
1290 ecx: &mut FoundryContextFor<'_, FEN>,
1291 log: Log,
1292 ) {
1293 self.as_mut().log_full(interpreter, ecx, log)
1294 }
1295
1296 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1297 call_inspectors!([&mut self.inner.printer], |inspector| {
1298 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1299 inspector, contract, target, value,
1300 )
1301 });
1302 }
1303}
1304
1305impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStack<FEN> {
1306 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1307 self.as_mut().should_use_create2_factory(depth, inputs)
1308 }
1309
1310 fn get_networks(&self) -> NetworkConfigs {
1311 self.networks
1312 }
1313
1314 fn create2_deployer(&self) -> Address {
1315 self.create2_deployer
1316 }
1317}
1318
1319impl<'a, FEN: FoundryEvmNetwork> Deref for InspectorStackRefMut<'a, FEN> {
1320 type Target = &'a mut InspectorStackInner;
1321
1322 fn deref(&self) -> &Self::Target {
1323 &self.inner
1324 }
1325}
1326
1327impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStackRefMut<'_, FEN> {
1328 fn deref_mut(&mut self) -> &mut Self::Target {
1329 &mut self.inner
1330 }
1331}
1332
1333impl<FEN: FoundryEvmNetwork> Deref for InspectorStack<FEN> {
1334 type Target = InspectorStackInner;
1335
1336 fn deref(&self) -> &Self::Target {
1337 &self.inner
1338 }
1339}
1340
1341impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStack<FEN> {
1342 fn deref_mut(&mut self) -> &mut Self::Target {
1343 &mut self.inner
1344 }
1345}