anvil_zksync_traces/decode/
mod.rs

1////////////////////////////////////////////////////////////////////////////////////////////////////////////
2// Attribution: File adapted from the Foundry `evm` crate for ZKsync usage                                        //
3//                                                                                                        //
4// Full credit goes to its authors. See the original implementation here:                                 //
5// https://github.com/foundry-rs/foundry/blob/master/crates/evm/traces/src/decoder/mod.rs.                //
6//                                                                                                        //
7// Note: These methods are used under the terms of the original project's license.                        //
8////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
10use 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
28/// The first four bytes of the call data for a function call specifies the function to be called.
29pub const SELECTOR_LEN: usize = 4;
30
31/// A base struct for builders. If you want to start building from a predefined
32/// [CallTraceDecoder] instance, create your own builder like that:
33///
34/// ```rust,ignore
35/// pub struct CallTraceDecoderBuilder;
36/// impl CallTraceDecoderBuilder {
37/// #[inline]
38/// pub fn default() -> CallTraceDecoderBuilderBase {
39/// // Construct an instance here
40/// }
41/// }
42/// ```
43#[must_use = "builders do nothing unless you call `build` on them"]
44pub struct CallTraceDecoderBuilderBase {
45    decoder: CallTraceDecoder,
46}
47
48impl CallTraceDecoderBuilderBase {
49    /// Create a new builder.
50    #[inline]
51    pub fn new(starting_decoder_state: CallTraceDecoder) -> Self {
52        Self {
53            decoder: starting_decoder_state,
54        }
55    }
56
57    /// Add known labels to the decoder.
58    #[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    /// Sets the signature identifier for events and functions.
65    #[inline]
66    pub fn with_signature_identifier(mut self, identifier: SignaturesIdentifier) -> Self {
67        self.decoder.signature_identifier = Some(identifier);
68        self
69    }
70
71    /// Build the decoder.
72    #[inline]
73    pub fn build(self) -> CallTraceDecoder {
74        self.decoder
75    }
76}
77
78/// The call trace decoder.
79///
80/// The decoder collects address labels which it
81/// then uses to decode the call trace.
82#[derive(Clone, Debug, Default)]
83pub struct CallTraceDecoder {
84    /// Addresses identified to be a specific contract.
85    ///
86    /// The values are in the form `"<artifact>:<contract>"`.
87    pub contracts: HashMap<Address, String>,
88    /// Address labels.
89    pub labels: HashMap<Address, String>,
90    /// Contract addresses that have a receive function.
91    pub receive_contracts: Vec<Address>,
92    /// Contract addresses that have fallback functions, mapped to function sigs.
93    pub fallback_contracts: HashMap<Address, Vec<String>>,
94    /// All known events.
95    pub events: BTreeMap<(B256, usize), Vec<Event>>,
96    /// Revert decoder. Contains all known custom errors.
97    pub revert_decoder: RevertDecoder,
98    /// All known functions.
99    pub functions: HashMap<Selector, Vec<Function>>,
100    /// A signature identifier for events and functions.
101    pub signature_identifier: Option<SignaturesIdentifier>,
102}
103
104impl CallTraceDecoder {
105    /// Creates a new call trace decoder.
106    pub fn new(
107        functions: HashMap<Selector, Vec<Function>>,
108        events: BTreeMap<(B256, usize), Vec<Event>>,
109    ) -> Self {
110        // Add known addresses (system contracts, precompiles) to the labels
111        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    /// Populates the traces with decoded data by mutating the
129    /// [CallTrace] in place. See [CallTraceDecoder::decode_function] and
130    /// [CallTraceDecoder::decode_event] for more details.
131    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    /// Decodes a call trace.
141    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            // If traced contract is a fallback contract, check if it has the decoded function.
168            // If not, then replace call data signature with `fallback`.
169            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    /// Decodes a function's input into the given trace.
203    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    /// Decodes a function's output into the given trace.
224    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            // Functions coming from an external database do not have any outputs specified,
234            // and will lead to returning an empty list of values.
235            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    /// Decodes an event from ZKsync type VmEvent.
250    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                                // undo patched names
284                                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    /// Prefetches function and event signatures into the identifier cache
301    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        // Need to decode revert reasons and errors as well
326    }
327
328    /// The default decoded return data for a trace.
329    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            // Convert Word to Word32 (assuming proper conversion exists)
397            let word32 = Word32::from(word); // This assumes there's a conversion method
398            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
437/// Restore the order of the params of a decoded event,
438/// as Alloy returns the indexed and unindexed params separately.
439fn 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        // Prevent panic of event `Transfer(from, to)` decoded with a signature
445        // `Transfer(address indexed from, address indexed to, uint256 indexed tokenId)` by making
446        // sure the event inputs is not higher than decoded indexed / un-indexed values.
447        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
462/// Given an `Event` without indexed parameters and a `VmEvent`, it tries to
463/// return the `Event` with the proper indexed parameters. Otherwise,
464/// it returns the original `Event`.
465pub 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
489/// Converts a `VmEvent` to a `LogData`.
490pub 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}