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]
|
||||
name = "ethers-contract"
|
||||
version = "1.0.2"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
rust-version = "1.64"
|
||||
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
|
|
@ -52,16 +52,15 @@ impl Context {
|
|||
hex::encode(error.selector())
|
||||
);
|
||||
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if util::can_derive_defaults(&error.inputs) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
let mut derives = self.expand_extra_derives();
|
||||
let params = error.inputs.iter().map(|param| ¶m.kind);
|
||||
util::derive_builtin_traits(params, &mut derives, true, true);
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[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)]
|
||||
pub #data_type_definition
|
||||
})
|
||||
|
@ -92,14 +91,17 @@ impl Context {
|
|||
})
|
||||
.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_contract = ethers_contract_crate();
|
||||
|
||||
quote! {
|
||||
#[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 {
|
||||
#( #variants(#variants), )*
|
||||
}
|
||||
|
|
|
@ -62,15 +62,19 @@ impl Context {
|
|||
})
|
||||
.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_contract = ethers_contract_crate();
|
||||
|
||||
let extra_derives = self.expand_extra_derives();
|
||||
let enum_name = self.expand_event_enum_name();
|
||||
|
||||
quote! {
|
||||
#[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 {
|
||||
#( #variants(#variants), )*
|
||||
}
|
||||
|
@ -191,15 +195,14 @@ impl Context {
|
|||
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 mut extra_derives = self.expand_extra_derives();
|
||||
if event.inputs.iter().map(|param| ¶m.kind).all(util::can_derive_default) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
let mut derives = self.expand_extra_derives();
|
||||
let params = event.inputs.iter().map(|param| ¶m.kind);
|
||||
util::derive_builtin_traits(params, &mut derives, true, true);
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
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)]
|
||||
pub #data_type_definition
|
||||
})
|
||||
|
|
|
@ -122,16 +122,15 @@ impl Context {
|
|||
hex::encode(function.selector())
|
||||
);
|
||||
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if util::can_derive_defaults(&function.inputs) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
let mut derives = self.expand_extra_derives();
|
||||
let params = function.inputs.iter().map(|param| ¶m.kind);
|
||||
util::derive_builtin_traits(params, &mut derives, true, true);
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[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 )]
|
||||
pub #call_type_definition
|
||||
})
|
||||
|
@ -162,16 +161,15 @@ impl Context {
|
|||
hex::encode(function.selector())
|
||||
);
|
||||
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if util::can_derive_defaults(&function.inputs) {
|
||||
extra_derives.extend(quote!(Default));
|
||||
}
|
||||
let mut derives = self.expand_extra_derives();
|
||||
let params = function.inputs.iter().map(|param| ¶m.kind);
|
||||
util::derive_builtin_traits(params, &mut derives, true, true);
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(Some(quote! {
|
||||
#[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
|
||||
}))
|
||||
}
|
||||
|
@ -197,7 +195,10 @@ impl Context {
|
|||
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();
|
||||
|
||||
|
@ -208,7 +209,7 @@ impl Context {
|
|||
#struct_def_tokens
|
||||
|
||||
#[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 {
|
||||
#( #variant_names(#struct_names), )*
|
||||
}
|
||||
|
|
|
@ -61,9 +61,9 @@ impl Context {
|
|||
.internal_structs
|
||||
.struct_tuples
|
||||
.get(id)
|
||||
.ok_or_else(|| eyre!("No types found for {id}"))?
|
||||
.clone();
|
||||
self.expand_internal_struct(struct_name, sol_struct, tuple)
|
||||
.ok_or_else(|| eyre!("No types found for {id}"))?;
|
||||
let types = if let ParamType::Tuple(types) = tuple { types } else { unreachable!() };
|
||||
self.expand_internal_struct(struct_name, sol_struct, types)
|
||||
}
|
||||
|
||||
/// Returns the `TokenStream` with all the internal structs extracted form the JSON ABI
|
||||
|
@ -83,7 +83,7 @@ impl Context {
|
|||
&self,
|
||||
name: &str,
|
||||
sol_struct: &SolStruct,
|
||||
tuple: ParamType,
|
||||
types: &[ParamType],
|
||||
) -> Result<TokenStream> {
|
||||
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 sig = match tuple {
|
||||
ParamType::Tuple(ref types) if !types.is_empty() => util::abi_signature_types(types),
|
||||
_ => String::new(),
|
||||
};
|
||||
let sig = util::abi_signature_types(types);
|
||||
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();
|
||||
|
||||
Ok(quote! {
|
||||
#[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
|
||||
})
|
||||
}
|
||||
|
@ -167,16 +165,14 @@ impl Context {
|
|||
|
||||
let name = util::ident(name);
|
||||
|
||||
let mut extra_derives = self.expand_extra_derives();
|
||||
if param_types.iter().all(util::can_derive_default) {
|
||||
extra_derives.extend(quote!(Default))
|
||||
}
|
||||
let mut derives = self.expand_extra_derives();
|
||||
util::derive_builtin_traits(¶m_types, &mut derives, true, true);
|
||||
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
||||
Ok(quote! {
|
||||
#[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 {
|
||||
#( #fields ),*
|
||||
}
|
||||
|
@ -231,7 +227,7 @@ impl InternalStructs {
|
|||
let mut function_params = HashMap::new();
|
||||
let mut outputs = HashMap::new();
|
||||
let mut event_params = HashMap::new();
|
||||
let mut structs = HashMap::new();
|
||||
|
||||
for item in abi
|
||||
.into_iter()
|
||||
.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
|
||||
// into a struct will create all structs
|
||||
let mut structs = HashMap::new();
|
||||
for component in top_level_internal_types.values() {
|
||||
insert_structs(&mut structs, component);
|
||||
}
|
||||
|
@ -569,7 +566,7 @@ fn struct_type_name(name: &str) -> &str {
|
|||
struct_type_identifier(name).rsplit('.').next().unwrap()
|
||||
}
|
||||
|
||||
/// `Pairing.G2Point` -> `Pairing.G2Point`
|
||||
/// `struct Pairing.G2Point[]` -> `Pairing.G2Point`
|
||||
fn struct_type_identifier(name: &str) -> &str {
|
||||
name.trim_start_matches("struct ").split('[').next().unwrap()
|
||||
}
|
||||
|
|
|
@ -1342,7 +1342,7 @@ contract Enum {
|
|||
[package]
|
||||
name = "ethers-contract"
|
||||
version = "1.0.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
rust-version = "1.64"
|
||||
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -1388,7 +1388,7 @@ contract Enum {
|
|||
[package]
|
||||
name = "ethers-contract"
|
||||
version = "1.0.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
rust-version = "1.64"
|
||||
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -1435,7 +1435,7 @@ contract Enum {
|
|||
[package]
|
||||
name = "ethers-contract"
|
||||
version = "1.0.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
rust-version = "1.64"
|
||||
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -1482,7 +1482,7 @@ contract Enum {
|
|||
[package]
|
||||
name = "ethers-contract"
|
||||
version = "1.0.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
rust-version = "1.64"
|
||||
authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
|
||||
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 inflector::Inflector;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
|
@ -165,28 +169,158 @@ pub(crate) fn json_files(root: impl AsRef<Path>) -> Vec<PathBuf> {
|
|||
.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
|
||||
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)
|
||||
/// The following traits are only implemented on tuples of arity 12 or less:
|
||||
///
|
||||
/// - [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`].
|
||||
///
|
||||
/// rust-std derives `Default` automatically only for arrays len <= 32
|
||||
pub(crate) fn can_derive_default(param: &ParamType) -> bool {
|
||||
const MAX_SUPPORTED_LEN: usize = 32;
|
||||
/// 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.
|
||||
pub(crate) fn derive_builtin_traits_struct(
|
||||
structs: &InternalStructs,
|
||||
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 {
|
||||
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) => {
|
||||
if *len > MAX_SUPPORTED_LEN {
|
||||
if *len > MAX_SUPPORTED_ARRAY_LEN {
|
||||
false
|
||||
} else {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -211,12 +345,24 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn can_detect_non_default() {
|
||||
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 128);
|
||||
assert!(!can_derive_default(¶m));
|
||||
|
||||
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 32);
|
||||
fn can_detect_derives() {
|
||||
// array
|
||||
let param = ParamType::FixedArray(Box::new(ParamType::Uint(256)), 32);
|
||||
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]
|
||||
|
|
|
@ -22,82 +22,117 @@ pub(crate) fn derive_eth_display_impl(input: DeriveInput) -> Result<TokenStream,
|
|||
}
|
||||
};
|
||||
|
||||
let ethers_core = ethers_core_crate();
|
||||
let hex_encode = quote! {#ethers_core::utils::hex::encode};
|
||||
|
||||
let mut fmts = TokenStream::new();
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
let ident = field.ident.clone().map(|id| quote! {#id}).unwrap_or_else(|| {
|
||||
let idx = Index::from(idx);
|
||||
quote! {#idx}
|
||||
let mut expressions = TokenStream::new();
|
||||
for (i, field) in fields.iter().enumerate() {
|
||||
let ident = field.ident.as_ref().map(|id| quote!(#id)).unwrap_or_else(|| {
|
||||
let idx = Index::from(i);
|
||||
quote!(#idx)
|
||||
});
|
||||
let tokens = if let Ok(param) = utils::find_parameter_type(&field.ty) {
|
||||
match param {
|
||||
ParamType::Address | ParamType::Uint(_) | ParamType::Int(_) => {
|
||||
quote! {
|
||||
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))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(param) = utils::find_parameter_type(&field.ty) {
|
||||
let ethers_core = ethers_core_crate();
|
||||
let hex_encode = quote!(#ethers_core::utils::hex::encode);
|
||||
fmt_params_tokens(¶m, ident, &mut expressions, &hex_encode);
|
||||
} else {
|
||||
// could not detect the parameter type and rely on using debug fmt
|
||||
quote! {
|
||||
write!(f, "{:?}", &self.#ident)?;
|
||||
fmt_debug_tokens(&ident, &mut expressions);
|
||||
}
|
||||
|
||||
// comma separator
|
||||
if i < fields.len() - 1 {
|
||||
let tokens = quote! {
|
||||
::core::fmt::Write::write_str(f, ", ")?;
|
||||
};
|
||||
fmts.extend(tokens);
|
||||
if idx < fields.len() - 1 {
|
||||
fmts.extend(quote! { write!(f, ", ")?;});
|
||||
expressions.extend(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
let name = &input.ident;
|
||||
Ok(quote! {
|
||||
impl ::std::fmt::Display for #name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
#fmts
|
||||
impl ::core::fmt::Display for #name {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
|
||||
#expressions
|
||||
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 ),*]}
|
||||
}
|
||||
|
||||
/// Parses an int type from its string representation
|
||||
pub fn parse_int_param_type(s: &str) -> Option<ParamType> {
|
||||
/// Parses an int / hash type from its string representation
|
||||
pub fn parse_param_type(s: &str) -> Option<ParamType> {
|
||||
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()?;
|
||||
if c == 'u' {
|
||||
if matches!(c, 'U' | 'u') {
|
||||
Some(ParamType::Uint(size))
|
||||
} else {
|
||||
Some(ParamType::Int(size))
|
||||
|
@ -100,57 +105,62 @@ pub fn param_type_quote(kind: &ParamType) -> TokenStream {
|
|||
/// given type
|
||||
pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
||||
match ty {
|
||||
Type::Array(ty) => {
|
||||
let param = find_parameter_type(ty.elem.as_ref())?;
|
||||
if let Expr::Lit(ref expr) = ty.len {
|
||||
Type::Array(arr) => {
|
||||
let ty = find_parameter_type(&arr.elem)?;
|
||||
if let Expr::Lit(ref expr) = arr.len {
|
||||
if let Lit::Int(ref len) = expr.lit {
|
||||
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) => {
|
||||
// check for `Vec`
|
||||
if ty.path.segments.len() == 1 && ty.path.segments[0].ident == "Vec" {
|
||||
if let PathArguments::AngleBracketed(ref args) = ty.path.segments[0].arguments {
|
||||
if args.args.len() == 1 {
|
||||
if let GenericArgument::Type(ref ty) = args.args.iter().next().unwrap() {
|
||||
return find_parameter_type(ty)
|
||||
.map(|kind| ParamType::Array(Box::new(kind)))
|
||||
if let Some(segment) = ty.path.segments.iter().find(|s| s.ident == "Vec") {
|
||||
if let PathArguments::AngleBracketed(ref args) = segment.arguments {
|
||||
// Vec<T, A?>
|
||||
debug_assert!(matches!(args.args.len(), 1 | 2));
|
||||
let ty = args.args.iter().next().unwrap();
|
||||
if let GenericArgument::Type(ref ty) = ty {
|
||||
return find_parameter_type(ty).map(|kind| ParamType::Array(Box::new(kind)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// match on the last segment of the path
|
||||
ty.path
|
||||
.get_ident()
|
||||
.or_else(|| ty.path.segments.last().map(|s| &s.ident))
|
||||
.and_then(|ident| {
|
||||
match ident.to_string().as_str() {
|
||||
// eth types
|
||||
"Address" => Some(ParamType::Address),
|
||||
"Bytes" => Some(ParamType::Bytes),
|
||||
"Uint8" => Some(ParamType::Uint(8)),
|
||||
|
||||
// core types
|
||||
"String" => Some(ParamType::String),
|
||||
"bool" => Some(ParamType::Bool),
|
||||
// usize / isize, shouldn't happen but use max width
|
||||
"usize" => Some(ParamType::Uint(64)),
|
||||
"isize" => Some(ParamType::Int(64)),
|
||||
|
||||
s => parse_param_type(s),
|
||||
}
|
||||
let mut ident = ty.path.get_ident();
|
||||
if ident.is_none() {
|
||||
ident = ty.path.segments.last().map(|s| &s.ident);
|
||||
}
|
||||
if let Some(ident) = ident {
|
||||
let ident = ident.to_string().to_lowercase();
|
||||
return match ident.as_str() {
|
||||
"address" => Ok(ParamType::Address),
|
||||
"bytes" => Ok(ParamType::Bytes),
|
||||
"string" => Ok(ParamType::String),
|
||||
"bool" => Ok(ParamType::Bool),
|
||||
"int" | "uint" => Ok(ParamType::Uint(256)),
|
||||
"h160" => Ok(ParamType::FixedBytes(20)),
|
||||
"h256" | "secret" | "hash" => Ok(ParamType::FixedBytes(32)),
|
||||
"h512" | "public" => Ok(ParamType::FixedBytes(64)),
|
||||
s => parse_int_param_type(s).ok_or_else(|| {
|
||||
Error::new(ty.span(), "Failed to derive proper ABI from fields")
|
||||
}),
|
||||
}
|
||||
}
|
||||
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
|
||||
.elems
|
||||
.iter()
|
||||
.map(find_parameter_type)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map(ParamType::Tuple),
|
||||
|
||||
_ => 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 ),*]
|
||||
})
|
||||
}
|
||||
|
||||
#[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
|
||||
use ethers_contract::{abigen, Abigen, EthCall, EthEvent};
|
||||
|
||||
use ethers_contract::{abigen, EthCall, EthEvent};
|
||||
use ethers_core::{
|
||||
abi::{AbiDecode, AbiEncode, Address, Tokenizable},
|
||||
types::{transaction::eip2718::TypedTransaction, Chain, Eip1559TransactionRequest, U256},
|
||||
types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, U256},
|
||||
utils::Anvil,
|
||||
};
|
||||
use ethers_providers::{MockProvider, Provider};
|
||||
use ethers_solc::Solc;
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn assert_codec<T: AbiDecode + AbiEncode>() {}
|
||||
fn assert_tokenizeable<T: Tokenizable>() {}
|
||||
fn assert_call<T: AbiEncode + AbiDecode + Default + Tokenizable>() {}
|
||||
fn assert_event<T: EthEvent>() {}
|
||||
const fn assert_codec<T: AbiDecode + AbiEncode>() {}
|
||||
const fn assert_tokenizeable<T: Tokenizable>() {}
|
||||
const fn assert_call<T: AbiEncode + AbiDecode + Default + Tokenizable>() {}
|
||||
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]
|
||||
fn can_gen_human_readable() {
|
||||
fn can_generate_human_readable() {
|
||||
abigen!(
|
||||
SimpleContract,
|
||||
r#"[
|
||||
|
@ -33,12 +32,12 @@ fn can_gen_human_readable() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_not_human_readable() {
|
||||
fn can_generate_not_human_readable() {
|
||||
abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_human_readable_multiple() {
|
||||
fn can_generate_human_readable_multiple() {
|
||||
abigen!(
|
||||
SimpleContract1,
|
||||
r#"[
|
||||
|
@ -59,7 +58,7 @@ fn can_gen_human_readable_multiple() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_structs_readable() {
|
||||
fn can_generate_structs_readable() {
|
||||
abigen!(
|
||||
SimpleContract,
|
||||
r#"[
|
||||
|
@ -90,7 +89,7 @@ fn can_gen_structs_readable() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_structs_with_arrays_readable() {
|
||||
fn can_generate_structs_with_arrays_readable() {
|
||||
abigen!(
|
||||
SimpleContract,
|
||||
r#"[
|
||||
|
@ -171,7 +170,7 @@ fn can_generate_internal_structs_multiple() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_return_struct() {
|
||||
fn can_generate_return_struct() {
|
||||
abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json");
|
||||
|
||||
fn verify<T: AbiEncode + AbiDecode + Clone + std::fmt::Debug + std::cmp::PartialEq>(
|
||||
|
@ -199,7 +198,7 @@ fn can_gen_return_struct() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_human_readable_with_structs() {
|
||||
fn can_generate_human_readable_with_structs() {
|
||||
abigen!(
|
||||
SimpleContract,
|
||||
r#"[
|
||||
|
@ -455,7 +454,7 @@ fn can_handle_duplicates_with_same_name() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_abigen_console_sol() {
|
||||
fn can_abican_generate_console_sol() {
|
||||
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
|
||||
// etherscan API not working #[test]
|
||||
// fn can_gen_multi_etherscan() {
|
||||
// fn can_generate_multi_etherscan() {
|
||||
// abigen!(
|
||||
// MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7";
|
||||
// MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2";
|
||||
|
@ -581,7 +580,7 @@ async fn can_abiencoderv2_output() {
|
|||
// }
|
||||
|
||||
#[test]
|
||||
fn can_gen_reserved_word_field_names() {
|
||||
fn can_generate_reserved_word_field_names() {
|
||||
abigen!(
|
||||
Test,
|
||||
r#"[
|
||||
|
@ -636,8 +635,8 @@ async fn can_send_struct_param() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_seaport() {
|
||||
abigen!(Seaport, "./tests/solidity-contracts/seaport.json");
|
||||
fn can_generate_seaport_1_0() {
|
||||
abigen!(Seaport, "./tests/solidity-contracts/seaport_1_0.json");
|
||||
|
||||
assert_eq!(
|
||||
FulfillAdvancedOrderCall::abi_signature(),
|
||||
|
@ -651,13 +650,58 @@ fn can_gen_seaport() {
|
|||
let encoded = err.clone().encode();
|
||||
assert_eq!(err, SeaportErrors::decode(encoded).unwrap());
|
||||
|
||||
let err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet {
|
||||
let _err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet {
|
||||
order_index: U256::zero(),
|
||||
consideration_index: 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]
|
||||
fn can_generate_to_string_overload() {
|
||||
abigen!(
|
||||
|
@ -691,11 +735,11 @@ fn can_generate_large_event() {
|
|||
fn can_generate_large_output_struct() {
|
||||
abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json");
|
||||
|
||||
let r = GetByIdReturn(Info::default());
|
||||
let _r = GetByIdReturn(Info::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gen_complex_function() {
|
||||
fn can_generate_complex_function() {
|
||||
abigen!(
|
||||
WyvernExchangeV1,
|
||||
r#"[
|
||||
|
@ -705,13 +749,13 @@ fn gen_complex_function() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_large_tuple_types() {
|
||||
fn can_generate_large_tuple_types() {
|
||||
abigen!(LargeTuple, "./tests/solidity-contracts/large_tuple.json");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_gen_large_tuple_array() {
|
||||
abigen!(LargeTuple, "./tests/solidity-contracts/large-array.json");
|
||||
fn can_generate_large_tuple_array() {
|
||||
abigen!(LargeArray, "./tests/solidity-contracts/large-array.json");
|
||||
|
||||
impl Default for CallWithLongArrayCall {
|
||||
fn default() -> Self {
|
||||
|
@ -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`
|
||||
// is incorrectly using the `Foo` ABI internally).
|
||||
bar.bar().call();
|
||||
drop(bar.bar().call());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -779,3 +823,11 @@ fn generates_non_zero_bytecode() {
|
|||
//sanity check that the bytecode is not the same
|
||||
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"))]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[cfg(feature = "abigen")]
|
||||
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)]
|
||||
|
||||
pub use crate::common::*;
|
||||
use ethers_contract::{abigen, ContractFactory, EthAbiType};
|
||||
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 contract = DsProxyFactory::new(Address::zero(), client);
|
||||
|
||||
fn is_send<T: Future + Send + 'static>(future: T) -> bool {
|
||||
true
|
||||
fn is_send<T: Future + Send + 'static>(future: T) -> T {
|
||||
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;
|
||||
pub(crate) mod common;
|
||||
#[cfg(feature = "abigen")]
|
||||
mod console;
|
||||
|
||||
#[cfg(feature = "abigen")]
|
||||
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
|
@ -33,13 +33,24 @@ pub enum FieldType {
|
|||
///
|
||||
/// Note: tuples will be treated as rust tuples
|
||||
Elementary(ParamType),
|
||||
/// A non elementary type field, treated as user defined struct
|
||||
/// A non elementary type field, treated as user-defined struct
|
||||
Struct(StructFieldType),
|
||||
/// Mapping
|
||||
Mapping(Box<MappingType>),
|
||||
}
|
||||
|
||||
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 {
|
||||
matches!(self, FieldType::Mapping(_))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue