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:
parent
c21362b696
commit
3732de844c
|
@ -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"
|
||||||
|
|
|
@ -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| ¶m.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| ¶m.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), )*
|
||||||
}
|
}
|
||||||
|
|
|
@ -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| ¶m.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| ¶m.kind).all(util::can_derive_default) {
|
let params = event.inputs.iter().map(|param| ¶m.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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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| ¶m.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| ¶m.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| ¶m.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), )*
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(¶m_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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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| ¶m.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(¶m));
|
let param = ParamType::FixedArray(Box::new(ParamType::Uint(256)), 32);
|
||||||
|
|
||||||
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 32);
|
|
||||||
assert!(can_derive_default(¶m));
|
assert!(can_derive_default(¶m));
|
||||||
|
assert!(can_derive_builtin_traits(¶m));
|
||||||
|
|
||||||
|
let param = ParamType::FixedArray(Box::new(ParamType::Uint(256)), 33);
|
||||||
|
assert!(!can_derive_default(¶m));
|
||||||
|
assert!(can_derive_builtin_traits(¶m));
|
||||||
|
|
||||||
|
// tuple
|
||||||
|
let param = ParamType::Tuple(vec![ParamType::Uint(256); 12]);
|
||||||
|
assert!(can_derive_default(¶m));
|
||||||
|
assert!(can_derive_builtin_traits(¶m));
|
||||||
|
|
||||||
|
let param = ParamType::Tuple(vec![ParamType::Uint(256); 13]);
|
||||||
|
assert!(!can_derive_default(¶m));
|
||||||
|
assert!(!can_derive_builtin_traits(¶m));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -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(¶m, 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);
|
||||||
|
}
|
||||||
|
|
|
@ -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}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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>();
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {}
|
|
|
@ -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};
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
File diff suppressed because one or more lines are too long
|
@ -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(_))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue