Skip to content

Commit b26af76

Browse files
committed
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 foundry-rs#12152 to the refactored external.rs file (after the Sourcify integration in foundry-rs#11917).
1 parent 27a5d39 commit b26af76

File tree

1 file changed

+33
-15
lines changed

1 file changed

+33
-15
lines changed

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

Lines changed: 33 additions & 15 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,26 +74,39 @@ 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> {
77+
// Collect contract info upfront so we can reference it in error messages
78+
let contracts_info: Vec<_> = self
79+
.contracts
80+
.iter()
81+
.filter(|(_, (_, metadata))| {
82+
metadata.as_ref().is_some_and(|metadata| !metadata.is_vyper())
83+
})
84+
.map(|(addr, (_, metadata))| (*addr, metadata.as_ref().unwrap().contract_name.clone()))
85+
.collect();
86+
7687
let outputs_fut = self
7788
.contracts
7889
.iter()
7990
// filter out vyper files
8091
.filter(|(_, (_, metadata))| {
8192
metadata.as_ref().is_some_and(|metadata| !metadata.is_vyper())
8293
})
83-
.map(|(address, (_, metadata))| async move {
84-
let metadata = metadata.as_ref().unwrap();
85-
sh_println!("Compiling: {} {address}", metadata.contract_name)?;
86-
let root = tempfile::tempdir()?;
87-
let root_path = root.path();
88-
let project = etherscan_project(metadata, root_path)?;
89-
let output = project.compile()?;
90-
91-
if output.has_compiler_errors() {
92-
eyre::bail!("{output}")
93-
}
94+
.map(|(address, (_, metadata))| {
95+
let addr = *address;
96+
let metadata = metadata.as_ref().unwrap().clone();
97+
async move {
98+
sh_println!("Compiling: {} {addr}", metadata.contract_name)?;
99+
let root = tempfile::tempdir()?;
100+
let root_path = root.path();
101+
let project = etherscan_project(&metadata, root_path)?;
102+
let output = project.compile()?;
103+
104+
if output.has_compiler_errors() {
105+
eyre::bail!("{output}")
106+
}
94107

95-
Ok((project, output, root))
108+
Ok((project, output, root))
109+
}
96110
})
97111
.collect::<Vec<_>>();
98112

@@ -102,9 +116,13 @@ impl ExternalIdentifier {
102116
let mut sources: ContractSources = Default::default();
103117

104118
// construct the map
105-
for res in outputs {
106-
let (project, output, _root) = res?;
107-
sources.insert(&output, project.root(), None)?;
119+
for (idx, res) in outputs.into_iter().enumerate() {
120+
let (addr, name) = &contracts_info[idx];
121+
let (project, output, _root) =
122+
res.wrap_err_with(|| format!("Failed to compile contract {name} at {addr}"))?;
123+
sources
124+
.insert(&output, project.root(), None)
125+
.wrap_err_with(|| format!("Failed to insert contract {name} at {addr}"))?;
108126
}
109127

110128
Ok(sources)

0 commit comments

Comments
 (0)