anvil_zksync_traces/decode/
mod.rs1use crate::identifier::SignaturesIdentifier;
11use alloy::dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt};
12use alloy::json_abi::{Event, Function};
13use alloy::primitives::{LogData, Selector, Sign, B256};
14use anvil_zksync_common::address_map::{is_precompile, KNOWN_ADDRESSES};
15use anvil_zksync_types::numbers::SignedU256;
16use anvil_zksync_types::traces::{
17 CallTrace, CallTraceNode, DecodedCallData, DecodedCallEvent, DecodedCallTrace,
18 DecodedReturnData, DecodedRevertData, DecodedValue, LabeledAddress, Word32,
19};
20use itertools::Itertools;
21use std::collections::{BTreeMap, HashMap};
22use zksync_multivm::interface::VmEvent;
23use zksync_types::{Address, H160};
24
25pub mod revert_decoder;
26use revert_decoder::RevertDecoder;
27
28pub const SELECTOR_LEN: usize = 4;
30
31#[must_use = "builders do nothing unless you call `build` on them"]
44pub struct CallTraceDecoderBuilderBase {
45 decoder: CallTraceDecoder,
46}
47
48impl CallTraceDecoderBuilderBase {
49 #[inline]
51 pub fn new(starting_decoder_state: CallTraceDecoder) -> Self {
52 Self {
53 decoder: starting_decoder_state,
54 }
55 }
56
57 #[inline]
59 pub fn with_labels(mut self, labels: impl IntoIterator<Item = (Address, String)>) -> Self {
60 self.decoder.labels.extend(labels);
61 self
62 }
63
64 #[inline]
66 pub fn with_signature_identifier(mut self, identifier: SignaturesIdentifier) -> Self {
67 self.decoder.signature_identifier = Some(identifier);
68 self
69 }
70
71 #[inline]
73 pub fn build(self) -> CallTraceDecoder {
74 self.decoder
75 }
76}
77
78#[derive(Clone, Debug, Default)]
83pub struct CallTraceDecoder {
84 pub contracts: HashMap<Address, String>,
88 pub labels: HashMap<Address, String>,
90 pub receive_contracts: Vec<Address>,
92 pub fallback_contracts: HashMap<Address, Vec<String>>,
94 pub events: BTreeMap<(B256, usize), Vec<Event>>,
96 pub revert_decoder: RevertDecoder,
98 pub functions: HashMap<Selector, Vec<Function>>,
100 pub signature_identifier: Option<SignaturesIdentifier>,
102}
103
104impl CallTraceDecoder {
105 pub fn new(
107 functions: HashMap<Selector, Vec<Function>>,
108 events: BTreeMap<(B256, usize), Vec<Event>>,
109 ) -> Self {
110 let labels: HashMap<H160, String> = KNOWN_ADDRESSES
112 .iter()
113 .map(|(address, known_address)| (*address, known_address.name.clone()))
114 .collect();
115
116 Self {
117 contracts: Default::default(),
118 labels,
119 receive_contracts: Default::default(),
120 fallback_contracts: Default::default(),
121 functions,
122 events,
123 revert_decoder: Default::default(),
124 signature_identifier: None,
125 }
126 }
127
128 pub async fn populate_traces(&self, traces: &mut Vec<CallTraceNode>) {
132 for node in traces {
133 node.trace.decoded = self.decode_function(&node.trace).await;
134 for log in node.logs.iter_mut() {
135 log.decoded = self.decode_event(&log.raw_log).await;
136 }
137 }
138 }
139
140 pub async fn decode_function(&self, trace: &CallTrace) -> DecodedCallTrace {
142 let label = self.labels.get(&trace.address).cloned();
143 let cdata = &trace.call.input;
144
145 if !is_precompile(&trace.address) && cdata.len() >= SELECTOR_LEN {
146 let selector = &cdata[..SELECTOR_LEN];
147 let mut functions = Vec::new();
148 let functions = match self.functions.get(selector) {
149 Some(fs) => fs,
150 None => {
151 if let Some(identifier) = &self.signature_identifier {
152 if let Some(function) = identifier.identify_function(selector).await {
153 functions.push(function);
154 }
155 }
156 &functions
157 }
158 };
159 let [func, ..] = &functions[..] else {
160 return DecodedCallTrace {
161 label,
162 call_data: None,
163 return_data: self.default_return_data(trace),
164 };
165 };
166
167 let mut call_data = self.decode_function_input(trace, func);
170 if let Some(fallback_functions) = self.fallback_contracts.get(&trace.address) {
171 if !fallback_functions.contains(&func.signature()) {
172 call_data.signature = "fallback()".into();
173 }
174 }
175
176 DecodedCallTrace {
177 label,
178 call_data: Some(call_data),
179 return_data: self.decode_function_output(trace, functions),
180 }
181 } else {
182 let has_receive = self.receive_contracts.contains(&trace.address);
183 let signature = if cdata.is_empty() && has_receive {
184 "receive()"
185 } else {
186 "fallback()"
187 }
188 .into();
189 let args = if cdata.is_empty() {
190 Vec::new()
191 } else {
192 vec![DecodedValue::Bytes(cdata.clone())]
193 };
194 DecodedCallTrace {
195 label,
196 call_data: Some(DecodedCallData { signature, args }),
197 return_data: self.default_return_data(trace),
198 }
199 }
200 }
201
202 fn decode_function_input(&self, trace: &CallTrace, func: &Function) -> DecodedCallData {
204 let mut args = None;
205 if trace.call.input.len() >= SELECTOR_LEN && args.is_none() {
206 if let Ok(v) = func.abi_decode_input(&trace.call.input[SELECTOR_LEN..], false) {
207 args = Some(
208 v.into_iter()
209 .map(|value| -> DecodedValue {
210 let decoded = decode_value(value);
211 label_value(decoded, |value| self.labels.get(value).cloned())
212 })
213 .collect(),
214 );
215 }
216 }
217 DecodedCallData {
218 signature: func.signature(),
219 args: args.unwrap_or_default(),
220 }
221 }
222
223 fn decode_function_output(&self, trace: &CallTrace, funcs: &[Function]) -> DecodedReturnData {
225 if !trace.success {
226 return self.default_return_data(trace);
227 }
228
229 if let Some(values) = funcs
230 .iter()
231 .find_map(|func| func.abi_decode_output(&trace.call.output, false).ok())
232 {
233 if values.is_empty() {
236 return DecodedReturnData::NormalReturn(vec![]);
237 }
238
239 return DecodedReturnData::NormalReturn(
240 values
241 .into_iter()
242 .map(|value| self.decode_value(value))
243 .collect(),
244 );
245 }
246 DecodedReturnData::NormalReturn(vec![])
247 }
248
249 pub async fn decode_event(&self, vm_event: &VmEvent) -> DecodedCallEvent {
251 let Some(&t0) = vm_event.indexed_topics.first() else {
252 return DecodedCallEvent {
253 name: None,
254 params: None,
255 };
256 };
257
258 let mut events = Vec::new();
259 let b256_t0 = B256::from_slice(t0.as_bytes());
260 let key = (b256_t0, indexed_inputs_zksync(vm_event) - 1);
261 let events = match self.events.get(&key) {
262 Some(es) => es,
263 None => {
264 if let Some(identifier) = &self.signature_identifier {
265 if let Some(event) = identifier.identify_event(&t0[..]).await {
266 events.push(get_indexed_event_from_vm_event(event, vm_event));
267 }
268 }
269 &events
270 }
271 };
272 let log_data = vm_event_to_log_data(vm_event);
273 for event in events {
274 if let Ok(decoded) = event.decode_log(&log_data, false) {
275 let params = reconstruct_params(event, &decoded);
276 return DecodedCallEvent {
277 name: Some(event.name.clone()),
278 params: Some(
279 params
280 .into_iter()
281 .zip(event.inputs.iter())
282 .map(|(param, input)| -> (String, DecodedValue) {
283 let name: String = input.name.clone();
285 let value: DecodedValue = self.decode_value(param);
286 (name, value)
287 })
288 .collect(),
289 ),
290 };
291 }
292 }
293
294 DecodedCallEvent {
295 name: None,
296 params: None,
297 }
298 }
299
300 pub async fn prefetch_signatures(&self, nodes: &[CallTraceNode]) {
302 let Some(identifier) = &self.signature_identifier else {
303 return;
304 };
305
306 let events: Vec<_> = nodes
307 .iter()
308 .flat_map(|node| {
309 node.logs
310 .iter()
311 .filter_map(|log| log.raw_log.indexed_topics.first().cloned())
312 })
313 .unique()
314 .collect();
315 identifier.identify_events(events).await;
316
317 let funcs: Vec<_> = nodes
318 .iter()
319 .filter(|&t| !is_precompile(&t.trace.address))
320 .filter_map(|n| n.trace.call.input.get(..SELECTOR_LEN).map(|s| s.to_vec()))
321 .filter(|s| !self.functions.contains_key(s.as_slice()))
322 .collect();
323 identifier.identify_functions(funcs).await;
324
325 }
327
328 fn default_return_data(&self, trace: &CallTrace) -> DecodedReturnData {
330 if trace.success {
331 DecodedReturnData::NormalReturn(vec![])
332 } else {
333 DecodedReturnData::Revert(DecodedRevertData::Error(
334 self.revert_decoder.decode(&trace.call.output),
335 ))
336 }
337 }
338
339 fn decode_value(&self, value: DynSolValue) -> DecodedValue {
340 label_value(decode_value(value), |addr| self.labels.get(addr).cloned())
341 }
342}
343
344pub fn label_value(
345 value: DecodedValue,
346 labeler: impl Fn(&Address) -> Option<String>,
347) -> DecodedValue {
348 fn label_values(
349 vec: Vec<DecodedValue>,
350 labeler: &dyn Fn(&Address) -> Option<String>,
351 ) -> Vec<DecodedValue> {
352 vec.into_iter().map(|v| label_value(v, labeler)).collect()
353 }
354
355 match value {
356 DecodedValue::Address(LabeledAddress {
357 label: None,
358 address,
359 }) => {
360 let label = labeler(&address);
361 if let Some(label) = &label {
362 tracing::info!("Address {address:?} resolved to the label {label}.");
363 };
364 DecodedValue::Address(LabeledAddress { label, address })
365 }
366 DecodedValue::Array(vec) => DecodedValue::Array(label_values(vec, &labeler)),
367 DecodedValue::FixedArray(vec) => DecodedValue::FixedArray(label_values(vec, &labeler)),
368 DecodedValue::Tuple(vec) => DecodedValue::Tuple(label_values(vec, &labeler)),
369 DecodedValue::CustomStruct {
370 name,
371 prop_names,
372 tuple,
373 } => DecodedValue::CustomStruct {
374 name,
375 prop_names,
376 tuple: label_values(tuple, &labeler),
377 },
378 other => other,
379 }
380}
381
382pub fn decode_value(value: DynSolValue) -> DecodedValue {
383 match value {
384 DynSolValue::Bool(b) => DecodedValue::Bool(b),
385 DynSolValue::Int(i, _) => match i.into_sign_and_abs() {
386 (Sign::Positive, value) => {
387 DecodedValue::Int(zksync_types::U256(value.into_limbs()).into())
388 }
389 (Sign::Negative, value) => DecodedValue::Int(SignedU256 {
390 sign: anvil_zksync_types::numbers::Sign::Negative,
391 inner: zksync_types::U256(value.into_limbs()),
392 }),
393 },
394 DynSolValue::Uint(u, _) => DecodedValue::Uint(zksync_types::U256(u.into_limbs())),
395 DynSolValue::FixedBytes(word, size) => {
396 let word32 = Word32::from(word); DecodedValue::FixedBytes(word32, size)
399 }
400 DynSolValue::Address(addr) => {
401 let address = Address::from(addr.0 .0);
402 DecodedValue::Address(LabeledAddress {
403 label: None,
404 address,
405 })
406 }
407 DynSolValue::Function(func) => DecodedValue::Function(*func.0),
408 DynSolValue::Bytes(bytes) => DecodedValue::Bytes(bytes),
409 DynSolValue::String(s) => DecodedValue::String(s),
410 DynSolValue::Array(arr) => {
411 let decoded = arr.into_iter().map(decode_value).collect();
412 DecodedValue::Array(decoded)
413 }
414 DynSolValue::FixedArray(arr) => {
415 let decoded = arr.into_iter().map(decode_value).collect();
416 DecodedValue::FixedArray(decoded)
417 }
418 DynSolValue::Tuple(tup) => {
419 let decoded = tup.into_iter().map(decode_value).collect();
420 DecodedValue::Tuple(decoded)
421 }
422 DynSolValue::CustomStruct {
423 name,
424 prop_names,
425 tuple,
426 } => {
427 let decoded = tuple.into_iter().map(decode_value).collect();
428 DecodedValue::CustomStruct {
429 name,
430 prop_names,
431 tuple: decoded,
432 }
433 }
434 }
435}
436
437fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec<DynSolValue> {
440 let mut indexed = 0;
441 let mut unindexed = 0;
442 let mut inputs = vec![];
443 for input in event.inputs.iter() {
444 if input.indexed && indexed < decoded.indexed.len() {
448 inputs.push(decoded.indexed[indexed].clone());
449 indexed += 1;
450 } else if unindexed < decoded.body.len() {
451 inputs.push(decoded.body[unindexed].clone());
452 unindexed += 1;
453 }
454 }
455
456 inputs
457}
458fn indexed_inputs_zksync(event: &VmEvent) -> usize {
459 event.indexed_topics.len()
460}
461
462pub fn get_indexed_event_from_vm_event(mut event: Event, vm_event: &VmEvent) -> Event {
466 if !event.anonymous && vm_event.indexed_topics.len() > 1 {
467 let indexed_params = vm_event.indexed_topics.len() - 1;
468 let num_inputs = event.inputs.len();
469 let num_address_params = event.inputs.iter().filter(|p| p.ty == "address").count();
470
471 event
472 .inputs
473 .iter_mut()
474 .enumerate()
475 .for_each(|(index, param)| {
476 if param.name.is_empty() {
477 param.name = format!("param{index}");
478 }
479 if num_inputs == indexed_params
480 || (num_address_params == indexed_params && param.ty == "address")
481 {
482 param.indexed = true;
483 }
484 })
485 }
486 event
487}
488
489pub fn vm_event_to_log_data(event: &VmEvent) -> LogData {
491 LogData::new_unchecked(
492 event
493 .indexed_topics
494 .iter()
495 .map(|h| B256::from_slice(h.as_bytes()))
496 .collect(),
497 event.value.clone().into(),
498 )
499}