Skip to main content

anvil/eth/backend/mem/
inspector.rs

1//! Anvil specific [`revm::Inspector`] implementation
2
3use crate::eth::macros::node_info;
4use alloy_primitives::{Address, Log, U256};
5use foundry_evm::{
6    call_inspectors,
7    decode::decode_console_logs,
8    inspectors::{LogCollector, TracingInspector},
9    traces::{
10        CallTraceDecoder, CallTraceNode, SparsedTraceArena, TracingInspectorConfig,
11        render_trace_arena_inner,
12    },
13};
14use revm::{
15    Inspector,
16    context::ContextTr,
17    inspector::JournalExt,
18    interpreter::{
19        CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
20        interpreter::EthInterpreter,
21    },
22};
23use revm_inspectors::transfer::TransferInspector;
24use std::sync::Arc;
25
26/// The [`revm::Inspector`] used when transacting in the evm
27#[derive(Clone, Debug, Default)]
28pub struct AnvilInspector {
29    /// Collects all traces
30    pub tracer: Option<TracingInspector>,
31    /// Collects all `console.sol` logs
32    pub log_collector: Option<LogCollector>,
33    /// Collects all internal ETH transfers as ERC20 transfer events.
34    pub transfer: Option<TransferInspector>,
35}
36
37/// Configuration for per-transaction inspector lifecycle.
38#[derive(Clone, Debug)]
39pub struct InspectorTxConfig {
40    /// Whether to print traces to stdout.
41    pub print_traces: bool,
42    /// Whether to print logs to stdout.
43    pub print_logs: bool,
44    /// Whether to enable step-level tracing (with state diffs).
45    pub enable_steps_tracing: bool,
46    /// Decoder for populating trace labels.
47    pub call_trace_decoder: Arc<CallTraceDecoder>,
48}
49
50impl AnvilInspector {
51    /// Finish a transaction: print traces/logs, drain the tracer, and reset for the next tx.
52    ///
53    /// Returns the collected call trace nodes from the finished transaction.
54    pub fn finish_transaction(&mut self, config: &InspectorTxConfig) -> Vec<CallTraceNode> {
55        // Print before draining so the tracer is still populated.
56        if config.print_traces {
57            self.print_traces(config.call_trace_decoder.clone());
58        }
59        self.print_logs();
60
61        let traces = self.tracer.take().map(|t| t.into_traces().into_nodes()).unwrap_or_default();
62
63        // Reinstall tracer for next tx.
64        let tracing_config = if config.enable_steps_tracing {
65            TracingInspectorConfig::all().with_state_diffs()
66        } else {
67            TracingInspectorConfig::all().set_steps(false)
68        };
69        self.tracer = Some(TracingInspector::new(tracing_config));
70
71        // Reset log collector for next tx.
72        if config.print_logs {
73            self.log_collector = Some(LogCollector::Capture { logs: Vec::new() });
74        }
75
76        traces
77    }
78
79    /// Called after the inspecting the evm
80    ///
81    /// This will log all `console.sol` logs
82    pub fn print_logs(&self) {
83        if let Some(LogCollector::Capture { logs }) = &self.log_collector {
84            print_logs(logs);
85        }
86    }
87
88    /// Consumes the type and prints the traces.
89    pub fn into_print_traces(mut self, decoder: Arc<CallTraceDecoder>) {
90        if let Some(a) = self.tracer.take() {
91            print_traces(a, decoder);
92        }
93    }
94
95    /// Called after the inspecting the evm
96    /// This will log all traces
97    pub fn print_traces(&self, decoder: Arc<CallTraceDecoder>) {
98        if let Some(a) = self.tracer.clone() {
99            print_traces(a, decoder);
100        }
101    }
102
103    /// Configures the `Tracer` [`revm::Inspector`]
104    pub fn with_tracing(mut self) -> Self {
105        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
106        self
107    }
108
109    /// Configures the `TracingInspector` [`revm::Inspector`]
110    pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self {
111        self.tracer = Some(TracingInspector::new(config));
112        self
113    }
114
115    /// Enables steps recording for `Tracer`.
116    pub fn with_steps_tracing(mut self) -> Self {
117        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
118        self
119    }
120
121    /// Configures the `Tracer` [`revm::Inspector`] with a log collector
122    pub fn with_log_collector(mut self) -> Self {
123        self.log_collector = Some(LogCollector::Capture { logs: Vec::new() });
124        self
125    }
126
127    /// Configures the `Tracer` [`revm::Inspector`] with a transfer event collector
128    pub fn with_transfers(mut self) -> Self {
129        self.transfer = Some(TransferInspector::new(false).with_logs(true));
130        self
131    }
132
133    /// Configures the `Tracer` [`revm::Inspector`] with a trace printer
134    pub fn with_trace_printer(mut self) -> Self {
135        self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
136        self
137    }
138}
139
140/// Prints the traces for the inspector
141///
142/// Caution: This blocks on call trace decoding
143///
144/// # Panics
145///
146/// If called outside tokio runtime
147fn print_traces(tracer: TracingInspector, decoder: Arc<CallTraceDecoder>) {
148    let arena = tokio::task::block_in_place(move || {
149        tokio::runtime::Handle::current().block_on(async move {
150            let mut arena = tracer.into_traces();
151            decoder.populate_traces(arena.nodes_mut()).await;
152            arena
153        })
154    });
155
156    let traces = SparsedTraceArena { arena, ignored: Default::default() };
157    let trace = render_trace_arena_inner(&traces, false, true);
158    node_info!(Traces = %format!("\n{}", trace));
159}
160
161impl<CTX> Inspector<CTX, EthInterpreter> for AnvilInspector
162where
163    CTX: ContextTr<Journal: JournalExt>,
164{
165    fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
166        call_inspectors!([&mut self.tracer], |inspector| {
167            inspector.initialize_interp(interp, ecx);
168        });
169    }
170
171    fn step(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
172        call_inspectors!([&mut self.tracer], |inspector| {
173            inspector.step(interp, ecx);
174        });
175    }
176
177    fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
178        call_inspectors!([&mut self.tracer], |inspector| {
179            inspector.step_end(interp, ecx);
180        });
181    }
182
183    #[allow(clippy::redundant_clone)]
184    fn log(&mut self, ecx: &mut CTX, log: Log) {
185        call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
186            inspector.log(ecx, log.clone());
187        });
188    }
189
190    #[allow(clippy::redundant_clone)]
191    fn log_full(&mut self, interp: &mut Interpreter, ecx: &mut CTX, log: Log) {
192        call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
193            inspector.log_full(interp, ecx, log.clone());
194        });
195    }
196
197    fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
198        call_inspectors!(
199            #[ret]
200            [&mut self.tracer, &mut self.log_collector, &mut self.transfer],
201            |inspector| inspector.call(ecx, inputs).map(Some),
202        );
203        None
204    }
205
206    fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
207        if let Some(tracer) = &mut self.tracer {
208            tracer.call_end(ecx, inputs, outcome);
209        }
210    }
211
212    fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
213        call_inspectors!(
214            #[ret]
215            [&mut self.tracer, &mut self.transfer],
216            |inspector| inspector.create(ecx, inputs).map(Some),
217        );
218        None
219    }
220
221    fn create_end(&mut self, ecx: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {
222        if let Some(tracer) = &mut self.tracer {
223            tracer.create_end(ecx, inputs, outcome);
224        }
225    }
226
227    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
228        call_inspectors!([&mut self.tracer, &mut self.transfer], |inspector| {
229            Inspector::<CTX, EthInterpreter>::selfdestruct(inspector, contract, target, value)
230        });
231    }
232}
233
234/// Prints all the logs
235pub fn print_logs(logs: &[Log]) {
236    for log in decode_console_logs(logs) {
237        tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
238    }
239}