Skip to content

Commit 074ca5a

Browse files
silvekkk0xrusowsky
andauthored
feat(traces): add contract context to Etherscan compilation errors (#12152)
* feat(traces): add contract context to compilation errors Add contract name and address to error messages when compiling contracts from Etherscan/Sourcify fails, making it easier to identify which specific contract caused the compilation failure. Applies the same improvement as PR #12152 to the refactored external.rs file (after the Sourcify integration in #11917). * chore: avoid redundant work and allocations --------- Co-authored-by: 0xrusowsky <0xrusowsky@proton.me>
1 parent 98bc956 commit 074ca5a

File tree

1 file changed

+26
-11
lines changed

1 file changed

+26
-11
lines changed

crates/evm/traces/src/identifier/external.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use alloy_primitives::{
44
Address,
55
map::{Entry, HashMap},
66
};
7+
use eyre::WrapErr;
78
use foundry_block_explorers::{contract::Metadata, errors::EtherscanError};
89
use foundry_common::compile::etherscan_project;
910
use foundry_config::{Chain, Config};
@@ -73,21 +74,30 @@ impl ExternalIdentifier {
7374
/// Goes over the list of contracts we have pulled from the traces, clones their source from
7475
/// Etherscan and compiles them locally, for usage in the debugger.
7576
pub async fn get_compiled_contracts(&self) -> eyre::Result<ContractSources> {
76-
let outputs_fut = self
77+
// Collect contract info upfront so we can reference it in error messages
78+
let contracts_info: Vec<_> = self
7779
.contracts
7880
.iter()
79-
// filter out vyper files
80-
.filter(|(_, (_, metadata))| {
81-
metadata.as_ref().is_some_and(|metadata| !metadata.is_vyper())
81+
// filter out vyper files and contracts without metadata
82+
.filter_map(|(addr, (_, metadata))| {
83+
if let Some(metadata) = metadata.as_ref()
84+
&& !metadata.is_vyper()
85+
{
86+
Some((*addr, metadata))
87+
} else {
88+
None
89+
}
8290
})
83-
.map(|(address, (_, metadata))| async move {
84-
let metadata = metadata.as_ref().unwrap();
85-
sh_println!("Compiling: {} {address}", metadata.contract_name)?;
91+
.collect();
92+
93+
let outputs_fut = contracts_info
94+
.iter()
95+
.map(|(addr, metadata)| async move {
96+
sh_println!("Compiling: {} {addr}", metadata.contract_name)?;
8697
let root = tempfile::tempdir()?;
8798
let root_path = root.path();
8899
let project = etherscan_project(metadata, root_path)?;
89100
let output = project.compile()?;
90-
91101
if output.has_compiler_errors() {
92102
eyre::bail!("{output}")
93103
}
@@ -102,9 +112,14 @@ impl ExternalIdentifier {
102112
let mut sources: ContractSources = Default::default();
103113

104114
// construct the map
105-
for res in outputs {
106-
let (project, output, _root) = res?;
107-
sources.insert(&output, project.root(), None)?;
115+
for (idx, res) in outputs.into_iter().enumerate() {
116+
let (addr, metadata) = &contracts_info[idx];
117+
let name = &metadata.contract_name;
118+
let (project, output, _) =
119+
res.wrap_err_with(|| format!("Failed to compile contract {name} at {addr}"))?;
120+
sources
121+
.insert(&output, project.root(), None)
122+
.wrap_err_with(|| format!("Failed to insert contract {name} at {addr}"))?;
108123
}
109124

110125
Ok(sources)

0 commit comments

Comments
 (0)