foundry_evm_traces/identifier/mod.rs
1use alloy_json_abi::JsonAbi;
2use alloy_primitives::{Address, Bytes, map::HashMap};
3use foundry_common::ContractsByArtifact;
4use foundry_compilers::ArtifactId;
5use foundry_config::{Chain, Config};
6use revm_inspectors::tracing::types::CallTraceNode;
7use std::borrow::Cow;
8
9mod local;
10pub use local::LocalTraceIdentifier;
11
12mod etherscan;
13pub use etherscan::EtherscanIdentifier;
14
15mod signatures;
16pub use signatures::{SignaturesCache, SignaturesIdentifier};
17
18/// An address identified by a [`TraceIdentifier`].
19pub struct IdentifiedAddress<'a> {
20 /// The address.
21 pub address: Address,
22 /// The label for the address.
23 pub label: Option<String>,
24 /// The contract this address represents.
25 ///
26 /// Note: This may be in the format `"<artifact>:<contract>"`.
27 pub contract: Option<String>,
28 /// The ABI of the contract at this address.
29 pub abi: Option<Cow<'a, JsonAbi>>,
30 /// The artifact ID of the contract, if any.
31 pub artifact_id: Option<ArtifactId>,
32}
33
34/// Trace identifiers figure out what ABIs and labels belong to all the addresses of the trace.
35pub trait TraceIdentifier {
36 /// Attempts to identify an address in one or more call traces.
37 fn identify_addresses(&mut self, nodes: &[&CallTraceNode]) -> Vec<IdentifiedAddress<'_>>;
38}
39
40/// A collection of trace identifiers.
41pub struct TraceIdentifiers<'a> {
42 /// The local trace identifier.
43 pub local: Option<LocalTraceIdentifier<'a>>,
44 /// The optional Etherscan trace identifier.
45 pub etherscan: Option<EtherscanIdentifier>,
46}
47
48impl Default for TraceIdentifiers<'_> {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl TraceIdentifier for TraceIdentifiers<'_> {
55 fn identify_addresses(&mut self, nodes: &[&CallTraceNode]) -> Vec<IdentifiedAddress<'_>> {
56 let mut identities = Vec::with_capacity(nodes.len());
57 if let Some(local) = &mut self.local {
58 identities.extend(local.identify_addresses(nodes));
59 if identities.len() >= nodes.len() {
60 return identities;
61 }
62 }
63 if let Some(etherscan) = &mut self.etherscan {
64 identities.extend(etherscan.identify_addresses(nodes));
65 }
66 identities
67 }
68}
69
70impl<'a> TraceIdentifiers<'a> {
71 /// Creates a new, empty instance.
72 pub const fn new() -> Self {
73 Self { local: None, etherscan: None }
74 }
75
76 /// Sets the local identifier.
77 pub fn with_local(mut self, known_contracts: &'a ContractsByArtifact) -> Self {
78 self.local = Some(LocalTraceIdentifier::new(known_contracts));
79 self
80 }
81
82 /// Sets the local identifier.
83 pub fn with_local_and_bytecodes(
84 mut self,
85 known_contracts: &'a ContractsByArtifact,
86 contracts_bytecode: &'a HashMap<Address, Bytes>,
87 ) -> Self {
88 self.local =
89 Some(LocalTraceIdentifier::new(known_contracts).with_bytecodes(contracts_bytecode));
90 self
91 }
92
93 /// Sets the etherscan identifier.
94 pub fn with_etherscan(mut self, config: &Config, chain: Option<Chain>) -> eyre::Result<Self> {
95 self.etherscan = EtherscanIdentifier::new(config, chain)?;
96 Ok(self)
97 }
98
99 /// Returns `true` if there are no set identifiers.
100 pub fn is_empty(&self) -> bool {
101 self.local.is_none() && self.etherscan.is_none()
102 }
103}