From 27576821ee6cc8b072ac100eab703e7978063ac5 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:57:44 +0100 Subject: [PATCH 1/2] chore: better parse error messages --- crates/cast/src/lib.rs | 13 ++++------ crates/common/src/abi.rs | 32 ++++++++++--------------- crates/evm/fuzz/src/strategies/param.rs | 4 ++-- crates/forge/bin/cmd/script/mod.rs | 4 ++-- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 8ff767354a78..c4637ac9dcd7 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -16,7 +16,7 @@ use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::Client; use foundry_common::{ - abi::encode_function_args, + abi::{encode_function_args, get_func}, fmt::*, types::{ToAlloy, ToEthers}, TransactionReceiptWithRevertReason, @@ -1561,12 +1561,7 @@ impl SimpleCast { /// # Ok::<_, eyre::Report>(()) /// ``` pub fn abi_encode(sig: &str, args: &[impl AsRef]) -> Result { - let func = match Function::parse(sig) { - Ok(func) => func, - Err(err) => { - eyre::bail!("Could not process human-readable ABI. Please check if you've left the parenthesis unclosed or if some type is incomplete.\nError:\n{}", err) - } - }; + let func = get_func(sig)?; let calldata = match encode_function_args(&func, args) { Ok(res) => hex::encode(res), Err(e) => eyre::bail!("Could not ABI encode the function and arguments. Did you pass in the right types?\nError\n{}", e), @@ -1589,7 +1584,7 @@ impl SimpleCast { /// # Ok::<_, eyre::Report>(()) /// ``` pub fn calldata_encode(sig: impl AsRef, args: &[impl AsRef]) -> Result { - let func = Function::parse(sig.as_ref())?; + let func = get_func(sig.as_ref())?; let calldata = encode_function_args(&func, args)?; Ok(hex::encode_prefixed(calldata)) } @@ -1909,7 +1904,7 @@ impl SimpleCast { eyre::bail!("number of leading zeroes must not be greater than 4"); } if optimize == 0 { - let selector = Function::parse(signature)?.selector(); + let selector = get_func(signature)?.selector(); return Ok((selector.to_string(), String::from(signature))) } let Some((name, params)) = signature.split_once('(') else { diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index 5a166a20f09e..622d8481cf92 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -1,9 +1,9 @@ //! ABI related helper functions. use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt, JsonAbiExt}; -use alloy_json_abi::{AbiItem, Event, Function}; +use alloy_json_abi::{Event, Function}; use alloy_primitives::{hex, Address, Log}; -use eyre::{ContextCompat, Result}; +use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::{contract::ContractMetadata, errors::EtherscanError, Client}; use foundry_config::Chain; use std::{future::Future, pin::Pin}; @@ -28,7 +28,7 @@ pub fn abi_decode_calldata( input: bool, fn_selector: bool, ) -> Result> { - let func = Function::parse(sig)?; + let func = get_func(sig)?; let calldata = hex::decode(calldata)?; let mut calldata = calldata.as_slice(); @@ -56,7 +56,7 @@ pub fn abi_decode_calldata( pub trait IntoFunction { /// Consumes self and produces a function /// - /// # Panic + /// # Panics /// /// This function does not return a Result, so it is expected that the consumer /// uses it correctly so that it does not panic. @@ -70,38 +70,30 @@ impl IntoFunction for Function { } impl IntoFunction for String { + #[track_caller] fn into(self) -> Function { IntoFunction::into(self.as_str()) } } impl<'a> IntoFunction for &'a str { + #[track_caller] fn into(self) -> Function { - Function::parse(self).expect("could not parse function") + match get_func(self) { + Ok(func) => func, + Err(e) => panic!("could not parse function: {e}"), + } } } /// Given a function signature string, it tries to parse it as a `Function` pub fn get_func(sig: &str) -> Result { - if let Ok(func) = Function::parse(sig) { - Ok(func) - } else { - // Try to parse as human readable ABI. - let item = match AbiItem::parse(sig) { - Ok(item) => match item { - AbiItem::Function(func) => func, - _ => return Err(eyre::eyre!("Expected function, got {:?}", item)), - }, - Err(e) => return Err(e.into()), - }; - Ok(item.into_owned().to_owned()) - } + Function::parse(sig).wrap_err("could not parse function signature") } /// Given an event signature string, it tries to parse it as a `Event` pub fn get_event(sig: &str) -> Result { - let sig = sig.strip_prefix("event").unwrap_or(sig).trim(); - Ok(Event::parse(sig)?) + Event::parse(sig).wrap_err("could not parse event signature") } /// Given an event without indexed parameters and a rawlog, it tries to return the event with the diff --git a/crates/evm/fuzz/src/strategies/param.rs b/crates/evm/fuzz/src/strategies/param.rs index 09fbfe590bc2..093cb29c8b42 100644 --- a/crates/evm/fuzz/src/strategies/param.rs +++ b/crates/evm/fuzz/src/strategies/param.rs @@ -166,14 +166,14 @@ pub fn fuzz_param_from_state( #[cfg(test)] mod tests { use crate::strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state}; - use alloy_json_abi::Function; + use foundry_common::abi::get_func; use foundry_config::FuzzDictionaryConfig; use revm::db::{CacheDB, EmptyDB}; #[test] fn can_fuzz_array() { let f = "testArray(uint64[2] calldata values)"; - let func = Function::parse(f).unwrap(); + let func = get_func(f).unwrap(); let db = CacheDB::new(EmptyDB::default()); let state = build_initial_state(&db, &FuzzDictionaryConfig::default()); let strat = proptest::strategy::Union::new_weighted(vec![ diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/forge/bin/cmd/script/mod.rs index 472b38c752fd..335c89f1b9c5 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/forge/bin/cmd/script/mod.rs @@ -24,7 +24,7 @@ use forge::{ }; use foundry_cli::opts::MultiWallet; use foundry_common::{ - abi::encode_function_args, + abi::{encode_function_args, get_func}, contracts::get_contract_name, errors::UnlinkedByteCode, evm::{Breakpoints, EvmArgs}, @@ -445,7 +445,7 @@ impl ScriptArgs { /// /// Note: We assume that the `sig` is already stripped of its prefix, See [`ScriptArgs`] fn get_method_and_calldata(&self, abi: &Abi) -> Result<(Function, Bytes)> { - let (func, data) = if let Ok(func) = Function::parse(&self.sig) { + let (func, data) = if let Ok(func) = get_func(&self.sig) { ( abi.functions().find(|&abi_func| abi_func.selector() == func.selector()).wrap_err( format!("Function `{}` is not implemented in your script.", self.sig), From 93bc9d60bfa3dc113455eef202578a6efd8a16d1 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sat, 2 Dec 2023 15:00:32 +0100 Subject: [PATCH 2/2] chore: clippy --- crates/cast/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index c4637ac9dcd7..427fdb0b1dbb 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1,5 +1,5 @@ use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; -use alloy_json_abi::{ContractObject, Function}; +use alloy_json_abi::ContractObject; use alloy_primitives::{Address, I256, U256}; use alloy_rlp::Decodable; use base::{Base, NumberWithBase, ToBase};