1use crate::{eth::macros::node_info, revm::Database};
4use alloy_primitives::{Address, Log};
5use foundry_evm::{
6 call_inspectors,
7 decode::decode_console_logs,
8 inspectors::{LogCollector, TracingInspector},
9 revm::{
10 interpreter::{
11 CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter,
12 },
13 primitives::U256,
14 EvmContext,
15 },
16 traces::{
17 render_trace_arena_inner, CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig,
18 },
19};
20
21#[derive(Clone, Debug, Default)]
23pub struct Inspector {
24 pub tracer: Option<TracingInspector>,
26 pub log_collector: Option<LogCollector>,
28}
29
30impl Inspector {
31 pub fn print_logs(&self) {
35 if let Some(collector) = &self.log_collector {
36 print_logs(&collector.logs);
37 }
38 }
39
40 pub fn into_print_traces(mut self) {
42 if let Some(a) = self.tracer.take() {
43 print_traces(a)
44 }
45 }
46
47 pub fn print_traces(&self) {
50 if let Some(a) = self.tracer.clone() {
51 print_traces(a)
52 }
53 }
54
55 pub fn with_tracing(mut self) -> Self {
57 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
58 self
59 }
60
61 pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self {
62 self.tracer = Some(TracingInspector::new(config));
63 self
64 }
65
66 pub fn with_steps_tracing(mut self) -> Self {
68 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
69 self
70 }
71
72 pub fn with_log_collector(mut self) -> Self {
74 self.log_collector = Some(Default::default());
75 self
76 }
77
78 pub fn with_trace_printer(mut self) -> Self {
80 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
81 self
82 }
83}
84
85fn print_traces(tracer: TracingInspector) {
93 let arena = tokio::task::block_in_place(move || {
94 tokio::runtime::Handle::current().block_on(async move {
95 let mut arena = tracer.into_traces();
96 let decoder = CallTraceDecoder::new();
97 decoder.populate_traces(arena.nodes_mut()).await;
98 arena
99 })
100 });
101
102 let traces = SparsedTraceArena { arena, ignored: Default::default() };
103 node_info!("Traces:");
104 node_info!("{}", render_trace_arena_inner(&traces, false, true));
105}
106
107impl<DB: Database> revm::Inspector<DB> for Inspector {
108 fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext<DB>) {
109 call_inspectors!([&mut self.tracer], |inspector| {
110 inspector.initialize_interp(interp, ecx);
111 });
112 }
113
114 fn step(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext<DB>) {
115 call_inspectors!([&mut self.tracer], |inspector| {
116 inspector.step(interp, ecx);
117 });
118 }
119
120 fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext<DB>) {
121 call_inspectors!([&mut self.tracer], |inspector| {
122 inspector.step_end(interp, ecx);
123 });
124 }
125
126 fn log(&mut self, interp: &mut Interpreter, ecx: &mut EvmContext<DB>, log: &Log) {
127 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
128 inspector.log(interp, ecx, log);
129 });
130 }
131
132 fn call(&mut self, ecx: &mut EvmContext<DB>, inputs: &mut CallInputs) -> Option<CallOutcome> {
133 call_inspectors!(
134 #[ret]
135 [&mut self.tracer, &mut self.log_collector],
136 |inspector| inspector.call(ecx, inputs).map(Some),
137 );
138 None
139 }
140
141 fn call_end(
142 &mut self,
143 ecx: &mut EvmContext<DB>,
144 inputs: &CallInputs,
145 outcome: CallOutcome,
146 ) -> CallOutcome {
147 if let Some(tracer) = &mut self.tracer {
148 return tracer.call_end(ecx, inputs, outcome);
149 }
150
151 outcome
152 }
153
154 fn create(
155 &mut self,
156 ecx: &mut EvmContext<DB>,
157 inputs: &mut CreateInputs,
158 ) -> Option<CreateOutcome> {
159 if let Some(tracer) = &mut self.tracer {
160 if let Some(out) = tracer.create(ecx, inputs) {
161 return Some(out);
162 }
163 }
164 None
165 }
166
167 fn create_end(
168 &mut self,
169 ecx: &mut EvmContext<DB>,
170 inputs: &CreateInputs,
171 outcome: CreateOutcome,
172 ) -> CreateOutcome {
173 if let Some(tracer) = &mut self.tracer {
174 return tracer.create_end(ecx, inputs, outcome);
175 }
176
177 outcome
178 }
179
180 #[inline]
181 fn eofcreate(
182 &mut self,
183 ecx: &mut EvmContext<DB>,
184 inputs: &mut EOFCreateInputs,
185 ) -> Option<CreateOutcome> {
186 if let Some(tracer) = &mut self.tracer {
187 if let Some(out) = tracer.eofcreate(ecx, inputs) {
188 return Some(out);
189 }
190 }
191 None
192 }
193
194 #[inline]
195 fn eofcreate_end(
196 &mut self,
197 ecx: &mut EvmContext<DB>,
198 inputs: &EOFCreateInputs,
199 outcome: CreateOutcome,
200 ) -> CreateOutcome {
201 if let Some(tracer) = &mut self.tracer {
202 return tracer.eofcreate_end(ecx, inputs, outcome);
203 }
204
205 outcome
206 }
207
208 #[inline]
209 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
210 if let Some(tracer) = &mut self.tracer {
211 revm::Inspector::<DB>::selfdestruct(tracer, contract, target, value);
212 }
213 }
214}
215
216pub fn print_logs(logs: &[Log]) {
218 for log in decode_console_logs(logs) {
219 tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
220 }
221}