fix(abigen): builtin trait derives (#2170)

* fix(abigen): builtin trait derives

* refactor: EthDisplay

* feat(derive): improve type detection

* chore: cleanup

* chore: edition 2021

* chore: clippy

* chore: use unreachable
This commit is contained in:
DaniPopes 2023-02-21 01:27:43 +01:00 committed by GitHub
parent c21362b696
commit 3732de844c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 572 additions and 222 deletions

View File

@ -1,7 +1,7 @@
[package] [package]
name = "ethers-contract" name = "ethers-contract"
version = "1.0.2" version = "1.0.2"
edition = "2018" edition = "2021"
rust-version = "1.64" rust-version = "1.64"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@ -52,16 +52,15 @@ impl Context {
hex::encode(error.selector()) hex::encode(error.selector())
); );
let mut extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
if util::can_derive_defaults(&error.inputs) { let params = error.inputs.iter().map(|param| &param.kind);
extra_derives.extend(quote!(Default)); util::derive_builtin_traits(params, &mut derives, true, true);
}
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
Ok(quote! { Ok(quote! {
#[doc = #doc_str] #[doc = #doc_str]
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthError, #ethers_contract::EthDisplay, #extra_derives)] #[derive(Clone, #ethers_contract::EthError, #ethers_contract::EthDisplay, #derives)]
#[etherror(name = #error_name, abi = #abi_signature)] #[etherror(name = #error_name, abi = #abi_signature)]
pub #data_type_definition pub #data_type_definition
}) })
@ -92,14 +91,17 @@ impl Context {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
let params =
self.abi.errors.values().flatten().flat_map(|err| &err.inputs).map(|param| &param.kind);
util::derive_builtin_traits(params, &mut derives, false, true);
let ethers_core = ethers_core_crate(); let ethers_core = ethers_core_crate();
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
quote! { quote! {
#[doc = "Container type for all of the contract's custom errors"] #[doc = "Container type for all of the contract's custom errors"]
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #extra_derives)] #[derive(Clone, #ethers_contract::EthAbiType, #derives)]
pub enum #enum_name { pub enum #enum_name {
#( #variants(#variants), )* #( #variants(#variants), )*
} }

View File

@ -62,15 +62,19 @@ impl Context {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let enum_name = self.expand_event_enum_name();
let mut derives = self.expand_extra_derives();
let params =
self.abi.events.values().flatten().flat_map(|err| &err.inputs).map(|param| &param.kind);
util::derive_builtin_traits(params, &mut derives, false, true);
let ethers_core = ethers_core_crate(); let ethers_core = ethers_core_crate();
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
let extra_derives = self.expand_extra_derives();
let enum_name = self.expand_event_enum_name();
quote! { quote! {
#[doc = "Container type for all of the contract's events"] #[doc = "Container type for all of the contract's events"]
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #extra_derives)] #[derive(Clone, #ethers_contract::EthAbiType, #derives)]
pub enum #enum_name { pub enum #enum_name {
#( #variants(#variants), )* #( #variants(#variants), )*
} }
@ -191,15 +195,14 @@ impl Context {
let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty()); let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
let data_type_definition = expand_event_struct(&struct_name, &fields, all_anonymous_fields); let data_type_definition = expand_event_struct(&struct_name, &fields, all_anonymous_fields);
let mut extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
if event.inputs.iter().map(|param| &param.kind).all(util::can_derive_default) { let params = event.inputs.iter().map(|param| &param.kind);
extra_derives.extend(quote!(Default)); util::derive_builtin_traits(params, &mut derives, true, true);
}
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
Ok(quote! { Ok(quote! {
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #extra_derives)] #[derive(Clone, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)]
#[ethevent(name = #name, abi = #abi_signature)] #[ethevent(name = #name, abi = #abi_signature)]
pub #data_type_definition pub #data_type_definition
}) })

View File

@ -122,16 +122,15 @@ impl Context {
hex::encode(function.selector()) hex::encode(function.selector())
); );
let mut extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
if util::can_derive_defaults(&function.inputs) { let params = function.inputs.iter().map(|param| &param.kind);
extra_derives.extend(quote!(Default)); util::derive_builtin_traits(params, &mut derives, true, true);
}
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
Ok(quote! { Ok(quote! {
#[doc = #doc_str] #[doc = #doc_str]
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthCall, #ethers_contract::EthDisplay, #extra_derives)] #[derive(Clone, #ethers_contract::EthCall, #ethers_contract::EthDisplay, #derives)]
#[ethcall( name = #function_name, abi = #abi_signature )] #[ethcall( name = #function_name, abi = #abi_signature )]
pub #call_type_definition pub #call_type_definition
}) })
@ -162,16 +161,15 @@ impl Context {
hex::encode(function.selector()) hex::encode(function.selector())
); );
let mut extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
if util::can_derive_defaults(&function.inputs) { let params = function.inputs.iter().map(|param| &param.kind);
extra_derives.extend(quote!(Default)); util::derive_builtin_traits(params, &mut derives, true, true);
}
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
Ok(Some(quote! { Ok(Some(quote! {
#[doc = #doc_str] #[doc = #doc_str]
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #extra_derives)] #[derive(Clone, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
pub #return_type_definition pub #return_type_definition
})) }))
} }
@ -197,7 +195,10 @@ impl Context {
return Ok(struct_def_tokens) return Ok(struct_def_tokens)
} }
let extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
let params =
self.abi.functions.values().flatten().flat_map(|f| &f.inputs).map(|param| &param.kind);
util::derive_builtin_traits(params, &mut derives, false, true);
let enum_name = self.expand_calls_enum_name(); let enum_name = self.expand_calls_enum_name();
@ -208,7 +209,7 @@ impl Context {
#struct_def_tokens #struct_def_tokens
#[doc = "Container type for all of the contract's call "] #[doc = "Container type for all of the contract's call "]
#[derive(Debug, Clone, PartialEq, Eq, #ethers_contract::EthAbiType, #extra_derives)] #[derive(Clone, #ethers_contract::EthAbiType, #derives)]
pub enum #enum_name { pub enum #enum_name {
#( #variant_names(#struct_names), )* #( #variant_names(#struct_names), )*
} }

View File

@ -61,9 +61,9 @@ impl Context {
.internal_structs .internal_structs
.struct_tuples .struct_tuples
.get(id) .get(id)
.ok_or_else(|| eyre!("No types found for {id}"))? .ok_or_else(|| eyre!("No types found for {id}"))?;
.clone(); let types = if let ParamType::Tuple(types) = tuple { types } else { unreachable!() };
self.expand_internal_struct(struct_name, sol_struct, tuple) self.expand_internal_struct(struct_name, sol_struct, types)
} }
/// Returns the `TokenStream` with all the internal structs extracted form the JSON ABI /// Returns the `TokenStream` with all the internal structs extracted form the JSON ABI
@ -83,7 +83,7 @@ impl Context {
&self, &self,
name: &str, name: &str,
sol_struct: &SolStruct, sol_struct: &SolStruct,
tuple: ParamType, types: &[ParamType],
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let mut fields = Vec::with_capacity(sol_struct.fields().len()); let mut fields = Vec::with_capacity(sol_struct.fields().len());
@ -112,19 +112,17 @@ impl Context {
let struct_def = expand_struct(&name, &fields, is_tuple); let struct_def = expand_struct(&name, &fields, is_tuple);
let sig = match tuple { let sig = util::abi_signature_types(types);
ParamType::Tuple(ref types) if !types.is_empty() => util::abi_signature_types(types),
_ => String::new(),
};
let doc_str = format!("`{name}({sig})`"); let doc_str = format!("`{name}({sig})`");
let extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
util::derive_builtin_traits_struct(&self.internal_structs, sol_struct, types, &mut derives);
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
Ok(quote! { Ok(quote! {
#[doc = #doc_str] #[doc = #doc_str]
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #extra_derives)] #[derive(Clone, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
pub #struct_def pub #struct_def
}) })
} }
@ -167,16 +165,14 @@ impl Context {
let name = util::ident(name); let name = util::ident(name);
let mut extra_derives = self.expand_extra_derives(); let mut derives = self.expand_extra_derives();
if param_types.iter().all(util::can_derive_default) { util::derive_builtin_traits(&param_types, &mut derives, true, true);
extra_derives.extend(quote!(Default))
}
let ethers_contract = ethers_contract_crate(); let ethers_contract = ethers_contract_crate();
Ok(quote! { Ok(quote! {
#[doc = #abi_signature] #[doc = #abi_signature]
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #extra_derives)] #[derive(Clone, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
pub struct #name { pub struct #name {
#( #fields ),* #( #fields ),*
} }
@ -231,7 +227,7 @@ impl InternalStructs {
let mut function_params = HashMap::new(); let mut function_params = HashMap::new();
let mut outputs = HashMap::new(); let mut outputs = HashMap::new();
let mut event_params = HashMap::new(); let mut event_params = HashMap::new();
let mut structs = HashMap::new();
for item in abi for item in abi
.into_iter() .into_iter()
.filter(|item| matches!(item.type_field.as_str(), "constructor" | "function" | "event")) .filter(|item| matches!(item.type_field.as_str(), "constructor" | "function" | "event"))
@ -279,6 +275,7 @@ impl InternalStructs {
// turn each top level internal type (function input/output) and their nested types // turn each top level internal type (function input/output) and their nested types
// into a struct will create all structs // into a struct will create all structs
let mut structs = HashMap::new();
for component in top_level_internal_types.values() { for component in top_level_internal_types.values() {
insert_structs(&mut structs, component); insert_structs(&mut structs, component);
} }
@ -569,7 +566,7 @@ fn struct_type_name(name: &str) -> &str {
struct_type_identifier(name).rsplit('.').next().unwrap() struct_type_identifier(name).rsplit('.').next().unwrap()
} }
/// `Pairing.G2Point` -> `Pairing.G2Point` /// `struct Pairing.G2Point[]` -> `Pairing.G2Point`
fn struct_type_identifier(name: &str) -> &str { fn struct_type_identifier(name: &str) -> &str {
name.trim_start_matches("struct ").split('[').next().unwrap() name.trim_start_matches("struct ").split('[').next().unwrap()
} }

View File

@ -1342,7 +1342,7 @@ contract Enum {
[package] [package]
name = "ethers-contract" name = "ethers-contract"
version = "1.0.0" version = "1.0.0"
edition = "2018" edition = "2021"
rust-version = "1.64" rust-version = "1.64"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -1388,7 +1388,7 @@ contract Enum {
[package] [package]
name = "ethers-contract" name = "ethers-contract"
version = "1.0.0" version = "1.0.0"
edition = "2018" edition = "2021"
rust-version = "1.64" rust-version = "1.64"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -1435,7 +1435,7 @@ contract Enum {
[package] [package]
name = "ethers-contract" name = "ethers-contract"
version = "1.0.0" version = "1.0.0"
edition = "2018" edition = "2021"
rust-version = "1.64" rust-version = "1.64"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -1482,7 +1482,7 @@ contract Enum {
[package] [package]
name = "ethers-contract" name = "ethers-contract"
version = "1.0.0" version = "1.0.0"
edition = "2018" edition = "2021"
rust-version = "1.64" rust-version = "1.64"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@ -1,4 +1,8 @@
use ethers_core::abi::{Param, ParamType}; use crate::InternalStructs;
use ethers_core::abi::{
struct_def::{FieldType, StructFieldType},
ParamType, SolStruct,
};
use eyre::Result; use eyre::Result;
use inflector::Inflector; use inflector::Inflector;
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
@ -165,28 +169,158 @@ pub(crate) fn json_files(root: impl AsRef<Path>) -> Vec<PathBuf> {
.collect() .collect()
} }
/// Returns whether all the given parameters can derive [`Default`]. /// Returns whether all the given parameters can derive the builtin traits.
/// ///
/// rust-std derives `Default` automatically only for arrays len <= 32 /// The following traits are only implemented on tuples of arity 12 or less:
pub(crate) fn can_derive_defaults<'a>(params: impl IntoIterator<Item = &'a Param>) -> bool { ///
params.into_iter().map(|param| &param.kind).all(can_derive_default) /// - [PartialEq](https://doc.rust-lang.org/stable/std/cmp/trait.PartialEq.html)
/// - [Eq](https://doc.rust-lang.org/stable/std/cmp/trait.Eq.html)
/// - [PartialOrd](https://doc.rust-lang.org/stable/std/cmp/trait.PartialOrd.html)
/// - [Ord](https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html)
/// - [Debug](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html)
/// - [Default](https://doc.rust-lang.org/stable/std/default/trait.Default.html)
/// - [Hash](https://doc.rust-lang.org/stable/std/hash/trait.Hash.html)
///
/// while the `Default` trait is only implemented on arrays of length 32 or less.
///
/// Tuple reference: <https://doc.rust-lang.org/stable/std/primitive.tuple.html#trait-implementations-1>
///
/// Array reference: <https://doc.rust-lang.org/stable/std/primitive.array.html>
///
/// `derive_default` should be set to false when calling this for enums.
pub(crate) fn derive_builtin_traits<'a>(
params: impl IntoIterator<Item = &'a ParamType>,
stream: &mut TokenStream,
mut derive_default: bool,
mut derive_others: bool,
) {
for param in params {
derive_default &= can_derive_default(param);
derive_others &= can_derive_builtin_traits(param);
}
extend_derives(stream, derive_default, derive_others);
} }
/// Returns whether the given type can derive [`Default`]. /// This has to be a seperate function since a sol struct is converted into a tuple, but for
/// /// deriving purposes it shouldn't count as one, so we recurse back the struct fields.
/// rust-std derives `Default` automatically only for arrays len <= 32 pub(crate) fn derive_builtin_traits_struct(
pub(crate) fn can_derive_default(param: &ParamType) -> bool { structs: &InternalStructs,
const MAX_SUPPORTED_LEN: usize = 32; sol_struct: &SolStruct,
params: &[ParamType],
stream: &mut TokenStream,
) {
if sol_struct.fields().iter().any(|field| field.ty.is_struct()) {
let mut def = true;
let mut others = true;
_derive_builtin_traits_struct(structs, sol_struct, params, &mut def, &mut others);
extend_derives(stream, def, others);
} else {
derive_builtin_traits(params, stream, true, true);
}
}
fn _derive_builtin_traits_struct(
structs: &InternalStructs,
sol_struct: &SolStruct,
params: &[ParamType],
def: &mut bool,
others: &mut bool,
) {
let fields = sol_struct.fields();
debug_assert_eq!(fields.len(), params.len());
for (field, ty) in fields.iter().zip(params) {
match &field.ty {
FieldType::Struct(s_ty) => {
// a tuple here is actually a sol struct so we skip it
if !matches!(ty, ParamType::Tuple(_)) {
*def &= can_derive_default(ty);
*others &= can_derive_builtin_traits(ty);
}
let id = s_ty.identifier();
// TODO: InternalStructs does not contain this field's ID if the struct and field
// are in 2 different modules, like in `can_generate_internal_structs_multiple`
if let Some(recursed_struct) = structs.structs.get(&id) {
let recursed_params = get_struct_params(s_ty, ty);
_derive_builtin_traits_struct(
structs,
recursed_struct,
recursed_params,
def,
others,
);
}
}
FieldType::Elementary(ty1) => {
debug_assert_eq!(ty, ty1);
*def &= can_derive_default(ty);
*others &= can_derive_builtin_traits(ty);
}
FieldType::Mapping(_) => unreachable!(),
}
}
}
fn get_struct_params<'a>(s_ty: &StructFieldType, ty: &'a ParamType) -> &'a [ParamType] {
match (s_ty, ty) {
(StructFieldType::Type(_), ParamType::Tuple(params)) => params,
(StructFieldType::Array(s_ty), ParamType::Array(ty)) => get_struct_params(s_ty, ty),
(StructFieldType::FixedArray(s_ty, _), ParamType::FixedArray(ty, _)) => {
get_struct_params(s_ty, ty)
}
_ => unreachable!(),
}
}
fn extend_derives(stream: &mut TokenStream, def: bool, others: bool) {
if def {
stream.extend(quote!(Default,))
}
if others {
stream.extend(quote!(Debug, PartialEq, Eq, Hash))
}
}
const MAX_SUPPORTED_ARRAY_LEN: usize = 32;
const MAX_SUPPORTED_TUPLE_LEN: usize = 12;
/// Whether the given type can derive the `Default` trait.
fn can_derive_default(param: &ParamType) -> bool {
match param { match param {
ParamType::FixedBytes(len) => *len <= MAX_SUPPORTED_LEN, ParamType::Array(ty) => can_derive_default(ty),
ParamType::FixedBytes(len) => *len <= MAX_SUPPORTED_ARRAY_LEN,
ParamType::FixedArray(ty, len) => { ParamType::FixedArray(ty, len) => {
if *len > MAX_SUPPORTED_LEN { if *len > MAX_SUPPORTED_ARRAY_LEN {
false false
} else { } else {
can_derive_default(ty) can_derive_default(ty)
} }
} }
ParamType::Tuple(params) => params.iter().all(can_derive_default), ParamType::Tuple(params) => {
if params.len() > MAX_SUPPORTED_TUPLE_LEN {
false
} else {
params.iter().all(can_derive_default)
}
}
_ => true,
}
}
/// Whether the given type can derive the builtin traits listed in [`derive_builtin_traits`], minus
/// `Default`.
fn can_derive_builtin_traits(param: &ParamType) -> bool {
match param {
ParamType::Array(ty) | ParamType::FixedArray(ty, _) => can_derive_builtin_traits(ty),
ParamType::Tuple(params) => {
if params.len() > MAX_SUPPORTED_TUPLE_LEN {
false
} else {
params.iter().all(can_derive_builtin_traits)
}
}
_ => true, _ => true,
} }
} }
@ -211,12 +345,24 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn can_detect_non_default() { fn can_detect_derives() {
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 128); // array
assert!(!can_derive_default(&param)); let param = ParamType::FixedArray(Box::new(ParamType::Uint(256)), 32);
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 32);
assert!(can_derive_default(&param)); assert!(can_derive_default(&param));
assert!(can_derive_builtin_traits(&param));
let param = ParamType::FixedArray(Box::new(ParamType::Uint(256)), 33);
assert!(!can_derive_default(&param));
assert!(can_derive_builtin_traits(&param));
// tuple
let param = ParamType::Tuple(vec![ParamType::Uint(256); 12]);
assert!(can_derive_default(&param));
assert!(can_derive_builtin_traits(&param));
let param = ParamType::Tuple(vec![ParamType::Uint(256); 13]);
assert!(!can_derive_default(&param));
assert!(!can_derive_builtin_traits(&param));
} }
#[test] #[test]

View File

@ -22,82 +22,117 @@ pub(crate) fn derive_eth_display_impl(input: DeriveInput) -> Result<TokenStream,
} }
}; };
let ethers_core = ethers_core_crate(); let mut expressions = TokenStream::new();
let hex_encode = quote! {#ethers_core::utils::hex::encode}; for (i, field) in fields.iter().enumerate() {
let ident = field.ident.as_ref().map(|id| quote!(#id)).unwrap_or_else(|| {
let mut fmts = TokenStream::new(); let idx = Index::from(i);
for (idx, field) in fields.iter().enumerate() { quote!(#idx)
let ident = field.ident.clone().map(|id| quote! {#id}).unwrap_or_else(|| {
let idx = Index::from(idx);
quote! {#idx}
}); });
let tokens = if let Ok(param) = utils::find_parameter_type(&field.ty) { if let Ok(param) = utils::find_parameter_type(&field.ty) {
match param { let ethers_core = ethers_core_crate();
ParamType::Address | ParamType::Uint(_) | ParamType::Int(_) => { let hex_encode = quote!(#ethers_core::utils::hex::encode);
quote! { fmt_params_tokens(&param, ident, &mut expressions, &hex_encode);
write!(f, "{:?}", self.#ident)?;
}
}
ParamType::Bytes => {
quote! {
write!(f, "0x{}", #hex_encode(&self.#ident))?;
}
}
ParamType::Bool | ParamType::String => {
quote! {
self.#ident.fmt(f)?;
}
}
ParamType::Tuple(_) => {
quote! {
write!(f, "{:?}", &self.#ident)?;
}
}
ParamType::Array(ty) | ParamType::FixedArray(ty, _) => {
if *ty == ParamType::Uint(8) {
// `u8`
quote! {
write!(f, "0x{}", #hex_encode(&self.#ident[..]))?;
}
} else {
// format as array with `[arr[0].display, arr[1].display,...]`
quote! {
write!(f, "[")?;
for (idx, val) in self.#ident.iter().enumerate() {
write!(f, "{:?}", val)?;
if idx < self.#ident.len() - 1 {
write!(f, ", ")?;
}
}
write!(f, "]")?;
}
}
}
ParamType::FixedBytes(_) => {
quote! {
write!(f, "0x{}", #hex_encode(&self.#ident))?;
}
}
}
} else { } else {
// could not detect the parameter type and rely on using debug fmt // could not detect the parameter type and rely on using debug fmt
quote! { fmt_debug_tokens(&ident, &mut expressions);
write!(f, "{:?}", &self.#ident)?; }
}
}; // comma separator
fmts.extend(tokens); if i < fields.len() - 1 {
if idx < fields.len() - 1 { let tokens = quote! {
fmts.extend(quote! { write!(f, ", ")?;}); ::core::fmt::Write::write_str(f, ", ")?;
};
expressions.extend(tokens);
} }
} }
let name = &input.ident; let name = &input.ident;
Ok(quote! { Ok(quote! {
impl ::std::fmt::Display for #name { impl ::core::fmt::Display for #name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
#fmts #expressions
Ok(()) Ok(())
} }
} }
}) })
} }
/// Recursive for tuples len > 12.
fn fmt_params_tokens(
param: &ParamType,
ident: TokenStream,
out: &mut TokenStream,
hex_encode: &TokenStream,
) {
match param {
// Display
ParamType::Bool | ParamType::String | ParamType::Uint(_) | ParamType::Int(_) => {
fmt_display_tokens(&ident, out);
}
// Debug
ParamType::Address => fmt_debug_tokens(&ident, out),
// 0x ++ hex::encode
ParamType::Bytes | ParamType::FixedBytes(_) => hex_encode_tokens(&ident, out, hex_encode),
// Debug or recurse
ParamType::Tuple(params) => {
// Debug is implemented automatically only for tuples with arity <= 12
if params.len() <= 12 {
fmt_debug_tokens(&ident, out);
} else {
for (i, new_param) in params.iter().enumerate() {
let idx = Index::from(i);
let new_ident = quote!(#ident.#idx);
fmt_params_tokens(new_param, new_ident, out, hex_encode);
}
}
}
// 0x ++ hex::encode or DebugList
ParamType::Array(ty) | ParamType::FixedArray(ty, _) => match &**ty {
ParamType::Uint(8) => hex_encode_tokens(&ident, out, hex_encode),
ParamType::Tuple(params) if params.len() > 12 => {
// TODO: Recurse this
let idx = (0..params.len()).map(Index::from);
let tokens = quote! {
let mut list = f.debug_list();
for entry in self.#ident.iter() {
#( list.entry(&entry.#idx); )*
}
list.finish()?;
};
out.extend(tokens);
}
_ => {
let tokens = quote! {
f.debug_list().entries(self.#ident.iter()).finish()?;
};
out.extend(tokens);
}
},
}
}
fn fmt_display_tokens(ident: &TokenStream, out: &mut TokenStream) {
let tokens = quote! {
::core::fmt::Display::fmt(&self.#ident, f)?;
};
out.extend(tokens);
}
fn fmt_debug_tokens(ident: &TokenStream, out: &mut TokenStream) {
let tokens = quote! {
::core::fmt::Debug::fmt(&self.#ident, f)?;
};
out.extend(tokens);
}
fn hex_encode_tokens(ident: &TokenStream, out: &mut TokenStream, hex_encode: &TokenStream) {
let tokens = quote! {
::core::fmt::Write::write_str(f, "0x")?;
::core::fmt::Write::write_str(f, #hex_encode(&self.#ident).as_str())?;
};
out.extend(tokens);
}

View File

@ -21,12 +21,17 @@ pub fn selector(selector: Selector) -> TokenStream {
quote! {[#( #bytes ),*]} quote! {[#( #bytes ),*]}
} }
/// Parses an int type from its string representation /// Parses an int / hash type from its string representation
pub fn parse_int_param_type(s: &str) -> Option<ParamType> { pub fn parse_param_type(s: &str) -> Option<ParamType> {
match s.chars().next() { match s.chars().next() {
Some(c @ 'u') | Some(c @ 'i') => { Some('H' | 'h') => {
let size = s[1..].parse::<usize>().ok()? / 8;
Some(ParamType::FixedBytes(size))
}
Some(c @ 'U' | c @ 'I' | c @ 'u' | c @ 'i') => {
let size = s[1..].parse::<usize>().ok()?; let size = s[1..].parse::<usize>().ok()?;
if c == 'u' { if matches!(c, 'U' | 'u') {
Some(ParamType::Uint(size)) Some(ParamType::Uint(size))
} else { } else {
Some(ParamType::Int(size)) Some(ParamType::Int(size))
@ -100,57 +105,62 @@ pub fn param_type_quote(kind: &ParamType) -> TokenStream {
/// given type /// given type
pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> { pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
match ty { match ty {
Type::Array(ty) => { Type::Array(arr) => {
let param = find_parameter_type(ty.elem.as_ref())?; let ty = find_parameter_type(&arr.elem)?;
if let Expr::Lit(ref expr) = ty.len { if let Expr::Lit(ref expr) = arr.len {
if let Lit::Int(ref len) = expr.lit { if let Lit::Int(ref len) = expr.lit {
if let Ok(size) = len.base10_parse::<usize>() { if let Ok(size) = len.base10_parse::<usize>() {
return Ok(ParamType::FixedArray(Box::new(param), size)) return Ok(ParamType::FixedArray(Box::new(ty), size))
} }
} }
} }
Err(Error::new(ty.span(), "Failed to derive proper ABI from array field")) Err(Error::new(arr.span(), "Failed to derive proper ABI from array field"))
} }
Type::Path(ty) => { Type::Path(ty) => {
// check for `Vec` // check for `Vec`
if ty.path.segments.len() == 1 && ty.path.segments[0].ident == "Vec" { if let Some(segment) = ty.path.segments.iter().find(|s| s.ident == "Vec") {
if let PathArguments::AngleBracketed(ref args) = ty.path.segments[0].arguments { if let PathArguments::AngleBracketed(ref args) = segment.arguments {
if args.args.len() == 1 { // Vec<T, A?>
if let GenericArgument::Type(ref ty) = args.args.iter().next().unwrap() { debug_assert!(matches!(args.args.len(), 1 | 2));
return find_parameter_type(ty) let ty = args.args.iter().next().unwrap();
.map(|kind| ParamType::Array(Box::new(kind))) if let GenericArgument::Type(ref ty) = ty {
} return find_parameter_type(ty).map(|kind| ParamType::Array(Box::new(kind)))
} }
} }
} }
let mut ident = ty.path.get_ident();
if ident.is_none() { // match on the last segment of the path
ident = ty.path.segments.last().map(|s| &s.ident); ty.path
} .get_ident()
if let Some(ident) = ident { .or_else(|| ty.path.segments.last().map(|s| &s.ident))
let ident = ident.to_string().to_lowercase(); .and_then(|ident| {
return match ident.as_str() { match ident.to_string().as_str() {
"address" => Ok(ParamType::Address), // eth types
"bytes" => Ok(ParamType::Bytes), "Address" => Some(ParamType::Address),
"string" => Ok(ParamType::String), "Bytes" => Some(ParamType::Bytes),
"bool" => Ok(ParamType::Bool), "Uint8" => Some(ParamType::Uint(8)),
"int" | "uint" => Ok(ParamType::Uint(256)),
"h160" => Ok(ParamType::FixedBytes(20)), // core types
"h256" | "secret" | "hash" => Ok(ParamType::FixedBytes(32)), "String" => Some(ParamType::String),
"h512" | "public" => Ok(ParamType::FixedBytes(64)), "bool" => Some(ParamType::Bool),
s => parse_int_param_type(s).ok_or_else(|| { // usize / isize, shouldn't happen but use max width
Error::new(ty.span(), "Failed to derive proper ABI from fields") "usize" => Some(ParamType::Uint(64)),
}), "isize" => Some(ParamType::Int(64)),
}
} s => parse_param_type(s),
Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")) }
})
.ok_or_else(|| Error::new(ty.span(), "Failed to derive proper ABI from fields"))
} }
Type::Tuple(ty) => ty Type::Tuple(ty) => ty
.elems .elems
.iter() .iter()
.map(find_parameter_type) .map(find_parameter_type)
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
.map(ParamType::Tuple), .map(ParamType::Tuple),
_ => Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")), _ => Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")),
} }
} }
@ -267,3 +277,97 @@ pub fn abi_parameters_array(input: &DeriveInput, trait_name: &str) -> Result<Tok
[#( #iter ),*] [#( #iter ),*]
}) })
} }
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
macro_rules! type_test_cases {
($($t:ty => $e:expr),+ $(,)?) => {{
&[
$(
(parse_quote!($t), $e),
)+
]
}};
}
fn arr(ty: ParamType) -> ParamType {
ParamType::Array(Box::new(ty))
}
fn farr(ty: ParamType, len: usize) -> ParamType {
ParamType::FixedArray(Box::new(ty), len)
}
#[test]
fn can_find_params() {
use ParamType as PT;
let test_cases: &[(Type, ParamType)] = type_test_cases! {
u8 => PT::Uint(8),
u16 => PT::Uint(16),
u32 => PT::Uint(32),
u64 => PT::Uint(64),
usize => PT::Uint(64),
u128 => PT::Uint(128),
::ethers::types::U256 => PT::Uint(256),
ethers::types::U256 => PT::Uint(256),
::ethers_core::types::U256 => PT::Uint(256),
ethers_core::types::U256 => PT::Uint(256),
U256 => PT::Uint(256),
i8 => PT::Int(8),
i16 => PT::Int(16),
i32 => PT::Int(32),
i64 => PT::Int(64),
isize => PT::Int(64),
i128 => PT::Int(128),
::ethers::types::I256 => PT::Int(256),
ethers::types::I256 => PT::Int(256),
::ethers_core::types::I256 => PT::Int(256),
ethers_core::types::I256 => PT::Int(256),
I256 => PT::Int(256),
::ethers::types::H160 => PT::FixedBytes(20),
H160 => PT::FixedBytes(20),
::ethers::types::H256 => PT::FixedBytes(32),
H256 => PT::FixedBytes(32),
::ethers::types::H512 => PT::FixedBytes(64),
H512 => PT::FixedBytes(64),
::std::vec::Vec<::ethers_core::types::U256, ::std::alloc::Global> => arr(PT::Uint(256)),
::std::vec::Vec<::ethers_core::types::U256, Global> => arr(PT::Uint(256)),
::std::vec::Vec<::ethers_core::types::U256> => arr(PT::Uint(256)),
::std::vec::Vec<ethers::types::U256> => arr(PT::Uint(256)),
::std::vec::Vec<U256> => arr(PT::Uint(256)),
std::vec::Vec<U256> => arr(PT::Uint(256)),
vec::Vec<U256> => arr(PT::Uint(256)),
Vec<U256> => arr(PT::Uint(256)),
[u64; 8] => farr(PT::Uint(64), 8),
[u64; 16] => farr(PT::Uint(64), 16),
[::ethers_core::types::U256; 2] => farr(PT::Uint(256), 2),
[String; 4] => farr(PT::String, 4),
[Address; 2] => farr(PT::Address, 2),
(String, String, Address) => PT::Tuple(vec![PT::String, PT::String, PT::Address]),
(::ethers_core::types::U256, u8, ::ethers_core::types::Address)
=> PT::Tuple(vec![PT::Uint(256), PT::Uint(8), PT::Address]),
(::ethers::types::Bytes, ::ethers::types::H256, (::ethers::types::Address, ::std::string::String))
=> PT::Tuple(vec![
PT::Bytes,
PT::FixedBytes(32),
PT::Tuple(vec![PT::Address, PT::String])
]),
};
for (ty, expected) in test_cases {
match find_parameter_type(ty) {
Ok(ty) => assert_eq!(ty, *expected),
Err(e) => panic!("{e}: {ty:#?}\n{expected}"),
}
}
}
}

View File

@ -1,26 +1,25 @@
#![cfg(feature = "abigen")]
#![allow(unused)]
//! Test cases to validate the `abigen!` macro //! Test cases to validate the `abigen!` macro
use ethers_contract::{abigen, Abigen, EthCall, EthEvent};
use ethers_contract::{abigen, EthCall, EthEvent};
use ethers_core::{ use ethers_core::{
abi::{AbiDecode, AbiEncode, Address, Tokenizable}, abi::{AbiDecode, AbiEncode, Address, Tokenizable},
types::{transaction::eip2718::TypedTransaction, Chain, Eip1559TransactionRequest, U256}, types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, U256},
utils::Anvil, utils::Anvil,
}; };
use ethers_providers::{MockProvider, Provider}; use ethers_providers::{MockProvider, Provider};
use ethers_solc::Solc; use ethers_solc::Solc;
use std::{ use std::sync::Arc;
convert::{TryFrom, TryInto},
sync::Arc,
};
fn assert_codec<T: AbiDecode + AbiEncode>() {} const fn assert_codec<T: AbiDecode + AbiEncode>() {}
fn assert_tokenizeable<T: Tokenizable>() {} const fn assert_tokenizeable<T: Tokenizable>() {}
fn assert_call<T: AbiEncode + AbiDecode + Default + Tokenizable>() {} const fn assert_call<T: AbiEncode + AbiDecode + Default + Tokenizable>() {}
fn assert_event<T: EthEvent>() {} const fn assert_event<T: EthEvent>() {}
const fn assert_clone<T: Clone>() {}
const fn assert_default<T: Default>() {}
const fn assert_builtin<T: std::fmt::Debug + PartialEq + Eq + std::hash::Hash>() {}
#[test] #[test]
fn can_gen_human_readable() { fn can_generate_human_readable() {
abigen!( abigen!(
SimpleContract, SimpleContract,
r#"[ r#"[
@ -33,12 +32,12 @@ fn can_gen_human_readable() {
} }
#[test] #[test]
fn can_gen_not_human_readable() { fn can_generate_not_human_readable() {
abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json"); abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json");
} }
#[test] #[test]
fn can_gen_human_readable_multiple() { fn can_generate_human_readable_multiple() {
abigen!( abigen!(
SimpleContract1, SimpleContract1,
r#"[ r#"[
@ -59,7 +58,7 @@ fn can_gen_human_readable_multiple() {
} }
#[test] #[test]
fn can_gen_structs_readable() { fn can_generate_structs_readable() {
abigen!( abigen!(
SimpleContract, SimpleContract,
r#"[ r#"[
@ -90,7 +89,7 @@ fn can_gen_structs_readable() {
} }
#[test] #[test]
fn can_gen_structs_with_arrays_readable() { fn can_generate_structs_with_arrays_readable() {
abigen!( abigen!(
SimpleContract, SimpleContract,
r#"[ r#"[
@ -171,7 +170,7 @@ fn can_generate_internal_structs_multiple() {
} }
#[test] #[test]
fn can_gen_return_struct() { fn can_generate_return_struct() {
abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json"); abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json");
fn verify<T: AbiEncode + AbiDecode + Clone + std::fmt::Debug + std::cmp::PartialEq>( fn verify<T: AbiEncode + AbiDecode + Clone + std::fmt::Debug + std::cmp::PartialEq>(
@ -199,7 +198,7 @@ fn can_gen_return_struct() {
} }
#[test] #[test]
fn can_gen_human_readable_with_structs() { fn can_generate_human_readable_with_structs() {
abigen!( abigen!(
SimpleContract, SimpleContract,
r#"[ r#"[
@ -455,7 +454,7 @@ fn can_handle_duplicates_with_same_name() {
} }
#[test] #[test]
fn can_abigen_console_sol() { fn can_abican_generate_console_sol() {
abigen!(Console, "ethers-contract/tests/solidity-contracts/console.json",); abigen!(Console, "ethers-contract/tests/solidity-contracts/console.json",);
} }
@ -569,7 +568,7 @@ async fn can_abiencoderv2_output() {
// NOTE: this is commented out because this would result in compiler errors if key not set or // NOTE: this is commented out because this would result in compiler errors if key not set or
// etherscan API not working #[test] // etherscan API not working #[test]
// fn can_gen_multi_etherscan() { // fn can_generate_multi_etherscan() {
// abigen!( // abigen!(
// MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7"; // MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7";
// MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2"; // MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2";
@ -581,7 +580,7 @@ async fn can_abiencoderv2_output() {
// } // }
#[test] #[test]
fn can_gen_reserved_word_field_names() { fn can_generate_reserved_word_field_names() {
abigen!( abigen!(
Test, Test,
r#"[ r#"[
@ -636,8 +635,8 @@ async fn can_send_struct_param() {
} }
#[test] #[test]
fn can_gen_seaport() { fn can_generate_seaport_1_0() {
abigen!(Seaport, "./tests/solidity-contracts/seaport.json"); abigen!(Seaport, "./tests/solidity-contracts/seaport_1_0.json");
assert_eq!( assert_eq!(
FulfillAdvancedOrderCall::abi_signature(), FulfillAdvancedOrderCall::abi_signature(),
@ -651,13 +650,58 @@ fn can_gen_seaport() {
let encoded = err.clone().encode(); let encoded = err.clone().encode();
assert_eq!(err, SeaportErrors::decode(encoded).unwrap()); assert_eq!(err, SeaportErrors::decode(encoded).unwrap());
let err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet { let _err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet {
order_index: U256::zero(), order_index: U256::zero(),
consideration_index: U256::zero(), consideration_index: U256::zero(),
shortfall_amount: U256::zero(), shortfall_amount: U256::zero(),
}); });
} }
#[test]
fn can_generate_seaport_gt1_0() {
mod v1_1 {
use super::*;
abigen!(Seaport1, "./tests/solidity-contracts/seaport_1_1.json");
}
// (address,uint256,uint256,address,address,address,uint256
mod v1_2 {
use super::*;
abigen!(Seaport2, "./tests/solidity-contracts/seaport_1_2.json");
}
mod v1_3 {
use super::*;
abigen!(Seaport3, "./tests/solidity-contracts/seaport_1_3.json");
}
// tuples len <= 12
assert_clone::<v1_1::FulfillAdvancedOrderCall>();
assert_default::<v1_1::FulfillAdvancedOrderCall>();
assert_builtin::<v1_1::FulfillAdvancedOrderCall>();
assert_clone::<v1_2::FulfillAdvancedOrderCall>();
assert_default::<v1_2::FulfillAdvancedOrderCall>();
assert_builtin::<v1_2::FulfillAdvancedOrderCall>();
assert_clone::<v1_3::FulfillAdvancedOrderCall>();
assert_default::<v1_3::FulfillAdvancedOrderCall>();
assert_builtin::<v1_3::FulfillAdvancedOrderCall>();
// tuples len > 12
assert_clone::<v1_1::FulfillBasicOrderCall>();
// assert_default::<v1_1::FulfillBasicOrderCall>();
// assert_builtin::<v1_1::FulfillBasicOrderCall>();
assert_clone::<v1_2::FulfillBasicOrderCall>();
// assert_default::<v1_2::FulfillBasicOrderCall>();
// assert_builtin::<v1_2::FulfillBasicOrderCall>();
assert_clone::<v1_3::FulfillBasicOrderCall>();
// assert_default::<v1_3::FulfillBasicOrderCall>();
// assert_builtin::<v1_3::FulfillBasicOrderCall>();
}
#[test] #[test]
fn can_generate_to_string_overload() { fn can_generate_to_string_overload() {
abigen!( abigen!(
@ -691,11 +735,11 @@ fn can_generate_large_event() {
fn can_generate_large_output_struct() { fn can_generate_large_output_struct() {
abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json"); abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json");
let r = GetByIdReturn(Info::default()); let _r = GetByIdReturn(Info::default());
} }
#[test] #[test]
fn gen_complex_function() { fn can_generate_complex_function() {
abigen!( abigen!(
WyvernExchangeV1, WyvernExchangeV1,
r#"[ r#"[
@ -705,13 +749,13 @@ fn gen_complex_function() {
} }
#[test] #[test]
fn can_gen_large_tuple_types() { fn can_generate_large_tuple_types() {
abigen!(LargeTuple, "./tests/solidity-contracts/large_tuple.json"); abigen!(LargeTuple, "./tests/solidity-contracts/large_tuple.json");
} }
#[test] #[test]
fn can_gen_large_tuple_array() { fn can_generate_large_tuple_array() {
abigen!(LargeTuple, "./tests/solidity-contracts/large-array.json"); abigen!(LargeArray, "./tests/solidity-contracts/large-array.json");
impl Default for CallWithLongArrayCall { impl Default for CallWithLongArrayCall {
fn default() -> Self { fn default() -> Self {
@ -743,11 +787,11 @@ fn can_handle_overloaded_function_with_array() {
abigen!( abigen!(
Test, Test,
r#"[ r#"[
serializeString(string calldata, string calldata, string calldata) external returns (string memory) serializeString(string calldata, string calldata, string calldata) external returns (string memory)
serializeString(string calldata, string calldata, string[] calldata) external returns (string memory) serializeString(string calldata, string calldata, string[] calldata) external returns (string memory)
serializeBool(string calldata, string calldata, bool) external returns (string memory) serializeBool(string calldata, string calldata, bool) external returns (string memory)
serializeBool(string calldata, string calldata, bool[] calldata) external returns (string memory) serializeBool(string calldata, string calldata, bool[] calldata) external returns (string memory)
]"#, ]"#,
); );
} }
@ -767,7 +811,7 @@ fn convert_uses_correct_abi() {
// Ensure that `bar` is using the `Bar` ABI internally (this method lookup will panic if `bar` // Ensure that `bar` is using the `Bar` ABI internally (this method lookup will panic if `bar`
// is incorrectly using the `Foo` ABI internally). // is incorrectly using the `Foo` ABI internally).
bar.bar().call(); drop(bar.bar().call());
} }
#[test] #[test]
@ -779,3 +823,11 @@ fn generates_non_zero_bytecode() {
//sanity check that the bytecode is not the same //sanity check that the bytecode is not the same
assert_ne!(GREETER_BYTECODE, GREETER_DEPLOYED_BYTECODE); assert_ne!(GREETER_BYTECODE, GREETER_DEPLOYED_BYTECODE);
} }
#[test]
fn can_generate_hardhat_console() {
abigen!(HardhatConsole, "./tests/solidity-contracts/console.json");
fn exists<T>() {}
exists::<HardhatConsoleCalls>();
}

View File

@ -1,4 +1,5 @@
#![cfg(not(target_arch = "wasm32"))] #![cfg(not(target_arch = "wasm32"))]
#![allow(dead_code)]
#[cfg(feature = "abigen")] #[cfg(feature = "abigen")]
use ethers_core::types::Address; use ethers_core::types::Address;

View File

@ -1,5 +0,0 @@
//! ensure console.sol can be generated via abigen!
ethers_contract::abigen!(HardhatConsole, "./tests/solidity-contracts/console.json",);
fn assert_console_calls(_: &hardhat_console::HardhatConsoleCalls) {}

View File

@ -1,4 +1,5 @@
#![allow(unused)] #![allow(unused)]
pub use crate::common::*; pub use crate::common::*;
use ethers_contract::{abigen, ContractFactory, EthAbiType}; use ethers_contract::{abigen, ContractFactory, EthAbiType};
use ethers_core::types::{Filter, ValueOrArray, H256}; use ethers_core::types::{Filter, ValueOrArray, H256};

View File

@ -13,9 +13,9 @@ async fn contract_call_into_future_is_send() {
let client = Arc::new(provider); let client = Arc::new(provider);
let contract = DsProxyFactory::new(Address::zero(), client); let contract = DsProxyFactory::new(Address::zero(), client);
fn is_send<T: Future + Send + 'static>(future: T) -> bool { fn is_send<T: Future + Send + 'static>(future: T) -> T {
true future
} }
assert!(is_send(contract.cache().into_future())); is_send(contract.cache().into_future());
} }

View File

@ -1,11 +1,10 @@
#![allow(unused)] #![allow(clippy::extra_unused_type_parameters)]
#[cfg(feature = "abigen")]
mod abigen; mod abigen;
pub(crate) mod common; pub(crate) mod common;
#[cfg(feature = "abigen")]
mod console;
#[cfg(feature = "abigen")] #[cfg(feature = "abigen")]
mod contract; mod contract;
mod contract_call;
fn main() {} mod contract_call;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -33,13 +33,24 @@ pub enum FieldType {
/// ///
/// Note: tuples will be treated as rust tuples /// Note: tuples will be treated as rust tuples
Elementary(ParamType), Elementary(ParamType),
/// A non elementary type field, treated as user defined struct /// A non elementary type field, treated as user-defined struct
Struct(StructFieldType), Struct(StructFieldType),
/// Mapping /// Mapping
Mapping(Box<MappingType>), Mapping(Box<MappingType>),
} }
impl FieldType { impl FieldType {
/// Whether this field is an elementary [`ParamType`].
pub fn is_elementary(&self) -> bool {
matches!(self, FieldType::Elementary(_))
}
/// Whether this field is a user-defined struct.
pub fn is_struct(&self) -> bool {
matches!(self, FieldType::Struct(_))
}
/// Whether this field is a mapping.
pub fn is_mapping(&self) -> bool { pub fn is_mapping(&self) -> bool {
matches!(self, FieldType::Mapping(_)) matches!(self, FieldType::Mapping(_))
} }