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