fix(abigen): only derive default of no arrays len > 32 (#1653)

* fix(abigen): only derive default of no arrays len > 32

* impl default
This commit is contained in:
Matthias Seitz 2022-08-31 17:24:21 +02:00 committed by GitHub
parent 13a0144aba
commit 430c56ee4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 9 deletions

View File

@ -1,8 +1,9 @@
use std::collections::{btree_map::Entry, BTreeMap, HashMap};
use super::{types, util, Context};
use crate::contract::common::{
expand_data_struct, expand_data_tuple, expand_param_type, expand_params,
use crate::{
contract::common::{expand_data_struct, expand_data_tuple, expand_param_type, expand_params},
util::can_derive_defaults,
};
use ethers_core::{
abi::{Function, FunctionExt, Param, ParamType},
@ -134,9 +135,20 @@ impl Context {
// use the same derives as for events
let derives = util::expand_derives(&self.event_derives);
// rust-std only derives default automatically for arrays len <= 32
// for large array types we skip derive(Default) <https://github.com/gakonst/ethers-rs/issues/1640>
let derive_default = if can_derive_defaults(&function.inputs) {
quote! {
#[derive(Default)]
}
} else {
quote! {}
};
Ok(quote! {
#abi_signature_doc
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthCall, #ethers_contract::EthDisplay, #derives)]
#[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthCall, #ethers_contract::EthDisplay, #derives)]
#derive_default
#[ethcall( name = #function_name, abi = #abi_signature )]
pub #call_type_definition
})
@ -175,9 +187,20 @@ impl Context {
// use the same derives as for events
let derives = util::expand_derives(&self.event_derives);
// rust-std only derives default automatically for arrays len <= 32
// for large array types we skip derive(Default) <https://github.com/gakonst/ethers-rs/issues/1640>
let derive_default = if can_derive_defaults(&function.outputs) {
quote! {
#[derive(Default)]
}
} else {
quote! {}
};
Ok(quote! {
#abi_signature_doc
#[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
#[derive(Clone, Debug,Eq, PartialEq, #ethers_contract::EthAbiType, #ethers_contract::EthAbiCodec, #derives)]
#derive_default
pub #return_type_definition
})
}

View File

@ -1,11 +1,12 @@
use ethers_core::types::Address;
use std::path::PathBuf;
use ethers_core::{
abi::{Param, ParamType},
types::Address,
};
use eyre::Result;
use inflector::Inflector;
use proc_macro2::{Ident, Literal, Span, TokenStream};
use quote::quote;
use std::path::PathBuf;
use syn::{Ident as SynIdent, Path};
/// Expands a identifier string into a token.
@ -185,10 +186,42 @@ pub fn json_files(root: impl AsRef<std::path::Path>) -> Vec<PathBuf> {
.collect()
}
/// rust-std derives `Default` automatically only for arrays len <= 32
///
/// Returns whether the corresponding struct can derive `Default`
pub fn can_derive_defaults(params: &[Param]) -> bool {
params.iter().map(|param| &param.kind).all(can_derive_default)
}
pub fn can_derive_default(param: &ParamType) -> bool {
const MAX_SUPPORTED_LEN: usize = 32;
match param {
ParamType::FixedBytes(len) => *len <= MAX_SUPPORTED_LEN,
ParamType::FixedArray(ty, len) => {
if *len > MAX_SUPPORTED_LEN {
false
} else {
can_derive_default(ty)
}
}
ParamType::Tuple(params) => params.iter().all(can_derive_default),
_ => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_detect_non_default() {
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 128);
assert!(!can_derive_default(&param));
let param = ParamType::FixedArray(Box::new(ParamType::Uint(64)), 32);
assert!(can_derive_default(&param));
}
#[test]
fn can_resolve_path() {
let raw = "./$ENV_VAR";

View File

@ -11,10 +11,14 @@ use ethers_middleware::SignerMiddleware;
use ethers_providers::{MockProvider, Provider};
use ethers_signers::{LocalWallet, Signer};
use ethers_solc::Solc;
use std::{convert::TryFrom, sync::Arc};
use std::{
convert::{TryFrom, TryInto},
sync::Arc,
};
fn assert_codec<T: AbiDecode + AbiEncode>() {}
fn assert_tokenizeable<T: Tokenizable>() {}
fn assert_call<T: AbiEncode + AbiDecode + Default + Tokenizable>() {}
#[test]
fn can_gen_human_readable() {
@ -237,6 +241,9 @@ fn can_gen_human_readable_with_structs() {
assert_eq!(contract_call, decoded_enum);
assert_eq!(contract_call, call.into());
assert_eq!(encoded_call, contract_call.encode());
assert_call::<BarCall>();
assert_call::<YeetCall>();
}
#[test]
@ -301,6 +308,10 @@ fn can_handle_overloaded_functions() {
let _contract_call = SimpleContractCalls::SetValue0(call);
let call = SetValue1Call("message".to_string(), "message".to_string());
let _contract_call = SimpleContractCalls::SetValue1(call);
assert_call::<SetValue0Call>();
assert_call::<SetValue1Call>();
assert_call::<GetValueWithOtherValueAndAddrCall>();
}
#[test]
@ -695,3 +706,17 @@ fn gen_complex_function() {
fn can_gen_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");
impl Default for CallWithLongArrayCall {
fn default() -> Self {
Self { long_array: [0; 128] }
}
}
let _call = CallWithLongArrayCall::default();
assert_call::<CallWithLongArrayCall>();
}

View File

@ -0,0 +1,15 @@
[
{
"inputs": [
{
"internalType": "uint64[128]",
"name": "longArray",
"type": "uint64[128]"
}
],
"name": "callWithLongArray",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
]