1use crate::{
2 CallTrace, CallTraceArena, CallTraceNode, DecodedCallData,
3 debug::DebugTraceIdentifier,
4 identifier::{IdentifiedAddress, LocalTraceIdentifier, SignaturesIdentifier, TraceIdentifier},
5};
6use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt, FunctionExt, JsonAbiExt};
7use alloy_json_abi::{Error, Event, Function, JsonAbi};
8use alloy_primitives::{
9 Address, B256, LogData, Selector,
10 map::{HashMap, HashSet, hash_map::Entry},
11};
12use foundry_common::{
13 ContractsByArtifact, SELECTOR_LEN, abi::get_indexed_event, fmt::format_token,
14 get_contract_name, selectors::SelectorKind,
15};
16use foundry_evm_core::{
17 abi::{Vm, console},
18 constants::{CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS},
19 decode::RevertDecoder,
20 precompiles::{
21 BLAKE_2F, BLS12_G1ADD, BLS12_G1MSM, BLS12_G2ADD, BLS12_G2MSM, BLS12_MAP_FP_TO_G1,
22 BLS12_MAP_FP2_TO_G2, BLS12_PAIRING_CHECK, EC_ADD, EC_MUL, EC_PAIRING, EC_RECOVER, IDENTITY,
23 MOD_EXP, P256_VERIFY, POINT_EVALUATION, RIPEMD_160, SHA_256,
24 },
25};
26use itertools::Itertools;
27use revm_inspectors::tracing::types::{DecodedCallLog, DecodedCallTrace};
28use std::{collections::BTreeMap, sync::OnceLock};
29use tempo_contracts::precompiles::{
30 IAccountKeychain, IFeeManager, IStablecoinDEX, ITIP20Factory, ITIP403Registry, IValidatorConfig,
31};
32use tempo_precompiles::{
33 ACCOUNT_KEYCHAIN_ADDRESS, NONCE_PRECOMPILE_ADDRESS, PATH_USD_ADDRESS, STABLECOIN_DEX_ADDRESS,
34 TIP_FEE_MANAGER_ADDRESS, TIP20_FACTORY_ADDRESS, TIP403_REGISTRY_ADDRESS,
35 VALIDATOR_CONFIG_ADDRESS, nonce::INonce, tip20::ITIP20,
36};
37
38mod precompiles;
39
40#[derive(Default)]
42#[must_use = "builders do nothing unless you call `build` on them"]
43pub struct CallTraceDecoderBuilder {
44 decoder: CallTraceDecoder,
45}
46
47impl CallTraceDecoderBuilder {
48 #[inline]
50 pub fn new() -> Self {
51 Self { decoder: CallTraceDecoder::new().clone() }
52 }
53
54 #[inline]
56 pub fn with_labels(mut self, labels: impl IntoIterator<Item = (Address, String)>) -> Self {
57 self.decoder.labels.extend(labels);
58 self
59 }
60
61 #[inline]
63 pub fn with_abi(mut self, abi: &JsonAbi) -> Self {
64 self.decoder.collect_abi(abi, None);
65 self
66 }
67
68 #[inline]
70 pub fn with_known_contracts(mut self, contracts: &ContractsByArtifact) -> Self {
71 trace!(target: "evm::traces", len=contracts.len(), "collecting known contract ABIs");
72 for contract in contracts.values() {
73 self.decoder.collect_abi(&contract.abi, None);
74 }
75 self
76 }
77
78 #[inline]
80 pub fn with_local_identifier_abis(self, identifier: &LocalTraceIdentifier<'_>) -> Self {
81 self.with_known_contracts(identifier.contracts())
82 }
83
84 #[inline]
86 pub const fn with_verbosity(mut self, level: u8) -> Self {
87 self.decoder.verbosity = level;
88 self
89 }
90
91 #[inline]
93 pub fn with_signature_identifier(mut self, identifier: SignaturesIdentifier) -> Self {
94 self.decoder.signature_identifier = Some(identifier);
95 self
96 }
97
98 #[inline]
100 pub const fn with_label_disabled(mut self, disable_alias: bool) -> Self {
101 self.decoder.disable_labels = disable_alias;
102 self
103 }
104
105 #[inline]
107 pub const fn with_chain_id(mut self, chain_id: Option<u64>) -> Self {
108 self.decoder.chain_id = chain_id;
109 self
110 }
111
112 #[inline]
114 pub fn with_debug_identifier(mut self, identifier: DebugTraceIdentifier) -> Self {
115 self.decoder.debug_identifier = Some(identifier);
116 self
117 }
118
119 #[inline]
121 pub fn build(self) -> CallTraceDecoder {
122 self.decoder
123 }
124}
125
126#[derive(Clone, Debug, Default)]
134pub struct CallTraceDecoder {
135 pub contracts: HashMap<Address, String>,
139 pub labels: HashMap<Address, String>,
141 pub receive_contracts: HashSet<Address>,
143 pub fallback_contracts: HashMap<Address, HashSet<Selector>>,
146 pub non_fallback_contracts: HashMap<Address, HashSet<Selector>>,
149
150 pub functions: HashMap<Selector, Vec<Function>>,
152 pub events: BTreeMap<(B256, usize), Vec<Event>>,
156 pub revert_decoder: RevertDecoder,
158
159 pub signature_identifier: Option<SignaturesIdentifier>,
161 pub verbosity: u8,
163
164 pub debug_identifier: Option<DebugTraceIdentifier>,
166
167 pub disable_labels: bool,
169
170 pub chain_id: Option<u64>,
172}
173
174impl CallTraceDecoder {
175 pub fn new() -> &'static Self {
180 static INIT: OnceLock<CallTraceDecoder> = OnceLock::new();
183 INIT.get_or_init(Self::init)
184 }
185
186 #[instrument(name = "CallTraceDecoder::init", level = "debug")]
187 fn init() -> Self {
188 Self {
189 contracts: Default::default(),
190 labels: HashMap::from_iter([
191 (CHEATCODE_ADDRESS, "VM".to_string()),
192 (HARDHAT_CONSOLE_ADDRESS, "console".to_string()),
193 (DEFAULT_CREATE2_DEPLOYER, "Create2Deployer".to_string()),
194 (CALLER, "DefaultSender".to_string()),
195 (EC_RECOVER, "ECRecover".to_string()),
196 (SHA_256, "SHA-256".to_string()),
197 (RIPEMD_160, "RIPEMD-160".to_string()),
198 (IDENTITY, "Identity".to_string()),
199 (MOD_EXP, "ModExp".to_string()),
200 (EC_ADD, "ECAdd".to_string()),
201 (EC_MUL, "ECMul".to_string()),
202 (EC_PAIRING, "ECPairing".to_string()),
203 (BLAKE_2F, "Blake2F".to_string()),
204 (POINT_EVALUATION, "PointEvaluation".to_string()),
205 (BLS12_G1ADD, "BLS12_G1ADD".to_string()),
206 (BLS12_G1MSM, "BLS12_G1MSM".to_string()),
207 (BLS12_G2ADD, "BLS12_G2ADD".to_string()),
208 (BLS12_G2MSM, "BLS12_G2MSM".to_string()),
209 (BLS12_PAIRING_CHECK, "BLS12_PAIRING_CHECK".to_string()),
210 (BLS12_MAP_FP_TO_G1, "BLS12_MAP_FP_TO_G1".to_string()),
211 (BLS12_MAP_FP2_TO_G2, "BLS12_MAP_FP2_TO_G2".to_string()),
212 (P256_VERIFY, "P256VERIFY".to_string()),
213 (TIP_FEE_MANAGER_ADDRESS, "FeeManager".to_string()),
215 (TIP403_REGISTRY_ADDRESS, "TIP403Registry".to_string()),
216 (TIP20_FACTORY_ADDRESS, "TIP20Factory".to_string()),
217 (STABLECOIN_DEX_ADDRESS, "StablecoinDex".to_string()),
218 (NONCE_PRECOMPILE_ADDRESS, "Nonce".to_string()),
219 (VALIDATOR_CONFIG_ADDRESS, "ValidatorConfig".to_string()),
220 (ACCOUNT_KEYCHAIN_ADDRESS, "AccountKeychain".to_string()),
221 (PATH_USD_ADDRESS, "PathUSD".to_string()),
222 ]),
223 receive_contracts: Default::default(),
224 fallback_contracts: Default::default(),
225 non_fallback_contracts: Default::default(),
226
227 functions: console::hh::abi::functions()
228 .into_values()
229 .chain(Vm::abi::functions().into_values())
230 .chain(IFeeManager::abi::functions().into_values())
232 .chain(ITIP20::abi::functions().into_values())
233 .chain(ITIP403Registry::abi::functions().into_values())
234 .chain(ITIP20Factory::abi::functions().into_values())
235 .chain(IStablecoinDEX::abi::functions().into_values())
236 .chain(INonce::abi::functions().into_values())
237 .chain(IValidatorConfig::abi::functions().into_values())
238 .chain(IAccountKeychain::abi::functions().into_values())
239 .flatten()
240 .map(|func| (func.selector(), vec![func]))
241 .collect(),
242 events: console::ds::abi::events()
243 .into_values()
244 .chain(IFeeManager::abi::events().into_values())
246 .chain(ITIP20::abi::events().into_values())
247 .chain(ITIP403Registry::abi::events().into_values())
248 .chain(ITIP20Factory::abi::events().into_values())
249 .chain(IStablecoinDEX::abi::events().into_values())
250 .chain(INonce::abi::events().into_values())
251 .chain(IValidatorConfig::abi::events().into_values())
252 .chain(IAccountKeychain::abi::events().into_values())
253 .flatten()
254 .map(|event| ((event.selector(), indexed_inputs(&event)), vec![event]))
255 .collect(),
256 revert_decoder: Default::default(),
257
258 signature_identifier: None,
259 verbosity: 0,
260
261 debug_identifier: None,
262
263 disable_labels: false,
264
265 chain_id: None,
266 }
267 }
268
269 pub fn clear_addresses(&mut self) {
271 self.contracts.clear();
272
273 let default_labels = &Self::new().labels;
274 if self.labels.len() > default_labels.len() {
275 self.labels.clone_from(default_labels);
276 }
277
278 self.receive_contracts.clear();
279 self.fallback_contracts.clear();
280 self.non_fallback_contracts.clear();
281 }
282
283 pub fn identify(&mut self, arena: &CallTraceArena, identifier: &mut impl TraceIdentifier) {
287 self.collect_identified_addresses(self.identify_addresses(arena, identifier));
288 }
289
290 pub fn identify_addresses<'a>(
294 &self,
295 arena: &CallTraceArena,
296 identifier: &'a mut impl TraceIdentifier,
297 ) -> Vec<IdentifiedAddress<'a>> {
298 let nodes = arena.nodes().iter().filter(|node| {
299 if node.is_precompile()
301 || precompiles::is_known_precompile(node.trace.address, self.chain_id)
302 {
303 return false;
304 }
305 let address = &node.trace.address;
306 !self.labels.contains_key(address) || !self.contracts.contains_key(address)
307 });
308 identifier.identify_addresses(&nodes.collect::<Vec<_>>())
309 }
310
311 pub fn push_event(&mut self, event: Event) {
313 self.events.entry((event.selector(), indexed_inputs(&event))).or_default().push(event);
314 }
315
316 pub fn push_function(&mut self, function: Function) {
318 match self.functions.entry(function.selector()) {
319 Entry::Occupied(entry) => {
320 if entry.get().contains(&function) {
322 return;
323 }
324 trace!(target: "evm::traces", selector=%entry.key(), new=%function.signature(), "duplicate function selector");
325 entry.into_mut().push(function);
326 }
327 Entry::Vacant(entry) => {
328 entry.insert(vec![function]);
329 }
330 }
331 }
332
333 fn select_contract_function<'a>(
337 &self,
338 functions: &'a [Function],
339 trace: &CallTrace,
340 ) -> &'a [Function] {
341 if functions.len() > 1 {
345 for (i, func) in functions.iter().enumerate() {
346 if trace.data.len() >= SELECTOR_LEN
347 && func.abi_decode_input(&trace.data[SELECTOR_LEN..]).is_ok()
348 {
349 return &functions[i..i + 1];
350 }
351 }
352 }
353 functions
354 }
355
356 pub fn push_error(&mut self, error: Error) {
358 self.revert_decoder.push_error(error);
359 }
360
361 pub const fn without_label(&mut self, disable: bool) {
362 self.disable_labels = disable;
363 }
364
365 fn collect_identified_addresses(&mut self, mut addrs: Vec<IdentifiedAddress<'_>>) {
366 addrs.sort_by_key(|identity| identity.address);
367 addrs.dedup_by_key(|identity| identity.address);
368 if addrs.is_empty() {
369 return;
370 }
371
372 trace!(target: "evm::traces", len=addrs.len(), "collecting address identities");
373 for IdentifiedAddress { address, label, contract, abi, artifact_id: _ } in addrs {
374 let _span = trace_span!(target: "evm::traces", "identity", ?contract, ?label).entered();
375
376 if let Some(contract) = contract {
377 self.contracts.entry(address).or_insert(contract);
378 }
379
380 if let Some(label) = label.filter(|s| !s.is_empty()) {
381 self.labels.entry(address).or_insert(label);
382 }
383
384 if let Some(abi) = abi {
385 self.collect_abi(&abi, Some(address));
386 }
387 }
388 }
389
390 fn collect_abi(&mut self, abi: &JsonAbi, address: Option<Address>) {
391 let len = abi.len();
392 if len == 0 {
393 return;
394 }
395 trace!(target: "evm::traces", len, ?address, "collecting ABI");
396 for function in abi.functions() {
397 self.push_function(function.clone());
398 }
399 for event in abi.events() {
400 self.push_event(event.clone());
401 }
402 for error in abi.errors() {
403 self.push_error(error.clone());
404 }
405 if let Some(address) = address {
406 if abi.receive.is_some() {
407 self.receive_contracts.insert(address);
408 }
409
410 if abi.fallback.is_some() {
411 self.fallback_contracts
412 .insert(address, abi.functions().map(|f| f.selector()).collect());
413 } else {
414 self.non_fallback_contracts
415 .insert(address, abi.functions().map(|f| f.selector()).collect());
416 }
417 }
418 }
419
420 pub async fn populate_traces(&self, traces: &mut Vec<CallTraceNode>) {
424 for node in traces {
425 node.trace.decoded = Some(Box::new(self.decode_function(&node.trace).await));
426 for log in &mut node.logs {
427 log.decoded = Some(Box::new(self.decode_event(&log.raw_log).await));
428 }
429
430 if let Some(debug) = self.debug_identifier.as_ref()
431 && let Some(identified) = self.contracts.get(&node.trace.address)
432 {
433 debug.identify_node_steps(node, get_contract_name(identified))
434 }
435 }
436 }
437
438 pub async fn decode_function(&self, trace: &CallTrace) -> DecodedCallTrace {
440 let label =
441 if self.disable_labels { None } else { self.labels.get(&trace.address).cloned() };
442
443 if trace.kind.is_any_create() {
444 return DecodedCallTrace { label, ..Default::default() };
445 }
446
447 if let Some(trace) = precompiles::decode(trace, self.chain_id) {
448 return trace;
449 }
450
451 let cdata = &trace.data;
452 if trace.address == DEFAULT_CREATE2_DEPLOYER {
453 return DecodedCallTrace {
454 label,
455 call_data: Some(DecodedCallData { signature: "create2".to_string(), args: vec![] }),
456 return_data: self.default_return_data(trace),
457 };
458 }
459
460 if is_abi_call_data(cdata) {
461 let selector = Selector::try_from(&cdata[..SELECTOR_LEN]).unwrap();
462 let mut functions = Vec::new();
463 let functions = match self.functions.get(&selector) {
464 Some(fs) => fs,
465 None => {
466 if let Some(identifier) = &self.signature_identifier
467 && let Some(function) = identifier.identify_function(selector).await
468 {
469 functions.push(function);
470 }
471 &functions
472 }
473 };
474
475 if let Some(contract_selectors) = self.non_fallback_contracts.get(&trace.address)
478 && !contract_selectors.contains(&selector)
479 && (!cdata.is_empty() || !self.receive_contracts.contains(&trace.address))
480 {
481 let return_data = if trace.success {
482 None
483 } else {
484 let revert_msg = self.revert_decoder.decode(&trace.output, trace.status);
485
486 if trace.output.is_empty() || revert_msg.contains("EvmError: Revert") {
487 Some(format!(
488 "unrecognized function selector {} for contract {}, which has no fallback function.",
489 selector, trace.address
490 ))
491 } else {
492 Some(revert_msg)
493 }
494 };
495
496 return if let Some(func) = functions.first() {
497 DecodedCallTrace {
498 label,
499 call_data: Some(self.decode_function_input(trace, func)),
500 return_data,
501 }
502 } else {
503 DecodedCallTrace {
504 label,
505 call_data: self.fallback_call_data(trace),
506 return_data,
507 }
508 };
509 }
510
511 let contract_functions = self.select_contract_function(functions, trace);
512 let [func, ..] = contract_functions else {
513 return DecodedCallTrace {
514 label,
515 call_data: self.fallback_call_data(trace),
516 return_data: self.default_return_data(trace),
517 };
518 };
519
520 let mut call_data = self.decode_function_input(trace, func);
523 if let Some(fallback_functions) = self.fallback_contracts.get(&trace.address)
524 && !fallback_functions.contains(&selector)
525 && let Some(cd) = self.fallback_call_data(trace)
526 {
527 call_data.signature = cd.signature;
528 }
529
530 DecodedCallTrace {
531 label,
532 call_data: Some(call_data),
533 return_data: self.decode_function_output(trace, contract_functions),
534 }
535 } else {
536 DecodedCallTrace {
537 label,
538 call_data: self.fallback_call_data(trace),
539 return_data: self.default_return_data(trace),
540 }
541 }
542 }
543
544 fn decode_function_input(&self, trace: &CallTrace, func: &Function) -> DecodedCallData {
546 let mut args = None;
547 if trace.data.len() >= SELECTOR_LEN {
548 if trace.address == CHEATCODE_ADDRESS {
549 if let Some(v) = self.decode_cheatcode_inputs(func, &trace.data) {
551 args = Some(v);
552 }
553 }
554
555 if args.is_none()
556 && let Ok(v) = func.abi_decode_input(&trace.data[SELECTOR_LEN..])
557 {
558 args = Some(v.iter().map(|value| self.format_value(value)).collect());
559 }
560 }
561
562 DecodedCallData { signature: func.signature(), args: args.unwrap_or_default() }
563 }
564
565 fn decode_cheatcode_inputs(&self, func: &Function, data: &[u8]) -> Option<Vec<String>> {
567 match func.name.as_str() {
568 "expectRevert" => {
569 let decoded = match data.get(SELECTOR_LEN..) {
570 Some(data) => func.abi_decode_input(data).ok(),
571 None => None,
572 };
573 let Some(decoded) = decoded else {
574 return Some(vec![self.revert_decoder.decode(data, None)]);
575 };
576 let Some(first) = decoded.first() else {
577 return Some(vec![self.revert_decoder.decode(data, None)]);
578 };
579 let expected_revert = match first {
580 DynSolValue::Bytes(bytes) => bytes.as_slice(),
581 DynSolValue::FixedBytes(word, size) => &word[..*size],
582 _ => return None,
583 };
584 Some(
585 std::iter::once(self.revert_decoder.decode(expected_revert, None))
586 .chain(decoded.iter().skip(1).map(|value| self.format_value(value)))
587 .collect(),
588 )
589 }
590 "addr" | "createWallet" | "deriveKey" | "rememberKey" => {
591 Some(vec!["<pk>".to_string()])
593 }
594 "broadcast" | "startBroadcast" => {
595 (!func.inputs.is_empty() && func.inputs[0].ty == "uint256").then(|| vec!["<pk>".to_string()])
598 }
599 "getNonce" => {
600 (!func.inputs.is_empty() && func.inputs[0].ty == "tuple").then(|| vec!["<pk>".to_string()])
603 }
604 "sign" | "signP256" => {
605 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
606
607 if !decoded.is_empty() &&
610 (func.inputs[0].ty == "uint256" || func.inputs[0].ty == "tuple")
611 {
612 decoded[0] = DynSolValue::String("<pk>".to_string());
613 }
614
615 Some(decoded.iter().map(format_token).collect())
616 }
617 "signDelegation" | "signAndAttachDelegation" => {
618 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
619 decoded[1] = DynSolValue::String("<pk>".to_string());
623 Some(decoded.iter().map(format_token).collect())
624 }
625 "parseJson" |
626 "parseJsonUint" |
627 "parseJsonUintArray" |
628 "parseJsonInt" |
629 "parseJsonIntArray" |
630 "parseJsonString" |
631 "parseJsonStringArray" |
632 "parseJsonAddress" |
633 "parseJsonAddressArray" |
634 "parseJsonBool" |
635 "parseJsonBoolArray" |
636 "parseJsonBytes" |
637 "parseJsonBytesArray" |
638 "parseJsonBytes32" |
639 "parseJsonBytes32Array" |
640 "writeJson" |
641 "keyExists" |
643 "keyExistsJson" |
644 "serializeBool" |
645 "serializeUint" |
646 "serializeUintToHex" |
647 "serializeInt" |
648 "serializeAddress" |
649 "serializeBytes32" |
650 "serializeString" |
651 "serializeBytes" => {
652 if self.verbosity >= 5 {
653 None
654 } else {
655 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
656 let token = if func.name.as_str() == "parseJson" ||
657 func.name.as_str() == "keyExists" ||
659 func.name.as_str() == "keyExistsJson"
660 {
661 "<JSON file>"
662 } else {
663 "<stringified JSON>"
664 };
665 decoded[0] = DynSolValue::String(token.to_string());
666 Some(decoded.iter().map(format_token).collect())
667 }
668 }
669 s if s.contains("Toml") => {
670 if self.verbosity >= 5 {
671 None
672 } else {
673 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
674 let token = if func.name.as_str() == "parseToml" ||
675 func.name.as_str() == "keyExistsToml"
676 {
677 "<TOML file>"
678 } else {
679 "<stringified TOML>"
680 };
681 decoded[0] = DynSolValue::String(token.to_string());
682 Some(decoded.iter().map(format_token).collect())
683 }
684 }
685 "createFork" |
686 "createSelectFork" |
687 "rpc" => {
688 let mut decoded = func.abi_decode_input(&data[SELECTOR_LEN..]).ok()?;
689
690 if !decoded.is_empty() && func.inputs[0].ty == "string" {
692 let url_or_alias = decoded[0].as_str().unwrap_or_default();
693
694 if url_or_alias.starts_with("http") || url_or_alias.starts_with("ws") {
695 decoded[0] = DynSolValue::String("<rpc url>".to_string());
696 }
697 } else {
698 return None;
699 }
700
701 Some(decoded.iter().map(format_token).collect())
702 }
703 _ => None,
704 }
705 }
706
707 fn decode_function_output(&self, trace: &CallTrace, funcs: &[Function]) -> Option<String> {
709 if !trace.success {
710 return self.default_return_data(trace);
711 }
712
713 if trace.address == CHEATCODE_ADDRESS
714 && let Some(decoded) = funcs.iter().find_map(|func| self.decode_cheatcode_outputs(func))
715 {
716 return Some(decoded);
717 }
718
719 if let Some(values) =
720 funcs.iter().find_map(|func| func.abi_decode_output(&trace.output).ok())
721 {
722 if values.is_empty() {
725 return None;
726 }
727
728 return Some(
729 values.iter().map(|value| self.format_value(value)).format(", ").to_string(),
730 );
731 }
732
733 None
734 }
735
736 fn decode_cheatcode_outputs(&self, func: &Function) -> Option<String> {
738 match func.name.as_str() {
739 s if s.starts_with("env") => Some("<env var value>"),
740 "createWallet" | "deriveKey" => Some("<pk>"),
741 "promptSecret" | "promptSecretUint" => Some("<secret>"),
742 "parseJson" if self.verbosity < 5 => Some("<encoded JSON value>"),
743 "readFile" if self.verbosity < 5 => Some("<file>"),
744 "rpcUrl" | "rpcUrls" | "rpcUrlStructs" => Some("<rpc url>"),
745 _ => None,
746 }
747 .map(Into::into)
748 }
749
750 #[track_caller]
751 fn fallback_call_data(&self, trace: &CallTrace) -> Option<DecodedCallData> {
752 let cdata = &trace.data;
753 let signature = if cdata.is_empty() && self.receive_contracts.contains(&trace.address) {
754 "receive()"
755 } else if self.fallback_contracts.contains_key(&trace.address) {
756 "fallback()"
757 } else {
758 return None;
759 }
760 .to_string();
761 let args = if cdata.is_empty() { Vec::new() } else { vec![cdata.to_string()] };
762 Some(DecodedCallData { signature, args })
763 }
764
765 fn default_return_data(&self, trace: &CallTrace) -> Option<String> {
767 if trace.status.is_none() || trace.status.is_some_and(|s| s.is_ok()) {
772 return None;
773 }
774 (!trace.success).then(|| self.revert_decoder.decode(&trace.output, trace.status))
775 }
776
777 pub async fn decode_event(&self, log: &LogData) -> DecodedCallLog {
779 let &[t0, ..] = log.topics() else { return DecodedCallLog { name: None, params: None } };
780
781 let mut events = Vec::new();
782 let events = match self.events.get(&(t0, log.topics().len() - 1)) {
783 Some(es) => es,
784 None => {
785 if let Some(identifier) = &self.signature_identifier
786 && let Some(event) = identifier.identify_event(t0).await
787 {
788 events.push(get_indexed_event(event, log));
789 }
790 &events
791 }
792 };
793 for event in events {
794 if let Ok(decoded) = event.decode_log(log) {
795 let params = reconstruct_params(event, &decoded);
796 return DecodedCallLog {
797 name: Some(event.name.clone()),
798 params: Some(
799 params
800 .into_iter()
801 .zip(event.inputs.iter())
802 .map(|(param, input)| {
803 let name = input.name.clone();
805 (name, self.format_value(¶m))
806 })
807 .collect(),
808 ),
809 };
810 }
811 }
812
813 DecodedCallLog { name: None, params: None }
814 }
815
816 pub async fn prefetch_signatures(&self, nodes: &[CallTraceNode]) {
818 let Some(identifier) = &self.signature_identifier else { return };
819 let events = nodes
820 .iter()
821 .flat_map(|node| {
822 node.logs
823 .iter()
824 .map(|log| log.raw_log.topics())
825 .filter(|&topics| {
826 if let Some(&first) = topics.first()
827 && self.events.contains_key(&(first, topics.len() - 1))
828 {
829 return false;
830 }
831 true
832 })
833 .filter_map(|topics| topics.first())
834 })
835 .copied();
836 let functions = nodes
837 .iter()
838 .filter(|&n| {
839 if n.trace.address == DEFAULT_CREATE2_DEPLOYER
841 || n.is_precompile()
842 || precompiles::is_known_precompile(n.trace.address, self.chain_id)
843 {
844 return false;
845 }
846 if n.trace.kind.is_any_create() || !is_abi_call_data(&n.trace.data) {
848 return false;
849 }
850 true
851 })
852 .filter_map(|n| n.trace.data.first_chunk().map(Selector::from))
853 .filter(|selector| !self.functions.contains_key(selector));
854 let selectors = events
855 .map(SelectorKind::Event)
856 .chain(functions.map(SelectorKind::Function))
857 .unique()
858 .collect::<Vec<_>>();
859 let _ = identifier.identify(&selectors).await;
860 }
861
862 fn format_value(&self, value: &DynSolValue) -> String {
864 if let DynSolValue::Address(addr) = value
865 && let Some(label) = self.labels.get(addr)
866 {
867 return format!("{label}: [{addr}]");
868 }
869 format_token(value)
870 }
871}
872
873fn is_abi_call_data(data: &[u8]) -> bool {
877 match data.len().cmp(&SELECTOR_LEN) {
878 std::cmp::Ordering::Less => false,
879 std::cmp::Ordering::Equal => true,
880 std::cmp::Ordering::Greater => is_abi_data(&data[SELECTOR_LEN..]),
881 }
882}
883
884fn is_abi_data(data: &[u8]) -> bool {
888 let rem = data.len() % 32;
889 if rem == 0 || data.is_empty() {
890 return true;
891 }
892 data[data.len() - rem..].iter().all(|byte| *byte == 0)
894}
895
896fn reconstruct_params(event: &Event, decoded: &DecodedEvent) -> Vec<DynSolValue> {
899 let mut indexed = 0;
900 let mut unindexed = 0;
901 let mut inputs = vec![];
902 for input in &event.inputs {
903 if input.indexed && indexed < decoded.indexed.len() {
907 inputs.push(decoded.indexed[indexed].clone());
908 indexed += 1;
909 } else if unindexed < decoded.body.len() {
910 inputs.push(decoded.body[unindexed].clone());
911 unindexed += 1;
912 }
913 }
914
915 inputs
916}
917
918fn indexed_inputs(event: &Event) -> usize {
919 event.inputs.iter().filter(|param| param.indexed).count()
920}
921
922#[cfg(test)]
923mod tests {
924 use super::*;
925 use alloy_primitives::hex;
926
927 #[test]
928 fn test_selector_collision_resolution() {
929 use alloy_json_abi::Function;
930 use alloy_primitives::Address;
931
932 let func1 = Function::parse("transferFrom(address,address,uint256)").unwrap();
934 let func2 = Function::parse("gasprice_bit_ether(int128)").unwrap();
935
936 assert_eq!(func1.selector(), func2.selector());
938
939 let functions = vec![func1, func2];
940
941 let trace = CallTrace {
943 address: Address::from([0x12; 20]),
944 data: hex!("23b872dd000000000000000000000000000000000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000064").to_vec().into(),
945 ..Default::default()
946 };
947
948 let decoder = CallTraceDecoder::new();
949 let result = decoder.select_contract_function(&functions, &trace);
950
951 assert_eq!(result.len(), 1);
953 assert_eq!(result[0].signature(), "transferFrom(address,address,uint256)");
954 }
955
956 #[test]
957 fn test_selector_collision_resolution_second_function() {
958 use alloy_json_abi::Function;
959 use alloy_primitives::Address;
960
961 let func1 = Function::parse("transferFrom(address,address,uint256)").unwrap();
963 let func2 = Function::parse("gasprice_bit_ether(int128)").unwrap();
964
965 let functions = vec![func1, func2];
966
967 let trace = CallTrace {
969 address: Address::from([0x12; 20]),
970 data: hex!("23b872dd0000000000000000000000000000000000000000000000000000000000000064")
971 .to_vec()
972 .into(),
973 ..Default::default()
974 };
975
976 let decoder = CallTraceDecoder::new();
977 let result = decoder.select_contract_function(&functions, &trace);
978
979 assert_eq!(result.len(), 1);
981 assert_eq!(result[0].signature(), "gasprice_bit_ether(int128)");
982 }
983
984 #[test]
985 fn test_should_redact() {
986 let decoder = CallTraceDecoder::new();
987
988 let expected_revert_bytes4 = vec![0xde, 0xad, 0xbe, 0xef];
989 let expect_revert_bytes4_data = Function::parse("expectRevert(bytes4)")
990 .unwrap()
991 .abi_encode_input(&[DynSolValue::FixedBytes(
992 B256::right_padding_from(expected_revert_bytes4.as_slice()),
993 4,
994 )])
995 .unwrap();
996
997 let expected_revert_bytes = hex!(
998 "08c379a000000000000000000000000000000000000000000000000000000000\
999 0000002000000000000000000000000000000000000000000000000000000000\
1000 00000004626f6f6d000000000000000000000000000000000000000000000000"
1001 )
1002 .to_vec();
1003 let expect_revert_bytes_data = Function::parse("expectRevert(bytes)")
1004 .unwrap()
1005 .abi_encode_input(&[DynSolValue::Bytes(expected_revert_bytes.clone())])
1006 .unwrap();
1007
1008 let reverter = Address::from([0x11; 20]);
1009 let expect_revert_bytes4_address_data = Function::parse("expectRevert(bytes4,address)")
1010 .unwrap()
1011 .abi_encode_input(&[
1012 DynSolValue::FixedBytes(
1013 B256::right_padding_from(expected_revert_bytes4.as_slice()),
1014 4,
1015 ),
1016 DynSolValue::Address(reverter),
1017 ])
1018 .unwrap();
1019
1020 let count = 42_u64;
1021 let expect_revert_bytes_count_data = Function::parse("expectRevert(bytes,uint64)")
1022 .unwrap()
1023 .abi_encode_input(&[
1024 DynSolValue::Bytes(expected_revert_bytes.clone()),
1025 DynSolValue::Uint(alloy_primitives::U256::from(count), 64),
1026 ])
1027 .unwrap();
1028
1029 let expect_revert_bytes_address_count_data =
1030 Function::parse("expectRevert(bytes,address,uint64)")
1031 .unwrap()
1032 .abi_encode_input(&[
1033 DynSolValue::Bytes(expected_revert_bytes.clone()),
1034 DynSolValue::Address(reverter),
1035 DynSolValue::Uint(alloy_primitives::U256::from(count), 64),
1036 ])
1037 .unwrap();
1038
1039 let expect_revert_runtime_data = expected_revert_bytes4.clone();
1040
1041 let cheatcode_input_test_cases = vec![
1043 (
1045 "expectRevert(bytes4)",
1046 expect_revert_bytes4_data,
1047 Some(vec![decoder.revert_decoder.decode(expected_revert_bytes4.as_slice(), None)]),
1048 ),
1049 (
1050 "expectRevert(bytes)",
1051 expect_revert_bytes_data,
1052 Some(vec![decoder.revert_decoder.decode(expected_revert_bytes.as_slice(), None)]),
1053 ),
1054 (
1055 "expectRevert(bytes4)",
1056 expect_revert_runtime_data.clone(),
1057 Some(vec![
1058 decoder.revert_decoder.decode(expect_revert_runtime_data.as_slice(), None),
1059 ]),
1060 ),
1061 (
1062 "expectRevert(bytes4,address)",
1063 expect_revert_bytes4_address_data,
1064 Some(vec![
1065 decoder.revert_decoder.decode(expected_revert_bytes4.as_slice(), None),
1066 decoder.format_value(&DynSolValue::Address(reverter)),
1067 ]),
1068 ),
1069 (
1070 "expectRevert(bytes,uint64)",
1071 expect_revert_bytes_count_data,
1072 Some(vec![
1073 decoder.revert_decoder.decode(expected_revert_bytes.as_slice(), None),
1074 decoder
1075 .format_value(&DynSolValue::Uint(alloy_primitives::U256::from(count), 64)),
1076 ]),
1077 ),
1078 (
1079 "expectRevert(bytes,address,uint64)",
1080 expect_revert_bytes_address_count_data,
1081 Some(vec![
1082 decoder.revert_decoder.decode(expected_revert_bytes.as_slice(), None),
1083 decoder.format_value(&DynSolValue::Address(reverter)),
1084 decoder
1085 .format_value(&DynSolValue::Uint(alloy_primitives::U256::from(count), 64)),
1086 ]),
1087 ),
1088 (
1089 "expectRevert()",
1090 expect_revert_runtime_data.clone(),
1091 Some(vec![
1092 decoder.revert_decoder.decode(expect_revert_runtime_data.as_slice(), None),
1093 ]),
1094 ),
1095 ("addr(uint256)", vec![], Some(vec!["<pk>".to_string()])),
1097 ("createWallet(string)", vec![], Some(vec!["<pk>".to_string()])),
1098 ("createWallet(uint256)", vec![], Some(vec!["<pk>".to_string()])),
1099 ("deriveKey(string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
1100 ("deriveKey(string,string,uint32)", vec![], Some(vec!["<pk>".to_string()])),
1101 ("deriveKey(string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
1102 ("deriveKey(string,string,uint32,string)", vec![], Some(vec!["<pk>".to_string()])),
1103 ("rememberKey(uint256)", vec![], Some(vec!["<pk>".to_string()])),
1104 ("broadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
1107 ("broadcast()", vec![], None), ("startBroadcast(uint256)", vec![], Some(vec!["<pk>".to_string()])),
1109 ("startBroadcast()", vec![], None), ("getNonce((address,uint256,uint256,uint256))", vec![], Some(vec!["<pk>".to_string()])),
1111 ("getNonce(address)", vec![], None), (
1115 "sign(uint256,bytes32)",
1116 hex!(
1117 "
1118 e341eaa4
1119 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
1120 0000000000000000000000000000000000000000000000000000000000000000
1121 "
1122 )
1123 .to_vec(),
1124 Some(vec![
1125 "\"<pk>\"".to_string(),
1126 "0x0000000000000000000000000000000000000000000000000000000000000000"
1127 .to_string(),
1128 ]),
1129 ),
1130 (
1131 "signP256(uint256,bytes32)",
1132 hex!(
1133 "
1134 83211b40
1135 7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
1136 0000000000000000000000000000000000000000000000000000000000000000
1137 "
1138 )
1139 .to_vec(),
1140 Some(vec![
1141 "\"<pk>\"".to_string(),
1142 "0x0000000000000000000000000000000000000000000000000000000000000000"
1143 .to_string(),
1144 ]),
1145 ),
1146 (
1147 "createFork(string)",
1149 hex!(
1150 "
1151 31ba3498
1152 0000000000000000000000000000000000000000000000000000000000000020
1153 000000000000000000000000000000000000000000000000000000000000002c
1154 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1155 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1156 "
1157 )
1158 .to_vec(),
1159 Some(vec!["\"<rpc url>\"".to_string()]),
1160 ),
1161 (
1162 "createFork(string)",
1164 hex!(
1165 "
1166 31ba3498
1167 0000000000000000000000000000000000000000000000000000000000000020
1168 000000000000000000000000000000000000000000000000000000000000002a
1169 7773733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f6d2f
1170 76322f6170695f6b657900000000000000000000000000000000000000000000
1171 "
1172 )
1173 .to_vec(),
1174 Some(vec!["\"<rpc url>\"".to_string()]),
1175 ),
1176 (
1177 "createFork(string)",
1179 hex!(
1180 "
1181 31ba3498
1182 0000000000000000000000000000000000000000000000000000000000000020
1183 0000000000000000000000000000000000000000000000000000000000000007
1184 6d61696e6e657400000000000000000000000000000000000000000000000000
1185 "
1186 )
1187 .to_vec(),
1188 Some(vec!["\"mainnet\"".to_string()]),
1189 ),
1190 (
1191 "createFork(string,uint256)",
1193 hex!(
1194 "
1195 6ba3ba2b
1196 0000000000000000000000000000000000000000000000000000000000000040
1197 0000000000000000000000000000000000000000000000000000000000000001
1198 000000000000000000000000000000000000000000000000000000000000002c
1199 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1200 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1201 "
1202 )
1203 .to_vec(),
1204 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
1205 ),
1206 (
1207 "createFork(string,uint256)",
1209 hex!(
1210 "
1211 6ba3ba2b
1212 0000000000000000000000000000000000000000000000000000000000000040
1213 0000000000000000000000000000000000000000000000000000000000000001
1214 0000000000000000000000000000000000000000000000000000000000000007
1215 6d61696e6e657400000000000000000000000000000000000000000000000000
1216 "
1217 )
1218 .to_vec(),
1219 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
1220 ),
1221 (
1222 "createFork(string,bytes32)",
1224 hex!(
1225 "
1226 7ca29682
1227 0000000000000000000000000000000000000000000000000000000000000040
1228 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1229 000000000000000000000000000000000000000000000000000000000000002c
1230 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1231 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1232 "
1233 )
1234 .to_vec(),
1235 Some(vec![
1236 "\"<rpc url>\"".to_string(),
1237 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1238 .to_string(),
1239 ]),
1240 ),
1241 (
1242 "createFork(string,bytes32)",
1245 hex!(
1246 "
1247 7ca29682
1248 0000000000000000000000000000000000000000000000000000000000000040
1249 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1250 0000000000000000000000000000000000000000000000000000000000000007
1251 6d61696e6e657400000000000000000000000000000000000000000000000000
1252 "
1253 )
1254 .to_vec(),
1255 Some(vec![
1256 "\"mainnet\"".to_string(),
1257 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1258 .to_string(),
1259 ]),
1260 ),
1261 (
1262 "createSelectFork(string)",
1264 hex!(
1265 "
1266 98680034
1267 0000000000000000000000000000000000000000000000000000000000000020
1268 000000000000000000000000000000000000000000000000000000000000002c
1269 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1270 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1271 "
1272 )
1273 .to_vec(),
1274 Some(vec!["\"<rpc url>\"".to_string()]),
1275 ),
1276 (
1277 "createSelectFork(string)",
1279 hex!(
1280 "
1281 98680034
1282 0000000000000000000000000000000000000000000000000000000000000020
1283 0000000000000000000000000000000000000000000000000000000000000007
1284 6d61696e6e657400000000000000000000000000000000000000000000000000
1285 "
1286 )
1287 .to_vec(),
1288 Some(vec!["\"mainnet\"".to_string()]),
1289 ),
1290 (
1291 "createSelectFork(string,uint256)",
1293 hex!(
1294 "
1295 71ee464d
1296 0000000000000000000000000000000000000000000000000000000000000040
1297 0000000000000000000000000000000000000000000000000000000000000001
1298 000000000000000000000000000000000000000000000000000000000000002c
1299 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1300 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1301 "
1302 )
1303 .to_vec(),
1304 Some(vec!["\"<rpc url>\"".to_string(), "1".to_string()]),
1305 ),
1306 (
1307 "createSelectFork(string,uint256)",
1309 hex!(
1310 "
1311 71ee464d
1312 0000000000000000000000000000000000000000000000000000000000000040
1313 0000000000000000000000000000000000000000000000000000000000000001
1314 0000000000000000000000000000000000000000000000000000000000000007
1315 6d61696e6e657400000000000000000000000000000000000000000000000000
1316 "
1317 )
1318 .to_vec(),
1319 Some(vec!["\"mainnet\"".to_string(), "1".to_string()]),
1320 ),
1321 (
1322 "createSelectFork(string,bytes32)",
1324 hex!(
1325 "
1326 84d52b7a
1327 0000000000000000000000000000000000000000000000000000000000000040
1328 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1329 000000000000000000000000000000000000000000000000000000000000002c
1330 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1331 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1332 "
1333 )
1334 .to_vec(),
1335 Some(vec![
1336 "\"<rpc url>\"".to_string(),
1337 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1338 .to_string(),
1339 ]),
1340 ),
1341 (
1342 "createSelectFork(string,bytes32)",
1345 hex!(
1346 "
1347 84d52b7a
1348 0000000000000000000000000000000000000000000000000000000000000040
1349 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
1350 0000000000000000000000000000000000000000000000000000000000000007
1351 6d61696e6e657400000000000000000000000000000000000000000000000000
1352 "
1353 )
1354 .to_vec(),
1355 Some(vec![
1356 "\"mainnet\"".to_string(),
1357 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1358 .to_string(),
1359 ]),
1360 ),
1361 (
1362 "rpc(string,string,string)",
1364 hex!(
1365 "
1366 0199a220
1367 0000000000000000000000000000000000000000000000000000000000000060
1368 00000000000000000000000000000000000000000000000000000000000000c0
1369 0000000000000000000000000000000000000000000000000000000000000100
1370 000000000000000000000000000000000000000000000000000000000000002c
1371 68747470733a2f2f6574682d6d61696e6e65742e672e616c6368656d792e636f
1372 6d2f76322f6170695f6b65790000000000000000000000000000000000000000
1373 000000000000000000000000000000000000000000000000000000000000000e
1374 6574685f67657442616c616e6365000000000000000000000000000000000000
1375 0000000000000000000000000000000000000000000000000000000000000034
1376 5b22307835353165373738343737386566386530343865343935646634396632
1377 363134663834613466316463222c22307830225d000000000000000000000000
1378 "
1379 )
1380 .to_vec(),
1381 Some(vec![
1382 "\"<rpc url>\"".to_string(),
1383 "\"eth_getBalance\"".to_string(),
1384 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1385 .to_string(),
1386 ]),
1387 ),
1388 (
1389 "rpc(string,string,string)",
1392 hex!(
1393 "
1394 0199a220
1395 0000000000000000000000000000000000000000000000000000000000000060
1396 00000000000000000000000000000000000000000000000000000000000000a0
1397 00000000000000000000000000000000000000000000000000000000000000e0
1398 0000000000000000000000000000000000000000000000000000000000000007
1399 6d61696e6e657400000000000000000000000000000000000000000000000000
1400 000000000000000000000000000000000000000000000000000000000000000e
1401 6574685f67657442616c616e6365000000000000000000000000000000000000
1402 0000000000000000000000000000000000000000000000000000000000000034
1403 5b22307835353165373738343737386566386530343865343935646634396632
1404 363134663834613466316463222c22307830225d000000000000000000000000
1405 "
1406 )
1407 .to_vec(),
1408 Some(vec![
1409 "\"mainnet\"".to_string(),
1410 "\"eth_getBalance\"".to_string(),
1411 "\"[\\\"0x551e7784778ef8e048e495df49f2614f84a4f1dc\\\",\\\"0x0\\\"]\""
1412 .to_string(),
1413 ]),
1414 ),
1415 ];
1416
1417 let cheatcode_output_test_cases = vec![
1419 ("createWallet(string)", Some("<pk>".to_string())),
1421 ("deriveKey(string,uint32)", Some("<pk>".to_string())),
1422 ("rpcUrl(string)", Some("<rpc url>".to_string())),
1424 ("rpcUrls()", Some("<rpc url>".to_string())),
1425 ("rpcUrlStructs()", Some("<rpc url>".to_string())),
1426 ];
1427
1428 for (function_signature, data, expected) in cheatcode_input_test_cases {
1429 let function = Function::parse(function_signature).unwrap();
1430 let result = decoder.decode_cheatcode_inputs(&function, &data);
1431 assert_eq!(result, expected, "Input case failed for: {function_signature}");
1432 }
1433
1434 for (function_signature, expected) in cheatcode_output_test_cases {
1435 let function = Function::parse(function_signature).unwrap();
1436 let result = Some(decoder.decode_cheatcode_outputs(&function).unwrap_or_default());
1437 assert_eq!(result, expected, "Output case failed for: {function_signature}");
1438 }
1439 }
1440
1441 struct RecordingIdentifier {
1443 queried: Vec<Address>,
1444 }
1445 impl TraceIdentifier for RecordingIdentifier {
1446 fn identify_addresses(&mut self, nodes: &[&CallTraceNode]) -> Vec<IdentifiedAddress<'_>> {
1447 self.queried.extend(nodes.iter().map(|n| n.trace.address));
1448 Vec::new()
1449 }
1450 }
1451
1452 #[test]
1453 fn test_identify_addresses_skips_evm_precompiles() {
1454 use foundry_evm_core::precompiles::SHA_256;
1455
1456 let decoder = CallTraceDecoder::new();
1457
1458 let mut arena = CallTraceArena::default();
1459 let regular_addr = Address::from([0x42; 20]);
1460 arena.nodes_mut()[0].trace.address = regular_addr;
1461
1462 arena.nodes_mut().push(CallTraceNode {
1464 trace: CallTrace {
1465 address: SHA_256,
1466 depth: 1,
1467 maybe_precompile: Some(true),
1468 ..Default::default()
1469 },
1470 idx: 1,
1471 ..Default::default()
1472 });
1473
1474 arena.nodes_mut().push(CallTraceNode {
1476 trace: CallTrace {
1477 address: SHA_256,
1478 depth: 1,
1479 maybe_precompile: None,
1480 ..Default::default()
1481 },
1482 idx: 2,
1483 ..Default::default()
1484 });
1485
1486 let mut identifier = RecordingIdentifier { queried: Vec::new() };
1487 decoder.identify_addresses(&arena, &mut identifier);
1488
1489 assert_eq!(identifier.queried, vec![regular_addr]);
1490 }
1491
1492 #[test]
1493 fn test_identify_addresses_skips_tempo_precompiles() {
1494 use foundry_evm_core::tempo::TEMPO_PRECOMPILE_ADDRESSES;
1495
1496 let mut decoder = CallTraceDecoder::new().clone();
1498 decoder.chain_id = Some(4217);
1499
1500 let mut arena = CallTraceArena::default();
1501 let regular_addr = Address::from([0x42; 20]);
1502 arena.nodes_mut()[0].trace.address = regular_addr;
1503
1504 let tempo_precompile = TEMPO_PRECOMPILE_ADDRESSES[0];
1507 arena.nodes_mut().push(CallTraceNode {
1508 trace: CallTrace {
1509 address: tempo_precompile,
1510 depth: 1,
1511 maybe_precompile: None,
1512 ..Default::default()
1513 },
1514 idx: 1,
1515 ..Default::default()
1516 });
1517
1518 let mut identifier = RecordingIdentifier { queried: Vec::new() };
1519 decoder.identify_addresses(&arena, &mut identifier);
1520
1521 assert_eq!(identifier.queried, vec![regular_addr]);
1523 }
1524
1525 #[test]
1526 fn test_identify_addresses_does_not_skip_tempo_precompiles_on_other_chains() {
1527 use foundry_evm_core::tempo::TEMPO_PRECOMPILE_ADDRESSES;
1528
1529 let mut decoder = CallTraceDecoder::new().clone();
1531 decoder.chain_id = Some(1);
1532
1533 let mut arena = CallTraceArena::default();
1534 let regular_addr = Address::from([0x42; 20]);
1535 arena.nodes_mut()[0].trace.address = regular_addr;
1536
1537 let tempo_precompile = TEMPO_PRECOMPILE_ADDRESSES[0];
1538 arena.nodes_mut().push(CallTraceNode {
1539 trace: CallTrace {
1540 address: tempo_precompile,
1541 depth: 1,
1542 maybe_precompile: None,
1543 ..Default::default()
1544 },
1545 idx: 1,
1546 ..Default::default()
1547 });
1548
1549 let mut identifier = RecordingIdentifier { queried: Vec::new() };
1550 decoder.identify_addresses(&arena, &mut identifier);
1551
1552 assert_eq!(identifier.queried, vec![regular_addr, tempo_precompile]);
1554 }
1555}