From 28bb3c02a585c52fb56ee5b3c8a359602597187c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Thu, 17 Aug 2023 17:59:45 +0200 Subject: [PATCH] chore: improve ABI parser and Abigen errors (#2561) --- .../ethers-contract-abigen/src/contract.rs | 25 +++++++++---------- ethers-core/src/abi/human_readable/mod.rs | 18 ++++++------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs index 729b11c76..bf565aa59 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract.rs @@ -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; @@ -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 @@ -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::(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)] diff --git a/ethers-core/src/abi/human_readable/mod.rs b/ethers-core/src/abi/human_readable/mod.rs index db5d97ac5..780a70854 100644 --- a/ethers-core/src/abi/human_readable/mod.rs +++ b/ethers-core/src/abi/human_readable/mod.rs @@ -133,17 +133,17 @@ impl AbiParser { let mut unresolved = self.structs.keys().collect::>(); 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 } } @@ -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(())