Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
chore: improve ABI parser and Abigen errors (#2561)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes authored Aug 17, 2023
1 parent fa30177 commit 28bb3c0
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 22 deletions.
25 changes: 12 additions & 13 deletions ethers-contract/ethers-contract-abigen/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use ethers_core::{
macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate},
types::Bytes,
};
use eyre::{eyre, Context as _, Result};
use eyre::{eyre, Result};
use proc_macro2::{Ident, Literal, TokenStream};
use quote::{format_ident, quote};
use serde::Deserialize;
Expand Down Expand Up @@ -206,8 +206,8 @@ impl Context {
// holds the deployed bytecode parsed from the abi_str, if present
let mut contract_deployed_bytecode = None;

let (abi, human_readable, abi_parser) = parse_abi(&abi_str).wrap_err_with(|| {
eyre::eyre!("error parsing abi for contract: {}", args.contract_name)
let (abi, human_readable, abi_parser) = parse_abi(&abi_str).map_err(|e| {
eyre::eyre!("error parsing abi for contract '{}': {e}", args.contract_name)
})?;

// try to extract all the solidity structs from the normal JSON ABI
Expand Down Expand Up @@ -465,16 +465,15 @@ where
/// Parse the abi via `Source::parse` and return if the abi defined as human readable
fn parse_abi(abi_str: &str) -> Result<(Abi, bool, AbiParser)> {
let mut abi_parser = AbiParser::default();
let res = if let Ok(abi) = abi_parser.parse_str(abi_str) {
(abi, true, abi_parser)
} else {
// a best-effort coercion of an ABI or an artifact JSON into an artifact JSON.
let contract: JsonContract = serde_json::from_str(abi_str)
.wrap_err_with(|| eyre::eyre!("failed deserializing abi:\n{}", abi_str))?;

(contract.into_abi(), false, abi_parser)
};
Ok(res)
match abi_parser.parse_str(abi_str) {
Ok(abi) => Ok((abi, true, abi_parser)),
Err(e) => match serde_json::from_str::<JsonContract>(abi_str) {
Ok(contract) => Ok((contract.into_abi(), false, abi_parser)),
Err(e2) => Err(eyre::eyre!(
"couldn't parse ABI string as either human readable (1) or JSON (2):\n1. {e}\n2. {e2}"
)),
},
}
}

#[derive(Deserialize)]
Expand Down
18 changes: 9 additions & 9 deletions ethers-core/src/abi/human_readable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,17 @@ impl AbiParser {
let mut unresolved = self.structs.keys().collect::<VecDeque<_>>();
let mut sequential_retries = 0;
while let Some(name) = unresolved.pop_front() {
let mut resolved = true;
let mut unresolved_field = None;
let sol = &self.structs[name];
let mut tuple = Vec::with_capacity(sol.fields().len());
for field in sol.fields() {
match field.r#type() {
FieldType::Elementary(param) => tuple.push(param.clone()),
FieldType::Struct(ty) => {
if let Some(param) = self.struct_tuples.get(ty.name()).cloned() {
tuple.push(ty.as_param(ParamType::Tuple(param)))
if let Some(param) = self.struct_tuples.get(ty.name()) {
tuple.push(ty.as_param(ParamType::Tuple(param.clone())))
} else {
resolved = false;
unresolved_field = Some(field);
break
}
}
Expand All @@ -155,15 +155,15 @@ impl AbiParser {
}
}
}
if resolved {
sequential_retries = 0;
self.struct_tuples.insert(sol.name().to_string(), tuple);
} else {
if let Some(f) = unresolved_field {
sequential_retries += 1;
if sequential_retries > unresolved.len() {
bail!("No struct definition found for struct `{}`", name)
bail!("Could not resolve field of struct '{name}': `{}: {:?}`", f.name, f.ty)
}
unresolved.push_back(name);
} else {
sequential_retries = 0;
self.struct_tuples.insert(sol.name().to_string(), tuple);
}
}
Ok(())
Expand Down

0 comments on commit 28bb3c0

Please sign in to comment.