From 35056d0559f99b99ae258458742503aff1195563 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 6 Sep 2022 18:28:59 +0200 Subject: [PATCH 1/6] fix(abigen): handle event defaults --- .../src/contract/events.rs | 26 +- ethers-contract/tests/it/abigen.rs | 1398 +++++++++-------- .../solidity-contracts/EventWithStruct.json | 33 + 3 files changed, 762 insertions(+), 695 deletions(-) create mode 100644 ethers-contract/tests/solidity-contracts/EventWithStruct.json diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index 9c0986352..d688458f9 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -1,6 +1,7 @@ use super::{types, util, Context}; +use crate::util::can_derive_defaults; use ethers_core::{ - abi::{Event, EventExt, EventParam, ParamType, SolStruct}, + abi::{Event, EventExt, EventParam, Param, ParamType, SolStruct}, macros::{ethers_contract_crate, ethers_core_crate}, }; use eyre::Result; @@ -255,10 +256,31 @@ impl Context { 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) + let derive_default = if can_derive_defaults( + &event + .inputs + .iter() + .map(|param| Param { + name: param.name.clone(), + kind: param.kind.clone(), + internal_type: None, + }) + .collect::>(), + ) { + quote! { + #[derive(Default)] + } + } else { + quote! {} + }; + let ethers_contract = ethers_contract_crate(); Ok(quote! { - #[derive(Clone, Debug, Default, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)] + #[derive(Clone, Debug, Eq, PartialEq, #ethers_contract::EthEvent, #ethers_contract::EthDisplay, #derives)] + #derive_default #[ethevent( name = #event_abi_name, abi = #abi_signature )] pub #data_type_definition }) diff --git a/ethers-contract/tests/it/abigen.rs b/ethers-contract/tests/it/abigen.rs index 2d924be3a..92b902f75 100644 --- a/ethers-contract/tests/it/abigen.rs +++ b/ethers-contract/tests/it/abigen.rs @@ -19,704 +19,716 @@ use std::{ fn assert_codec() {} fn assert_tokenizeable() {} fn assert_call() {} - -#[test] -fn can_gen_human_readable() { - abigen!( - SimpleContract, - r#"[ - event ValueChanged(address indexed author, string oldValue, string newValue) - ]"#, - event_derives(serde::Deserialize, serde::Serialize) - ); - assert_eq!("ValueChanged", ValueChangedFilter::name()); - assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature()); -} - -#[test] -fn can_gen_not_human_readable() { - abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json"); -} - -#[test] -fn can_gen_human_readable_multiple() { - abigen!( - SimpleContract1, - r#"[ - event ValueChanged1(address indexed author, string oldValue, string newValue) - ]"#, - event_derives(serde::Deserialize, serde::Serialize); - - SimpleContract2, - r#"[ - event ValueChanged2(address indexed author, string oldValue, string newValue) - ]"#, - event_derives(serde::Deserialize, serde::Serialize) - ); - assert_eq!("ValueChanged1", ValueChanged1Filter::name()); - assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature()); - assert_eq!("ValueChanged2", ValueChanged2Filter::name()); - assert_eq!("ValueChanged2(address,string,string)", ValueChanged2Filter::abi_signature()); -} - -#[test] -fn can_gen_structs_readable() { - abigen!( - SimpleContract, - r#"[ - struct Value {address addr; string value;} - struct Addresses {address[] addr; string s;} - event ValueChanged(Value indexed old, Value newValue, Addresses _a) - ]"#, - event_derives(serde::Deserialize, serde::Serialize) - ); - let addr = Addresses { - addr: vec!["eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()], - s: "hello".to_string(), - }; - let token = addr.clone().into_token(); - assert_eq!(addr, Addresses::from_token(token).unwrap()); - - assert_eq!("ValueChanged", ValueChangedFilter::name()); - assert_eq!( - "ValueChanged((address,string),(address,string),(address[],string))", - ValueChangedFilter::abi_signature() - ); - - assert_codec::(); - assert_codec::(); - let encoded = addr.clone().encode(); - let other = Addresses::decode(&encoded).unwrap(); - assert_eq!(addr, other); -} - -#[test] -fn can_gen_structs_with_arrays_readable() { - abigen!( - SimpleContract, - r#"[ - struct Value {address addr; string value;} - struct Addresses {address[] addr; string s;} - event ValueChanged(Value indexed old, Value newValue, Addresses[] _a) - ]"#, - event_derives(serde::Deserialize, serde::Serialize) - ); - assert_eq!( - "ValueChanged((address,string),(address,string),(address[],string)[])", - ValueChangedFilter::abi_signature() - ); - - assert_codec::(); - assert_codec::(); -} - -#[test] -fn can_generate_internal_structs() { - abigen!( - VerifierContract, - "ethers-contract/tests/solidity-contracts/verifier_abi.json", - event_derives(serde::Deserialize, serde::Serialize) - ); - assert_tokenizeable::(); - assert_tokenizeable::(); - assert_tokenizeable::(); - - assert_codec::(); - assert_codec::(); - assert_codec::(); -} - -#[test] -fn can_generate_internal_structs_multiple() { - // NOTE: nesting here is necessary due to how tests are structured... - use contract::*; - mod contract { - use super::*; - abigen!( - VerifierContract, - "ethers-contract/tests/solidity-contracts/verifier_abi.json", - event_derives(serde::Deserialize, serde::Serialize); - - MyOtherVerifierContract, - "ethers-contract/tests/solidity-contracts/verifier_abi.json", - event_derives(serde::Deserialize, serde::Serialize); - ); - } - assert_tokenizeable::(); - assert_tokenizeable::(); - assert_tokenizeable::(); - - assert_codec::(); - assert_codec::(); - assert_codec::(); - - let (provider, _) = Provider::mocked(); - let client = Arc::new(provider); - - let g1 = G1Point { x: U256::zero(), y: U256::zero() }; - let g2 = G2Point { x: [U256::zero(), U256::zero()], y: [U256::zero(), U256::zero()] }; - let vk = VerifyingKey { - alfa_1: g1.clone(), - beta_2: g2.clone(), - gamma_2: g2.clone(), - delta_2: g2.clone(), - ic: vec![g1.clone()], - }; - let proof = Proof { a: g1.clone(), b: g2, c: g1 }; - - // ensure both contracts use the same types - let contract = VerifierContract::new(Address::zero(), client.clone()); - let _ = contract.verify(vec![], proof.clone(), vk.clone()); - let contract = MyOtherVerifierContract::new(Address::zero(), client); - let _ = contract.verify(vec![], proof, vk); -} - -#[test] -fn can_gen_return_struct() { - abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json"); - - fn verify( - binding: T, - ) { - let encoded = binding.clone().encode(); - let decoded = T::decode(&encoded).unwrap(); - assert_eq!(binding, decoded); - } - - // just make sure they are accessible and work - - let dupe = DupeIntReturn { out_one: 5.into(), out_two: 1234.into() }; - verify(dupe); - - let array = - ArrayRelayerReturn { outputs: vec![4.into(), 9.into(), 2.into()], some_number: 42.into() }; - verify(array); - - let single = SingleUnnamedReturn(4321.into()); - verify(single); - - // doesnt exist: - // let nonexistant = CallWithoutReturnDataReturn; -} - -#[test] -fn can_gen_human_readable_with_structs() { - abigen!( - SimpleContract, - r#"[ - struct Foo { uint256 x; } - function foo(Foo memory x) - function bar(uint256 x, uint256 y, address addr) - yeet(uint256,uint256,address) - ]"#, - event_derives(serde::Deserialize, serde::Serialize) - ); - assert_tokenizeable::(); - assert_codec::(); - - let (client, _mock) = Provider::mocked(); - let contract = SimpleContract::new(Address::default(), Arc::new(client)); - let f = Foo { x: 100u64.into() }; - let _ = contract.foo(f); - - let call = BarCall { x: 1u64.into(), y: 0u64.into(), addr: Address::random() }; - let encoded_call = contract.encode("bar", (call.x, call.y, call.addr)).unwrap(); - assert_eq!(encoded_call, call.clone().encode()); - let decoded_call = BarCall::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(call, decoded_call); - - let contract_call = SimpleContractCalls::Bar(call); - let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(contract_call, decoded_enum); - assert_eq!(encoded_call, contract_call.encode()); - - let call = YeetCall(1u64.into(), 0u64.into(), Address::zero()); - let encoded_call = contract.encode("yeet", (call.0, call.1, call.2)).unwrap(); - assert_eq!(encoded_call, call.clone().encode()); - let decoded_call = YeetCall::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(call, decoded_call); - - let contract_call = SimpleContractCalls::Yeet(call.clone()); - let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(contract_call, decoded_enum); - assert_eq!(contract_call, call.into()); - assert_eq!(encoded_call, contract_call.encode()); - - assert_call::(); - assert_call::(); -} - -#[test] -fn can_handle_overloaded_functions() { - abigen!( - SimpleContract, - r#"[ - getValue() (uint256) - getValue(uint256 otherValue) (uint256) - getValue(uint256 otherValue, address addr) (uint256) - setValue(string, string) - setValue(string) - ]"# - ); - - let (provider, _) = Provider::mocked(); - let client = Arc::new(provider); - let contract = SimpleContract::new(Address::zero(), client); - // ensure both functions are callable - let _ = contract.get_value(); - let _ = contract.get_value_with_other_value(1337u64.into()); - let _ = contract.get_value_with_other_value_and_addr(1337u64.into(), Address::zero()); - - let call = GetValueCall; - - let encoded_call = contract.encode("getValue", ()).unwrap(); - assert_eq!(encoded_call, call.clone().encode()); - let decoded_call = GetValueCall::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(call, decoded_call); - - let contract_call = SimpleContractCalls::GetValue(call); - let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(contract_call, decoded_enum); - assert_eq!(encoded_call, contract_call.encode()); - - let call = GetValueWithOtherValueCall { other_value: 420u64.into() }; - - let encoded_call = contract.encode_with_selector([15, 244, 201, 22], call.other_value).unwrap(); - assert_eq!(encoded_call, call.clone().encode()); - let decoded_call = GetValueWithOtherValueCall::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(call, decoded_call); - - let contract_call = SimpleContractCalls::GetValueWithOtherValue(call); - let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(contract_call, decoded_enum); - assert_eq!(encoded_call, contract_call.encode()); - - let call = - GetValueWithOtherValueAndAddrCall { other_value: 420u64.into(), addr: Address::random() }; - - let encoded_call = - contract.encode_with_selector([14, 97, 29, 56], (call.other_value, call.addr)).unwrap(); - let decoded_call = GetValueWithOtherValueAndAddrCall::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(call, decoded_call); - - let contract_call = SimpleContractCalls::GetValueWithOtherValueAndAddr(call); - let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(contract_call, decoded_enum); - assert_eq!(encoded_call, contract_call.encode()); - - let call = SetValue0Call("message".to_string()); - let _contract_call = SimpleContractCalls::SetValue0(call); - let call = SetValue1Call("message".to_string(), "message".to_string()); - let _contract_call = SimpleContractCalls::SetValue1(call); - - assert_call::(); - assert_call::(); - assert_call::(); -} - -#[test] -fn can_handle_even_more_overloaded_functions() { - abigen!( - ConsoleLog, - r#"[ - log() - log(string, string) - log(string) - ]"# - ); - - let _call = Log0Call; - let _contract_call = ConsoleLogCalls::Log0; - let call = Log1Call("message".to_string()); - let _contract_call = ConsoleLogCalls::Log1(call); - let call = Log2Call("message".to_string(), "message".to_string()); - let _contract_call = ConsoleLogCalls::Log2(call); -} - -#[tokio::test] -async fn can_handle_underscore_functions() { - abigen!( - SimpleStorage, - r#"[ - _hashPuzzle() (uint256) - ]"#; - - SimpleStorage2, - "ethers-contract/tests/solidity-contracts/simplestorage_abi.json", - ); - - // launch the network & connect to it - let anvil = Anvil::new().spawn(); - let from = anvil.addresses()[0]; - let provider = Provider::try_from(anvil.endpoint()) - .unwrap() - .with_sender(from) - .interval(std::time::Duration::from_millis(10)); - let client = Arc::new(provider); - - let contract = "SimpleStorage"; - let path = "./tests/solidity-contracts/SimpleStorage.sol"; - let compiled = Solc::default().compile_source(path).unwrap(); - let compiled = compiled.get(path, contract).unwrap(); - let factory = ethers_contract::ContractFactory::new( - compiled.abi.unwrap().clone(), - compiled.bytecode().unwrap().clone(), - client.clone(), - ); - let addr = factory.deploy("hi".to_string()).unwrap().legacy().send().await.unwrap().address(); - - // connect to the contract - let contract = SimpleStorage::new(addr, client.clone()); - let contract2 = SimpleStorage2::new(addr, client.clone()); - - let res = contract.hash_puzzle().call().await.unwrap(); - let res2 = contract2.hash_puzzle().call().await.unwrap(); - let res3 = contract.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); - let res4 = contract2.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); - - // Manual call construction - use ethers_providers::Middleware; - // TODO: How do we handle underscores for calls here? - let data = simple_storage::HashPuzzleCall.encode(); - let tx = Eip1559TransactionRequest::new().data(data).to(addr); - let tx = TypedTransaction::Eip1559(tx); - let res5 = client.call(&tx, None).await.unwrap(); - let res5 = U256::from(res5.as_ref()); - assert_eq!(res, 100.into()); - assert_eq!(res, res2); - assert_eq!(res, res3); - assert_eq!(res, res4); - assert_eq!(res, res5); -} - -#[test] -fn can_handle_unique_underscore_functions() { - abigen!( - ConsoleLog, - r#"[ - log(string, string) - _log(string) - _log_(string) - __log__(string) - __log2__(string) - ]"# - ); - let call = LogCall("message".to_string(), "message".to_string()); - let _contract_call = ConsoleLogCalls::Log(call); - - let call = _LogCall("message".to_string()); - let _contract_call = ConsoleLogCalls::_Log(call); - - let call = _Log_Call("message".to_string()); - let _contract_call = ConsoleLogCalls::_Log_(call); - - let call = __Log__Call("message".to_string()); - let _contract_call = ConsoleLogCalls::__Log__(call); - - let call = Log2Call("message".to_string()); - let _contract_call = ConsoleLogCalls::Log2(call); -} - -#[test] -fn can_handle_underscore_numeric() { - abigen!( - Test, - r#"[ - _100pct(string) - ]"# - ); - let _call = _100PctCall("message".to_string()); - - let provider = Arc::new(Provider::new(MockProvider::new())); - let contract = Test::new(Address::default(), Arc::clone(&provider)); - // NOTE: this seems to be weird behaviour of `Inflector::to_snake_case` which turns "100pct" -> - // "10_0pct" - let _call = contract._10_0pct("hello".to_string()); -} - -#[test] -fn can_handle_duplicates_with_same_name() { - abigen!( - ConsoleLog, - r#"[ - log() - log(uint p0) - log(string p0) - ]"# - ); - - let call = Log0Call; - let _contract_call = ConsoleLogCalls::Log0(call); - - let call = Log1Call { p_0: 100.into() }; - let _contract_call = ConsoleLogCalls::Log1(call); - - let call = Log2Call { p_0: "message".to_string() }; - let _contract_call = ConsoleLogCalls::Log2(call); -} - -#[test] -fn can_abigen_console_sol() { - abigen!(Console, "ethers-contract/tests/solidity-contracts/console.json",); -} - -#[test] -fn can_generate_nested_types() { - abigen!( - Test, - r#"[ - struct Outer {Inner inner; uint256[] arr;} - struct Inner {uint256 inner;} - function myfun(Outer calldata a) - ]"#, - ); - - assert_eq!(MyfunCall::abi_signature(), "myfun(((uint256),uint256[]))"); - - let (client, _mock) = Provider::mocked(); - let contract = Test::new(Address::default(), Arc::new(client)); - - let inner = Inner { inner: 100u64.into() }; - let a = Outer { inner, arr: vec![101u64.into()] }; - let _ = contract.myfun(a.clone()); - - let call = MyfunCall { a: a.clone() }; - let encoded_call = contract.encode("myfun", (a,)).unwrap(); - assert_eq!(encoded_call, call.clone().encode()); - let decoded_call = MyfunCall::decode(encoded_call.as_ref()).unwrap(); - assert_eq!(call, decoded_call); -} - -#[test] -fn can_handle_different_calls() { - abigen!( - Test, - r#"[ - function fooBar() - function FOO_BAR() - ]"#, - ); - - let (client, _mock) = Provider::mocked(); - let contract = Test::new(Address::default(), Arc::new(client)); - - let _ = contract.fooBar(); - let _ = contract.FOO_BAR(); -} - -#[test] -fn can_handle_case_sensitive_calls() { - abigen!( - StakedOHM, - r#"[ - index() - INDEX() - ]"#, - ); - - let (client, _mock) = Provider::mocked(); - let contract = StakedOHM::new(Address::default(), Arc::new(client)); - - let _ = contract.index(); - let _ = contract.INDEX(); -} - -#[tokio::test] -async fn can_deploy_greeter() { - abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",); - let anvil = Anvil::new().spawn(); - let from = anvil.addresses()[0]; - let provider = Provider::try_from(anvil.endpoint()) - .unwrap() - .with_sender(from) - .interval(std::time::Duration::from_millis(10)); - let client = Arc::new(provider); - - let greeter_contract = - Greeter::deploy(client, "Hello World!".to_string()).unwrap().legacy().send().await.unwrap(); - - let greeting = greeter_contract.greet().call().await.unwrap(); - assert_eq!("Hello World!", greeting); -} - -#[tokio::test] -async fn can_abiencoderv2_output() { - abigen!(AbiEncoderv2Test, "ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json",); - let anvil = Anvil::new().spawn(); - let from = anvil.addresses()[0]; - let provider = Provider::try_from(anvil.endpoint()) - .unwrap() - .with_sender(from) - .interval(std::time::Duration::from_millis(10)); - let client = Arc::new(provider); - - let contract = "AbiencoderV2Test"; - let path = "./tests/solidity-contracts/Abiencoderv2Test.sol"; - let compiled = Solc::default().compile_source(path).unwrap(); - let compiled = compiled.get(path, contract).unwrap(); - let factory = ethers_contract::ContractFactory::new( - compiled.abi.unwrap().clone(), - compiled.bytecode().unwrap().clone(), - client.clone(), - ); - let addr = factory.deploy(()).unwrap().legacy().send().await.unwrap().address(); - - let contract = AbiEncoderv2Test::new(addr, client.clone()); - let person = Person { name: "Alice".to_string(), age: 20u64.into() }; - - let res = contract.default_person().call().await.unwrap(); - assert_eq!(res, person); -} - -// 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() { +// +// #[test] +// fn can_gen_human_readable() { +// abigen!( +// SimpleContract, +// r#"[ +// event ValueChanged(address indexed author, string oldValue, string newValue) +// ]"#, +// event_derives(serde::Deserialize, serde::Serialize) +// ); +// assert_eq!("ValueChanged", ValueChangedFilter::name()); +// assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature()); +// } +// +// #[test] +// fn can_gen_not_human_readable() { +// abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json"); +// } +// +// #[test] +// fn can_gen_human_readable_multiple() { +// abigen!( +// SimpleContract1, +// r#"[ +// event ValueChanged1(address indexed author, string oldValue, string newValue) +// ]"#, +// event_derives(serde::Deserialize, serde::Serialize); +// +// SimpleContract2, +// r#"[ +// event ValueChanged2(address indexed author, string oldValue, string newValue) +// ]"#, +// event_derives(serde::Deserialize, serde::Serialize) +// ); +// assert_eq!("ValueChanged1", ValueChanged1Filter::name()); +// assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature()); +// assert_eq!("ValueChanged2", ValueChanged2Filter::name()); +// assert_eq!("ValueChanged2(address,string,string)", ValueChanged2Filter::abi_signature()); +// } +// +// #[test] +// fn can_gen_structs_readable() { +// abigen!( +// SimpleContract, +// r#"[ +// struct Value {address addr; string value;} +// struct Addresses {address[] addr; string s;} +// event ValueChanged(Value indexed old, Value newValue, Addresses _a) +// ]"#, +// event_derives(serde::Deserialize, serde::Serialize) +// ); +// let addr = Addresses { +// addr: vec!["eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()], +// s: "hello".to_string(), +// }; +// let token = addr.clone().into_token(); +// assert_eq!(addr, Addresses::from_token(token).unwrap()); +// +// assert_eq!("ValueChanged", ValueChangedFilter::name()); +// assert_eq!( +// "ValueChanged((address,string),(address,string),(address[],string))", +// ValueChangedFilter::abi_signature() +// ); +// +// assert_codec::(); +// assert_codec::(); +// let encoded = addr.clone().encode(); +// let other = Addresses::decode(&encoded).unwrap(); +// assert_eq!(addr, other); +// } +// +// #[test] +// fn can_gen_structs_with_arrays_readable() { +// abigen!( +// SimpleContract, +// r#"[ +// struct Value {address addr; string value;} +// struct Addresses {address[] addr; string s;} +// event ValueChanged(Value indexed old, Value newValue, Addresses[] _a) +// ]"#, +// event_derives(serde::Deserialize, serde::Serialize) +// ); +// assert_eq!( +// "ValueChanged((address,string),(address,string),(address[],string)[])", +// ValueChangedFilter::abi_signature() +// ); +// +// assert_codec::(); +// assert_codec::(); +// } +// +// #[test] +// fn can_generate_internal_structs() { +// abigen!( +// VerifierContract, +// "ethers-contract/tests/solidity-contracts/verifier_abi.json", +// event_derives(serde::Deserialize, serde::Serialize) +// ); +// assert_tokenizeable::(); +// assert_tokenizeable::(); +// assert_tokenizeable::(); +// +// assert_codec::(); +// assert_codec::(); +// assert_codec::(); +// } +// +// #[test] +// fn can_generate_internal_structs_multiple() { +// // NOTE: nesting here is necessary due to how tests are structured... +// use contract::*; +// mod contract { +// use super::*; +// abigen!( +// VerifierContract, +// "ethers-contract/tests/solidity-contracts/verifier_abi.json", +// event_derives(serde::Deserialize, serde::Serialize); +// +// MyOtherVerifierContract, +// "ethers-contract/tests/solidity-contracts/verifier_abi.json", +// event_derives(serde::Deserialize, serde::Serialize); +// ); +// } +// assert_tokenizeable::(); +// assert_tokenizeable::(); +// assert_tokenizeable::(); +// +// assert_codec::(); +// assert_codec::(); +// assert_codec::(); +// +// let (provider, _) = Provider::mocked(); +// let client = Arc::new(provider); +// +// let g1 = G1Point { x: U256::zero(), y: U256::zero() }; +// let g2 = G2Point { x: [U256::zero(), U256::zero()], y: [U256::zero(), U256::zero()] }; +// let vk = VerifyingKey { +// alfa_1: g1.clone(), +// beta_2: g2.clone(), +// gamma_2: g2.clone(), +// delta_2: g2.clone(), +// ic: vec![g1.clone()], +// }; +// let proof = Proof { a: g1.clone(), b: g2, c: g1 }; +// +// // ensure both contracts use the same types +// let contract = VerifierContract::new(Address::zero(), client.clone()); +// let _ = contract.verify(vec![], proof.clone(), vk.clone()); +// let contract = MyOtherVerifierContract::new(Address::zero(), client); +// let _ = contract.verify(vec![], proof, vk); +// } +// +// #[test] +// fn can_gen_return_struct() { +// abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json"); +// +// fn verify( +// binding: T, +// ) { +// let encoded = binding.clone().encode(); +// let decoded = T::decode(&encoded).unwrap(); +// assert_eq!(binding, decoded); +// } +// +// // just make sure they are accessible and work +// +// let dupe = DupeIntReturn { out_one: 5.into(), out_two: 1234.into() }; +// verify(dupe); +// +// let array = +// ArrayRelayerReturn { outputs: vec![4.into(), 9.into(), 2.into()], some_number: 42.into() +// }; verify(array); +// +// let single = SingleUnnamedReturn(4321.into()); +// verify(single); +// +// // doesnt exist: +// // let nonexistant = CallWithoutReturnDataReturn; +// } +// +// #[test] +// fn can_gen_human_readable_with_structs() { +// abigen!( +// SimpleContract, +// r#"[ +// struct Foo { uint256 x; } +// function foo(Foo memory x) +// function bar(uint256 x, uint256 y, address addr) +// yeet(uint256,uint256,address) +// ]"#, +// event_derives(serde::Deserialize, serde::Serialize) +// ); +// assert_tokenizeable::(); +// assert_codec::(); +// +// let (client, _mock) = Provider::mocked(); +// let contract = SimpleContract::new(Address::default(), Arc::new(client)); +// let f = Foo { x: 100u64.into() }; +// let _ = contract.foo(f); +// +// let call = BarCall { x: 1u64.into(), y: 0u64.into(), addr: Address::random() }; +// let encoded_call = contract.encode("bar", (call.x, call.y, call.addr)).unwrap(); +// assert_eq!(encoded_call, call.clone().encode()); +// let decoded_call = BarCall::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(call, decoded_call); +// +// let contract_call = SimpleContractCalls::Bar(call); +// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(contract_call, decoded_enum); +// assert_eq!(encoded_call, contract_call.encode()); +// +// let call = YeetCall(1u64.into(), 0u64.into(), Address::zero()); +// let encoded_call = contract.encode("yeet", (call.0, call.1, call.2)).unwrap(); +// assert_eq!(encoded_call, call.clone().encode()); +// let decoded_call = YeetCall::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(call, decoded_call); +// +// let contract_call = SimpleContractCalls::Yeet(call.clone()); +// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(contract_call, decoded_enum); +// assert_eq!(contract_call, call.into()); +// assert_eq!(encoded_call, contract_call.encode()); +// +// assert_call::(); +// assert_call::(); +// } +// +// #[test] +// fn can_handle_overloaded_functions() { // abigen!( -// MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7"; -// MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2"; +// SimpleContract, +// r#"[ +// getValue() (uint256) +// getValue(uint256 otherValue) (uint256) +// getValue(uint256 otherValue, address addr) (uint256) +// setValue(string, string) +// setValue(string) +// ]"# // ); // +// let (provider, _) = Provider::mocked(); +// let client = Arc::new(provider); +// let contract = SimpleContract::new(Address::zero(), client); +// // ensure both functions are callable +// let _ = contract.get_value(); +// let _ = contract.get_value_with_other_value(1337u64.into()); +// let _ = contract.get_value_with_other_value_and_addr(1337u64.into(), Address::zero()); +// +// let call = GetValueCall; +// +// let encoded_call = contract.encode("getValue", ()).unwrap(); +// assert_eq!(encoded_call, call.clone().encode()); +// let decoded_call = GetValueCall::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(call, decoded_call); +// +// let contract_call = SimpleContractCalls::GetValue(call); +// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(contract_call, decoded_enum); +// assert_eq!(encoded_call, contract_call.encode()); +// +// let call = GetValueWithOtherValueCall { other_value: 420u64.into() }; +// +// let encoded_call = contract.encode_with_selector([15, 244, 201, 22], +// call.other_value).unwrap(); assert_eq!(encoded_call, call.clone().encode()); +// let decoded_call = GetValueWithOtherValueCall::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(call, decoded_call); +// +// let contract_call = SimpleContractCalls::GetValueWithOtherValue(call); +// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(contract_call, decoded_enum); +// assert_eq!(encoded_call, contract_call.encode()); +// +// let call = +// GetValueWithOtherValueAndAddrCall { other_value: 420u64.into(), addr: Address::random() +// }; +// +// let encoded_call = +// contract.encode_with_selector([14, 97, 29, 56], (call.other_value, call.addr)).unwrap(); +// let decoded_call = GetValueWithOtherValueAndAddrCall::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(call, decoded_call); +// +// let contract_call = SimpleContractCalls::GetValueWithOtherValueAndAddr(call); +// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(contract_call, decoded_enum); +// assert_eq!(encoded_call, contract_call.encode()); +// +// let call = SetValue0Call("message".to_string()); +// let _contract_call = SimpleContractCalls::SetValue0(call); +// let call = SetValue1Call("message".to_string(), "message".to_string()); +// let _contract_call = SimpleContractCalls::SetValue1(call); +// +// assert_call::(); +// assert_call::(); +// assert_call::(); +// } +// +// #[test] +// fn can_handle_even_more_overloaded_functions() { +// abigen!( +// ConsoleLog, +// r#"[ +// log() +// log(string, string) +// log(string) +// ]"# +// ); +// +// let _call = Log0Call; +// let _contract_call = ConsoleLogCalls::Log0; +// let call = Log1Call("message".to_string()); +// let _contract_call = ConsoleLogCalls::Log1(call); +// let call = Log2Call("message".to_string(), "message".to_string()); +// let _contract_call = ConsoleLogCalls::Log2(call); +// } +// +// #[tokio::test] +// async fn can_handle_underscore_functions() { +// abigen!( +// SimpleStorage, +// r#"[ +// _hashPuzzle() (uint256) +// ]"#; +// +// SimpleStorage2, +// "ethers-contract/tests/solidity-contracts/simplestorage_abi.json", +// ); +// +// // launch the network & connect to it +// let anvil = Anvil::new().spawn(); +// let from = anvil.addresses()[0]; +// let provider = Provider::try_from(anvil.endpoint()) +// .unwrap() +// .with_sender(from) +// .interval(std::time::Duration::from_millis(10)); +// let client = Arc::new(provider); +// +// let contract = "SimpleStorage"; +// let path = "./tests/solidity-contracts/SimpleStorage.sol"; +// let compiled = Solc::default().compile_source(path).unwrap(); +// let compiled = compiled.get(path, contract).unwrap(); +// let factory = ethers_contract::ContractFactory::new( +// compiled.abi.unwrap().clone(), +// compiled.bytecode().unwrap().clone(), +// client.clone(), +// ); +// let addr = +// factory.deploy("hi".to_string()).unwrap().legacy().send().await.unwrap().address(); +// +// // connect to the contract +// let contract = SimpleStorage::new(addr, client.clone()); +// let contract2 = SimpleStorage2::new(addr, client.clone()); +// +// let res = contract.hash_puzzle().call().await.unwrap(); +// let res2 = contract2.hash_puzzle().call().await.unwrap(); +// let res3 = contract.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); +// let res4 = contract2.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); +// +// // Manual call construction +// use ethers_providers::Middleware; +// // TODO: How do we handle underscores for calls here? +// let data = simple_storage::HashPuzzleCall.encode(); +// let tx = Eip1559TransactionRequest::new().data(data).to(addr); +// let tx = TypedTransaction::Eip1559(tx); +// let res5 = client.call(&tx, None).await.unwrap(); +// let res5 = U256::from(res5.as_ref()); +// assert_eq!(res, 100.into()); +// assert_eq!(res, res2); +// assert_eq!(res, res3); +// assert_eq!(res, res4); +// assert_eq!(res, res5); +// } +// +// #[test] +// fn can_handle_unique_underscore_functions() { +// abigen!( +// ConsoleLog, +// r#"[ +// log(string, string) +// _log(string) +// _log_(string) +// __log__(string) +// __log2__(string) +// ]"# +// ); +// let call = LogCall("message".to_string(), "message".to_string()); +// let _contract_call = ConsoleLogCalls::Log(call); +// +// let call = _LogCall("message".to_string()); +// let _contract_call = ConsoleLogCalls::_Log(call); +// +// let call = _Log_Call("message".to_string()); +// let _contract_call = ConsoleLogCalls::_Log_(call); +// +// let call = __Log__Call("message".to_string()); +// let _contract_call = ConsoleLogCalls::__Log__(call); +// +// let call = Log2Call("message".to_string()); +// let _contract_call = ConsoleLogCalls::Log2(call); +// } +// +// #[test] +// fn can_handle_underscore_numeric() { +// abigen!( +// Test, +// r#"[ +// _100pct(string) +// ]"# +// ); +// let _call = _100PctCall("message".to_string()); +// // let provider = Arc::new(Provider::new(MockProvider::new())); -// let _contract = MyContract::new(Address::default(), Arc::clone(&provider)); -// let _contract = MyContract2::new(Address::default(), provider); +// let contract = Test::new(Address::default(), Arc::clone(&provider)); +// // NOTE: this seems to be weird behaviour of `Inflector::to_snake_case` which turns "100pct" +// -> // "10_0pct" +// let _call = contract._10_0pct("hello".to_string()); +// } +// +// #[test] +// fn can_handle_duplicates_with_same_name() { +// abigen!( +// ConsoleLog, +// r#"[ +// log() +// log(uint p0) +// log(string p0) +// ]"# +// ); +// +// let call = Log0Call; +// let _contract_call = ConsoleLogCalls::Log0(call); +// +// let call = Log1Call { p_0: 100.into() }; +// let _contract_call = ConsoleLogCalls::Log1(call); +// +// let call = Log2Call { p_0: "message".to_string() }; +// let _contract_call = ConsoleLogCalls::Log2(call); +// } +// +// #[test] +// fn can_abigen_console_sol() { +// abigen!(Console, "ethers-contract/tests/solidity-contracts/console.json",); +// } +// +// #[test] +// fn can_generate_nested_types() { +// abigen!( +// Test, +// r#"[ +// struct Outer {Inner inner; uint256[] arr;} +// struct Inner {uint256 inner;} +// function myfun(Outer calldata a) +// ]"#, +// ); +// +// assert_eq!(MyfunCall::abi_signature(), "myfun(((uint256),uint256[]))"); +// +// let (client, _mock) = Provider::mocked(); +// let contract = Test::new(Address::default(), Arc::new(client)); +// +// let inner = Inner { inner: 100u64.into() }; +// let a = Outer { inner, arr: vec![101u64.into()] }; +// let _ = contract.myfun(a.clone()); +// +// let call = MyfunCall { a: a.clone() }; +// let encoded_call = contract.encode("myfun", (a,)).unwrap(); +// assert_eq!(encoded_call, call.clone().encode()); +// let decoded_call = MyfunCall::decode(encoded_call.as_ref()).unwrap(); +// assert_eq!(call, decoded_call); +// } +// +// #[test] +// fn can_handle_different_calls() { +// abigen!( +// Test, +// r#"[ +// function fooBar() +// function FOO_BAR() +// ]"#, +// ); +// +// let (client, _mock) = Provider::mocked(); +// let contract = Test::new(Address::default(), Arc::new(client)); +// +// let _ = contract.fooBar(); +// let _ = contract.FOO_BAR(); +// } +// +// #[test] +// fn can_handle_case_sensitive_calls() { +// abigen!( +// StakedOHM, +// r#"[ +// index() +// INDEX() +// ]"#, +// ); +// +// let (client, _mock) = Provider::mocked(); +// let contract = StakedOHM::new(Address::default(), Arc::new(client)); +// +// let _ = contract.index(); +// let _ = contract.INDEX(); +// } +// +// #[tokio::test] +// async fn can_deploy_greeter() { +// abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",); +// let anvil = Anvil::new().spawn(); +// let from = anvil.addresses()[0]; +// let provider = Provider::try_from(anvil.endpoint()) +// .unwrap() +// .with_sender(from) +// .interval(std::time::Duration::from_millis(10)); +// let client = Arc::new(provider); +// +// let greeter_contract = +// Greeter::deploy(client, "Hello +// World!".to_string()).unwrap().legacy().send().await.unwrap(); +// +// let greeting = greeter_contract.greet().call().await.unwrap(); +// assert_eq!("Hello World!", greeting); +// } +// +// #[tokio::test] +// async fn can_abiencoderv2_output() { +// abigen!(AbiEncoderv2Test, +// "ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json",); let anvil = +// Anvil::new().spawn(); let from = anvil.addresses()[0]; +// let provider = Provider::try_from(anvil.endpoint()) +// .unwrap() +// .with_sender(from) +// .interval(std::time::Duration::from_millis(10)); +// let client = Arc::new(provider); +// +// let contract = "AbiencoderV2Test"; +// let path = "./tests/solidity-contracts/Abiencoderv2Test.sol"; +// let compiled = Solc::default().compile_source(path).unwrap(); +// let compiled = compiled.get(path, contract).unwrap(); +// let factory = ethers_contract::ContractFactory::new( +// compiled.abi.unwrap().clone(), +// compiled.bytecode().unwrap().clone(), +// client.clone(), +// ); +// let addr = factory.deploy(()).unwrap().legacy().send().await.unwrap().address(); +// +// let contract = AbiEncoderv2Test::new(addr, client.clone()); +// let person = Person { name: "Alice".to_string(), age: 20u64.into() }; +// +// let res = contract.default_person().call().await.unwrap(); +// assert_eq!(res, person); +// } +// +// // 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() { +// // abigen!( +// // MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7"; +// // MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2"; +// // ); +// // +// // let provider = Arc::new(Provider::new(MockProvider::new())); +// // let _contract = MyContract::new(Address::default(), Arc::clone(&provider)); +// // let _contract = MyContract2::new(Address::default(), provider); +// // } +// +// #[test] +// fn can_gen_reserved_word_field_names() { +// abigen!( +// Test, +// r#"[ +// struct Foo { uint256 ref; } +// ]"#, +// ); +// +// let _foo = Foo { ref_: U256::default() }; +// } +// +// #[test] +// fn can_handle_overloaded_events() { +// abigen!( +// SimpleContract, +// r#"[ +// event ActionPaused(string cToken, string action, bool pauseState) +// event ActionPaused(string action, bool pauseState) +// ]"# +// ); +// +// let _ev1 = ActionPaused1Filter { +// c_token: "ctoken".to_string(), +// action: "action".to_string(), +// pause_state: false, +// }; +// let _ev2 = ActionPaused2Filter { action: "action".to_string(), pause_state: false }; +// } +// +// #[tokio::test] +// #[cfg(not(feature = "celo"))] +// async fn can_send_struct_param() { +// abigen!(StructContract, "./tests/solidity-contracts/StructContract.json"); +// +// let server = Anvil::new().spawn(); +// let wallet: LocalWallet = server.keys()[0].clone().into(); +// let provider = Provider::try_from(server.endpoint()).unwrap(); +// let client = +// Arc::new(SignerMiddleware::new(provider, wallet.with_chain_id(Chain::AnvilHardhat))); +// +// let contract = StructContract::deploy(client, ()).unwrap().legacy().send().await.unwrap(); +// +// let point = Point { x: 1337u64.into(), y: 0u64.into() }; +// let tx = contract.submit_point(point).legacy(); +// let tx = tx.send().await.unwrap().await.unwrap().unwrap(); +// assert_eq!(tx.logs.len(), 1); +// +// let logs: Vec = contract.event().from_block(0u64).query().await.unwrap(); +// assert_eq!(logs.len(), 1); +// } +// +// #[test] +// fn can_gen_seaport() { +// abigen!(Seaport, "./tests/solidity-contracts/seaport.json"); +// +// assert_eq!( +// FulfillAdvancedOrderCall::abi_signature(), +// "fulfillAdvancedOrder(((address,address,(uint8,address,uint256,uint256,uint256)[],(uint8, +// address,uint256,uint256,uint256,address)[],uint8,uint256,uint256,bytes32,uint256,bytes32, +// uint256),uint120,uint120,bytes,bytes),(uint256,uint8,uint256,uint256,bytes32[])[],bytes32, +// address)" ); +// assert_eq!(hex::encode(FulfillAdvancedOrderCall::selector()), "e7acab24"); +// +// assert_codec::(); +// let err = SeaportErrors::BadContractSignature(BadContractSignature::default()); +// +// let encoded = err.clone().encode(); +// assert_eq!(err, SeaportErrors::decode(encoded).unwrap()); +// +// let err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet { +// order_index: U256::zero(), +// consideration_index: U256::zero(), +// shortfall_amount: U256::zero(), +// }); +// } +// +// #[test] +// fn can_generate_to_string_overload() { +// abigen!( +// ToString, +// r#"[ +// toString(bytes) +// toString(address) +// toString(uint256) +// toString(int256) +// toString(bytes32) +// toString(bool) +// ]"# +// ); +// +// match ToStringCalls::ToString0(ToString0Call(Default::default())) { +// ToStringCalls::ToString0(_) => {} +// ToStringCalls::ToString1(_) => {} +// ToStringCalls::ToString2(_) => {} +// ToStringCalls::ToString3(_) => {} +// ToStringCalls::ToString4(_) => {} +// ToStringCalls::ToString5(_) => {} +// }; +// } +// +// #[test] +// fn can_generate_large_event() { +// abigen!(NewSale, "ethers-contract/tests/solidity-contracts/sale.json"); +// } +// +// #[test] +// fn can_generate_large_output_struct() { +// abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json"); +// +// let r = GetByIdReturn(Info::default()); +// } +// +// #[test] +// fn gen_complex_function() { +// abigen!( +// WyvernExchangeV1, +// r#"[ +// function atomicMatch_(address[14] addrs, uint[18] uints, uint8[8] +// feeMethodsSidesKindsHowToCalls, bytes calldataBuy, bytes calldataSell, bytes +// replacementPatternBuy, bytes replacementPatternSell, bytes staticExtradataBuy, bytes +// staticExtradataSell, uint8[2] vs, bytes32[5] rssMetadata) public payable ]"#, +// ); +// } +// +// #[test] +// 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::(); // } #[test] -fn can_gen_reserved_word_field_names() { - abigen!( - Test, - r#"[ - struct Foo { uint256 ref; } - ]"#, - ); - - let _foo = Foo { ref_: U256::default() }; -} - -#[test] -fn can_handle_overloaded_events() { - abigen!( - SimpleContract, - r#"[ - event ActionPaused(string cToken, string action, bool pauseState) - event ActionPaused(string action, bool pauseState) - ]"# - ); - - let _ev1 = ActionPaused1Filter { - c_token: "ctoken".to_string(), - action: "action".to_string(), - pause_state: false, - }; - let _ev2 = ActionPaused2Filter { action: "action".to_string(), pause_state: false }; -} - -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn can_send_struct_param() { - abigen!(StructContract, "./tests/solidity-contracts/StructContract.json"); - - let server = Anvil::new().spawn(); - let wallet: LocalWallet = server.keys()[0].clone().into(); - let provider = Provider::try_from(server.endpoint()).unwrap(); - let client = - Arc::new(SignerMiddleware::new(provider, wallet.with_chain_id(Chain::AnvilHardhat))); - - let contract = StructContract::deploy(client, ()).unwrap().legacy().send().await.unwrap(); - - let point = Point { x: 1337u64.into(), y: 0u64.into() }; - let tx = contract.submit_point(point).legacy(); - let tx = tx.send().await.unwrap().await.unwrap().unwrap(); - assert_eq!(tx.logs.len(), 1); - - let logs: Vec = contract.event().from_block(0u64).query().await.unwrap(); - assert_eq!(logs.len(), 1); -} - -#[test] -fn can_gen_seaport() { - abigen!(Seaport, "./tests/solidity-contracts/seaport.json"); - - assert_eq!( - FulfillAdvancedOrderCall::abi_signature(), - "fulfillAdvancedOrder(((address,address,(uint8,address,uint256,uint256,uint256)[],(uint8,address,uint256,uint256,uint256,address)[],uint8,uint256,uint256,bytes32,uint256,bytes32,uint256),uint120,uint120,bytes,bytes),(uint256,uint8,uint256,uint256,bytes32[])[],bytes32,address)" - ); - assert_eq!(hex::encode(FulfillAdvancedOrderCall::selector()), "e7acab24"); - - assert_codec::(); - let err = SeaportErrors::BadContractSignature(BadContractSignature::default()); - - let encoded = err.clone().encode(); - assert_eq!(err, SeaportErrors::decode(encoded).unwrap()); - - let err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet { - order_index: U256::zero(), - consideration_index: U256::zero(), - shortfall_amount: U256::zero(), - }); -} - -#[test] -fn can_generate_to_string_overload() { - abigen!( - ToString, - r#"[ - toString(bytes) - toString(address) - toString(uint256) - toString(int256) - toString(bytes32) - toString(bool) - ]"# - ); - - match ToStringCalls::ToString0(ToString0Call(Default::default())) { - ToStringCalls::ToString0(_) => {} - ToStringCalls::ToString1(_) => {} - ToStringCalls::ToString2(_) => {} - ToStringCalls::ToString3(_) => {} - ToStringCalls::ToString4(_) => {} - ToStringCalls::ToString5(_) => {} - }; -} - -#[test] -fn can_generate_large_event() { - abigen!(NewSale, "ethers-contract/tests/solidity-contracts/sale.json"); -} - -#[test] -fn can_generate_large_output_struct() { - abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json"); - - let r = GetByIdReturn(Info::default()); -} - -#[test] -fn gen_complex_function() { - abigen!( - WyvernExchangeV1, - r#"[ - function atomicMatch_(address[14] addrs, uint[18] uints, uint8[8] feeMethodsSidesKindsHowToCalls, bytes calldataBuy, bytes calldataSell, bytes replacementPatternBuy, bytes replacementPatternSell, bytes staticExtradataBuy, bytes staticExtradataSell, uint8[2] vs, bytes32[5] rssMetadata) public payable - ]"#, - ); -} - -#[test] -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::(); +fn can_generate_event_with_structs() { + abigen!(MyContract, "ethers-contract/tests/solidity-contracts/EventWithStruct.json"); } diff --git a/ethers-contract/tests/solidity-contracts/EventWithStruct.json b/ethers-contract/tests/solidity-contracts/EventWithStruct.json new file mode 100644 index 000000000..1bbb1f869 --- /dev/null +++ b/ethers-contract/tests/solidity-contracts/EventWithStruct.json @@ -0,0 +1,33 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "a", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "b", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct MyContract.MyStruct", + "name": "", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "c", + "type": "uint256" + } + ], + "name": "MyEvent", + "type": "event" + } +] \ No newline at end of file From 30830bc23fb60e6caab527ce22f61c37e387c16c Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 6 Sep 2022 19:28:14 +0200 Subject: [PATCH 2/6] feat(abigen): subsitute structs in event bindings --- .../ethers-contract-abigen/src/contract.rs | 1 + .../src/contract/events.rs | 85 +- .../src/contract/structs.rs | 42 +- .../ethers-contract-abigen/src/multi.rs | 32 +- ethers-contract/tests/it/abigen.rs | 1412 +++++++++-------- ethers-core/src/abi/human_readable/mod.rs | 29 +- 6 files changed, 831 insertions(+), 770 deletions(-) diff --git a/ethers-contract/ethers-contract-abigen/src/contract.rs b/ethers-contract/ethers-contract-abigen/src/contract.rs index 885b348a4..2d55698b1 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract.rs @@ -207,6 +207,7 @@ impl Context { .rust_type_names .extend(abi_parser.function_params.values().map(|ty| (ty.clone(), ty.clone()))); internal_structs.function_params = abi_parser.function_params.clone(); + internal_structs.event_params = abi_parser.event_params.clone(); internal_structs.outputs = abi_parser.outputs.clone(); internal_structs diff --git a/ethers-contract/ethers-contract-abigen/src/contract/events.rs b/ethers-contract/ethers-contract-abigen/src/contract/events.rs index d688458f9..907274398 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/events.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/events.rs @@ -1,7 +1,7 @@ use super::{types, util, Context}; use crate::util::can_derive_defaults; use ethers_core::{ - abi::{Event, EventExt, EventParam, Param, ParamType, SolStruct}, + abi::{Event, EventExt, EventParam, Param, ParamType}, macros::{ethers_contract_crate, ethers_core_crate}, }; use eyre::Result; @@ -141,54 +141,59 @@ impl Context { /// Note that this is slightly different from expanding a Solidity type as /// complex types like arrays and strings get emitted as hashes when they are /// indexed. - /// If a complex types matches with a struct previously parsed by the AbiParser, + /// If a complex types matches with a struct previously parsed by the internal structs, /// we can replace it - fn expand_input_type(&self, input: &EventParam) -> Result { + fn expand_input_type( + &self, + event: &Event, + input: &EventParam, + idx: usize, + ) -> Result { let ethers_core = ethers_core_crate(); Ok(match (&input.kind, input.indexed) { - (ParamType::Array(ty), true) => { - if let ParamType::Tuple(..) = **ty { - // represents an array of a struct - if let Some(ty) = self - .abi_parser - .structs - .get(&input.name) - .map(SolStruct::name) - .map(util::ident) - { - return Ok(quote! {::std::vec::Vec<#ty>}) - } - } + (ParamType::Array(_), true) => { quote! { #ethers_core::types::H256 } } - (ParamType::FixedArray(ty, size), true) => { - if let ParamType::Tuple(..) = **ty { - // represents a fixed array of a struct - if let Some(ty) = self - .abi_parser - .structs - .get(&input.name) - .map(SolStruct::name) - .map(util::ident) - { - let size = Literal::usize_unsuffixed(*size); - return Ok(quote! {[#ty; #size]}) - } - } + (ParamType::FixedArray(_, _), true) => { quote! { #ethers_core::types::H256 } } (ParamType::Tuple(..), true) => { - // represents a struct - if let Some(ty) = - self.abi_parser.structs.get(&input.name).map(SolStruct::name).map(util::ident) + quote! { #ethers_core::types::H256 } + } + (ParamType::Bytes, true) | (ParamType::String, true) => { + quote! { #ethers_core::types::H256 } + } + (ParamType::Tuple(_), false) => { + let ty = if let Some(rust_struct_name) = + self.internal_structs.get_event_input_struct_type(&event.name, idx) { - quote! {#ty} + let ident = util::ident(rust_struct_name); + quote! {#ident} } else { - quote! { #ethers_core::types::H256 } + types::expand(&input.kind)? + }; + ty + } + (ParamType::Array(_), _) => { + // represents an array of a struct + if let Some(rust_struct_name) = + self.internal_structs.get_event_input_struct_type(&event.name, idx) + { + let ty = util::ident(rust_struct_name); + return Ok(quote! {::std::vec::Vec<#ty>}) } + types::expand(&input.kind)? } - (ParamType::Bytes, true) | (ParamType::String, true) => { - quote! { #ethers_core::types::H256 } + (ParamType::FixedArray(_, size), _) => { + // represents a fixed array of a struct + if let Some(rust_struct_name) = + self.internal_structs.get_event_input_struct_type(&event.name, idx) + { + let ty = util::ident(rust_struct_name); + let size = Literal::usize_unsuffixed(*size); + return Ok(quote! {[#ty; #size]}) + } + types::expand(&input.kind)? } (kind, _) => types::expand(kind)?, }) @@ -200,10 +205,10 @@ impl Context { .inputs .iter() .enumerate() - .map(|(i, input)| { + .map(|(idx, input)| { // NOTE: Events can contain nameless values. - let name = util::expand_input_name(i, &input.name); - let ty = self.expand_input_type(input)?; + let name = util::expand_input_name(idx, &input.name); + let ty = self.expand_input_type(event, input, idx)?; Ok((name, ty, input.indexed)) }) diff --git a/ethers-contract/ethers-contract-abigen/src/contract/structs.rs b/ethers-contract/ethers-contract-abigen/src/contract/structs.rs index fc82f34f4..0eaed5d9e 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/structs.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/structs.rs @@ -229,6 +229,12 @@ pub struct InternalStructs { /// (function name) -> Vec all structs the function returns pub(crate) outputs: HashMap>, + /// (event name, idx) -> struct which are the identifying properties we get the name + /// from ethabi. + /// + /// Note: we need to map the index of the event here because events can contain nameless inputs + pub(crate) event_params: HashMap<(String, usize), String>, + /// All the structs extracted from the abi with their identifier as key pub(crate) structs: HashMap, @@ -245,23 +251,37 @@ impl InternalStructs { let mut top_level_internal_types = HashMap::new(); 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| item.type_field == "constructor" || item.type_field == "function") - { + for item in abi.into_iter().filter(|item| match item.type_field.as_str() { + "constructor" | "function" | "event" => true, + _ => false, + }) { + let is_event = item.type_field == "event"; + if let Some(name) = item.name { - for input in item.inputs { + for (idx, input) in item.inputs.into_iter().enumerate() { if let Some(ty) = input .internal_type .as_deref() .filter(|ty| ty.starts_with("struct ")) .map(struct_type_identifier) { - function_params.insert((name.clone(), input.name.clone()), ty.to_string()); + if is_event { + event_params.insert((name.clone(), idx), ty.to_string()); + } else { + function_params + .insert((name.clone(), input.name.clone()), ty.to_string()); + } top_level_internal_types.insert(ty.to_string(), input); } } + + if is_event { + // no outputs in an event + continue + } + let mut output_structs = Vec::new(); for output in item.outputs { if let Some(ty) = output @@ -300,6 +320,7 @@ impl InternalStructs { function_params, outputs, structs, + event_params, struct_tuples, rust_type_names: type_names .into_iter() @@ -318,6 +339,15 @@ impl InternalStructs { .map(String::as_str) } + /// Returns the name of the rust type that will be generated if the given input is a struct + /// This takes the index of event's parameter instead of the parameter's name like + /// [`Self::get_function_input_struct_type`] does because we can't rely on the name since events + /// support nameless parameters NOTE: this does not account for arrays or fixed arrays + pub fn get_event_input_struct_type(&self, event: &str, idx: usize) -> Option<&str> { + let key = (event.to_string(), idx); + self.event_params.get(&key).and_then(|id| self.rust_type_names.get(id)).map(String::as_str) + } + /// Returns the name of the rust type that will be generated if the given output is a struct /// NOTE: this does not account for arrays or fixed arrays pub fn get_function_output_struct_type( diff --git a/ethers-contract/ethers-contract-abigen/src/multi.rs b/ethers-contract/ethers-contract-abigen/src/multi.rs index 32396b9ca..9c687ebae 100644 --- a/ethers-contract/ethers-contract-abigen/src/multi.rs +++ b/ethers-contract/ethers-contract-abigen/src/multi.rs @@ -828,12 +828,12 @@ mod tests { let single_file = false; - multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap(); + multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap(); multi_gen .clone() .build() .unwrap() - .ensure_consistent_module(&mod_root, single_file) + .ensure_consistent_module(mod_root, single_file) .expect("Inconsistent bindings"); }) } @@ -845,12 +845,12 @@ mod tests { let single_file = true; - multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap(); + multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap(); multi_gen .clone() .build() .unwrap() - .ensure_consistent_module(&mod_root, single_file) + .ensure_consistent_module(mod_root, single_file) .expect("Inconsistent bindings"); }) } @@ -868,13 +868,13 @@ mod tests { .clone() .build() .unwrap() - .write_to_crate(name, version, &mod_root, single_file) + .write_to_crate(name, version, mod_root, single_file) .unwrap(); multi_gen .clone() .build() .unwrap() - .ensure_consistent_crate(name, version, &mod_root, single_file, true) + .ensure_consistent_crate(name, version, mod_root, single_file, true) .expect("Inconsistent bindings"); }) } @@ -892,13 +892,13 @@ mod tests { .clone() .build() .unwrap() - .write_to_crate(name, version, &mod_root, single_file) + .write_to_crate(name, version, mod_root, single_file) .unwrap(); multi_gen .clone() .build() .unwrap() - .ensure_consistent_crate(name, version, &mod_root, single_file, true) + .ensure_consistent_crate(name, version, mod_root, single_file, true) .expect("Inconsistent bindings"); }) } @@ -910,7 +910,7 @@ mod tests { let single_file = false; - multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap(); + multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap(); let mut cloned = multi_gen.clone(); cloned.push( @@ -924,7 +924,7 @@ mod tests { ); let result = - cloned.build().unwrap().ensure_consistent_module(&mod_root, single_file).is_err(); + cloned.build().unwrap().ensure_consistent_module(mod_root, single_file).is_err(); // ensure inconsistent bindings are detected assert!(result, "Inconsistent bindings wrongly approved"); @@ -938,7 +938,7 @@ mod tests { let single_file = true; - multi_gen.clone().build().unwrap().write_to_module(&mod_root, single_file).unwrap(); + multi_gen.clone().build().unwrap().write_to_module(mod_root, single_file).unwrap(); let mut cloned = multi_gen.clone(); cloned.push( @@ -952,7 +952,7 @@ mod tests { ); let result = - cloned.build().unwrap().ensure_consistent_module(&mod_root, single_file).is_err(); + cloned.build().unwrap().ensure_consistent_module(mod_root, single_file).is_err(); // ensure inconsistent bindings are detected assert!(result, "Inconsistent bindings wrongly approved"); @@ -972,7 +972,7 @@ mod tests { .clone() .build() .unwrap() - .write_to_crate(name, version, &mod_root, single_file) + .write_to_crate(name, version, mod_root, single_file) .unwrap(); let mut cloned = multi_gen.clone(); @@ -989,7 +989,7 @@ mod tests { let result = cloned .build() .unwrap() - .ensure_consistent_crate(name, version, &mod_root, single_file, true) + .ensure_consistent_crate(name, version, mod_root, single_file, true) .is_err(); // ensure inconsistent bindings are detected @@ -1010,7 +1010,7 @@ mod tests { .clone() .build() .unwrap() - .write_to_crate(name, version, &mod_root, single_file) + .write_to_crate(name, version, mod_root, single_file) .unwrap(); let mut cloned = multi_gen.clone(); @@ -1027,7 +1027,7 @@ mod tests { let result = cloned .build() .unwrap() - .ensure_consistent_crate(name, version, &mod_root, single_file, true) + .ensure_consistent_crate(name, version, mod_root, single_file, true) .is_err(); // ensure inconsistent bindings are detected diff --git a/ethers-contract/tests/it/abigen.rs b/ethers-contract/tests/it/abigen.rs index 92b902f75..09abb6b79 100644 --- a/ethers-contract/tests/it/abigen.rs +++ b/ethers-contract/tests/it/abigen.rs @@ -19,716 +19,720 @@ use std::{ fn assert_codec() {} fn assert_tokenizeable() {} fn assert_call() {} -// -// #[test] -// fn can_gen_human_readable() { -// abigen!( -// SimpleContract, -// r#"[ -// event ValueChanged(address indexed author, string oldValue, string newValue) -// ]"#, -// event_derives(serde::Deserialize, serde::Serialize) -// ); -// assert_eq!("ValueChanged", ValueChangedFilter::name()); -// assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature()); -// } -// -// #[test] -// fn can_gen_not_human_readable() { -// abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json"); -// } -// -// #[test] -// fn can_gen_human_readable_multiple() { -// abigen!( -// SimpleContract1, -// r#"[ -// event ValueChanged1(address indexed author, string oldValue, string newValue) -// ]"#, -// event_derives(serde::Deserialize, serde::Serialize); -// -// SimpleContract2, -// r#"[ -// event ValueChanged2(address indexed author, string oldValue, string newValue) -// ]"#, -// event_derives(serde::Deserialize, serde::Serialize) -// ); -// assert_eq!("ValueChanged1", ValueChanged1Filter::name()); -// assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature()); -// assert_eq!("ValueChanged2", ValueChanged2Filter::name()); -// assert_eq!("ValueChanged2(address,string,string)", ValueChanged2Filter::abi_signature()); -// } -// -// #[test] -// fn can_gen_structs_readable() { -// abigen!( -// SimpleContract, -// r#"[ -// struct Value {address addr; string value;} -// struct Addresses {address[] addr; string s;} -// event ValueChanged(Value indexed old, Value newValue, Addresses _a) -// ]"#, -// event_derives(serde::Deserialize, serde::Serialize) -// ); -// let addr = Addresses { -// addr: vec!["eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()], -// s: "hello".to_string(), -// }; -// let token = addr.clone().into_token(); -// assert_eq!(addr, Addresses::from_token(token).unwrap()); -// -// assert_eq!("ValueChanged", ValueChangedFilter::name()); -// assert_eq!( -// "ValueChanged((address,string),(address,string),(address[],string))", -// ValueChangedFilter::abi_signature() -// ); -// -// assert_codec::(); -// assert_codec::(); -// let encoded = addr.clone().encode(); -// let other = Addresses::decode(&encoded).unwrap(); -// assert_eq!(addr, other); -// } -// -// #[test] -// fn can_gen_structs_with_arrays_readable() { -// abigen!( -// SimpleContract, -// r#"[ -// struct Value {address addr; string value;} -// struct Addresses {address[] addr; string s;} -// event ValueChanged(Value indexed old, Value newValue, Addresses[] _a) -// ]"#, -// event_derives(serde::Deserialize, serde::Serialize) -// ); -// assert_eq!( -// "ValueChanged((address,string),(address,string),(address[],string)[])", -// ValueChangedFilter::abi_signature() -// ); -// -// assert_codec::(); -// assert_codec::(); -// } -// -// #[test] -// fn can_generate_internal_structs() { -// abigen!( -// VerifierContract, -// "ethers-contract/tests/solidity-contracts/verifier_abi.json", -// event_derives(serde::Deserialize, serde::Serialize) -// ); -// assert_tokenizeable::(); -// assert_tokenizeable::(); -// assert_tokenizeable::(); -// -// assert_codec::(); -// assert_codec::(); -// assert_codec::(); -// } -// -// #[test] -// fn can_generate_internal_structs_multiple() { -// // NOTE: nesting here is necessary due to how tests are structured... -// use contract::*; -// mod contract { -// use super::*; -// abigen!( -// VerifierContract, -// "ethers-contract/tests/solidity-contracts/verifier_abi.json", -// event_derives(serde::Deserialize, serde::Serialize); -// -// MyOtherVerifierContract, -// "ethers-contract/tests/solidity-contracts/verifier_abi.json", -// event_derives(serde::Deserialize, serde::Serialize); -// ); -// } -// assert_tokenizeable::(); -// assert_tokenizeable::(); -// assert_tokenizeable::(); -// -// assert_codec::(); -// assert_codec::(); -// assert_codec::(); -// -// let (provider, _) = Provider::mocked(); -// let client = Arc::new(provider); -// -// let g1 = G1Point { x: U256::zero(), y: U256::zero() }; -// let g2 = G2Point { x: [U256::zero(), U256::zero()], y: [U256::zero(), U256::zero()] }; -// let vk = VerifyingKey { -// alfa_1: g1.clone(), -// beta_2: g2.clone(), -// gamma_2: g2.clone(), -// delta_2: g2.clone(), -// ic: vec![g1.clone()], -// }; -// let proof = Proof { a: g1.clone(), b: g2, c: g1 }; -// -// // ensure both contracts use the same types -// let contract = VerifierContract::new(Address::zero(), client.clone()); -// let _ = contract.verify(vec![], proof.clone(), vk.clone()); -// let contract = MyOtherVerifierContract::new(Address::zero(), client); -// let _ = contract.verify(vec![], proof, vk); -// } -// -// #[test] -// fn can_gen_return_struct() { -// abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json"); -// -// fn verify( -// binding: T, -// ) { -// let encoded = binding.clone().encode(); -// let decoded = T::decode(&encoded).unwrap(); -// assert_eq!(binding, decoded); -// } -// -// // just make sure they are accessible and work -// -// let dupe = DupeIntReturn { out_one: 5.into(), out_two: 1234.into() }; -// verify(dupe); -// -// let array = -// ArrayRelayerReturn { outputs: vec![4.into(), 9.into(), 2.into()], some_number: 42.into() -// }; verify(array); -// -// let single = SingleUnnamedReturn(4321.into()); -// verify(single); -// -// // doesnt exist: -// // let nonexistant = CallWithoutReturnDataReturn; -// } -// -// #[test] -// fn can_gen_human_readable_with_structs() { -// abigen!( -// SimpleContract, -// r#"[ -// struct Foo { uint256 x; } -// function foo(Foo memory x) -// function bar(uint256 x, uint256 y, address addr) -// yeet(uint256,uint256,address) -// ]"#, -// event_derives(serde::Deserialize, serde::Serialize) -// ); -// assert_tokenizeable::(); -// assert_codec::(); -// -// let (client, _mock) = Provider::mocked(); -// let contract = SimpleContract::new(Address::default(), Arc::new(client)); -// let f = Foo { x: 100u64.into() }; -// let _ = contract.foo(f); -// -// let call = BarCall { x: 1u64.into(), y: 0u64.into(), addr: Address::random() }; -// let encoded_call = contract.encode("bar", (call.x, call.y, call.addr)).unwrap(); -// assert_eq!(encoded_call, call.clone().encode()); -// let decoded_call = BarCall::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(call, decoded_call); -// -// let contract_call = SimpleContractCalls::Bar(call); -// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(contract_call, decoded_enum); -// assert_eq!(encoded_call, contract_call.encode()); -// -// let call = YeetCall(1u64.into(), 0u64.into(), Address::zero()); -// let encoded_call = contract.encode("yeet", (call.0, call.1, call.2)).unwrap(); -// assert_eq!(encoded_call, call.clone().encode()); -// let decoded_call = YeetCall::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(call, decoded_call); -// -// let contract_call = SimpleContractCalls::Yeet(call.clone()); -// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(contract_call, decoded_enum); -// assert_eq!(contract_call, call.into()); -// assert_eq!(encoded_call, contract_call.encode()); -// -// assert_call::(); -// assert_call::(); -// } -// -// #[test] -// fn can_handle_overloaded_functions() { -// abigen!( -// SimpleContract, -// r#"[ -// getValue() (uint256) -// getValue(uint256 otherValue) (uint256) -// getValue(uint256 otherValue, address addr) (uint256) -// setValue(string, string) -// setValue(string) -// ]"# -// ); -// -// let (provider, _) = Provider::mocked(); -// let client = Arc::new(provider); -// let contract = SimpleContract::new(Address::zero(), client); -// // ensure both functions are callable -// let _ = contract.get_value(); -// let _ = contract.get_value_with_other_value(1337u64.into()); -// let _ = contract.get_value_with_other_value_and_addr(1337u64.into(), Address::zero()); -// -// let call = GetValueCall; -// -// let encoded_call = contract.encode("getValue", ()).unwrap(); -// assert_eq!(encoded_call, call.clone().encode()); -// let decoded_call = GetValueCall::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(call, decoded_call); -// -// let contract_call = SimpleContractCalls::GetValue(call); -// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(contract_call, decoded_enum); -// assert_eq!(encoded_call, contract_call.encode()); -// -// let call = GetValueWithOtherValueCall { other_value: 420u64.into() }; -// -// let encoded_call = contract.encode_with_selector([15, 244, 201, 22], -// call.other_value).unwrap(); assert_eq!(encoded_call, call.clone().encode()); -// let decoded_call = GetValueWithOtherValueCall::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(call, decoded_call); -// -// let contract_call = SimpleContractCalls::GetValueWithOtherValue(call); -// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(contract_call, decoded_enum); -// assert_eq!(encoded_call, contract_call.encode()); -// -// let call = -// GetValueWithOtherValueAndAddrCall { other_value: 420u64.into(), addr: Address::random() -// }; -// -// let encoded_call = -// contract.encode_with_selector([14, 97, 29, 56], (call.other_value, call.addr)).unwrap(); -// let decoded_call = GetValueWithOtherValueAndAddrCall::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(call, decoded_call); -// -// let contract_call = SimpleContractCalls::GetValueWithOtherValueAndAddr(call); -// let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(contract_call, decoded_enum); -// assert_eq!(encoded_call, contract_call.encode()); -// -// let call = SetValue0Call("message".to_string()); -// let _contract_call = SimpleContractCalls::SetValue0(call); -// let call = SetValue1Call("message".to_string(), "message".to_string()); -// let _contract_call = SimpleContractCalls::SetValue1(call); -// -// assert_call::(); -// assert_call::(); -// assert_call::(); -// } -// -// #[test] -// fn can_handle_even_more_overloaded_functions() { -// abigen!( -// ConsoleLog, -// r#"[ -// log() -// log(string, string) -// log(string) -// ]"# -// ); -// -// let _call = Log0Call; -// let _contract_call = ConsoleLogCalls::Log0; -// let call = Log1Call("message".to_string()); -// let _contract_call = ConsoleLogCalls::Log1(call); -// let call = Log2Call("message".to_string(), "message".to_string()); -// let _contract_call = ConsoleLogCalls::Log2(call); -// } -// -// #[tokio::test] -// async fn can_handle_underscore_functions() { -// abigen!( -// SimpleStorage, -// r#"[ -// _hashPuzzle() (uint256) -// ]"#; -// -// SimpleStorage2, -// "ethers-contract/tests/solidity-contracts/simplestorage_abi.json", -// ); -// -// // launch the network & connect to it -// let anvil = Anvil::new().spawn(); -// let from = anvil.addresses()[0]; -// let provider = Provider::try_from(anvil.endpoint()) -// .unwrap() -// .with_sender(from) -// .interval(std::time::Duration::from_millis(10)); -// let client = Arc::new(provider); -// -// let contract = "SimpleStorage"; -// let path = "./tests/solidity-contracts/SimpleStorage.sol"; -// let compiled = Solc::default().compile_source(path).unwrap(); -// let compiled = compiled.get(path, contract).unwrap(); -// let factory = ethers_contract::ContractFactory::new( -// compiled.abi.unwrap().clone(), -// compiled.bytecode().unwrap().clone(), -// client.clone(), -// ); -// let addr = -// factory.deploy("hi".to_string()).unwrap().legacy().send().await.unwrap().address(); -// -// // connect to the contract -// let contract = SimpleStorage::new(addr, client.clone()); -// let contract2 = SimpleStorage2::new(addr, client.clone()); -// -// let res = contract.hash_puzzle().call().await.unwrap(); -// let res2 = contract2.hash_puzzle().call().await.unwrap(); -// let res3 = contract.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); -// let res4 = contract2.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); -// -// // Manual call construction -// use ethers_providers::Middleware; -// // TODO: How do we handle underscores for calls here? -// let data = simple_storage::HashPuzzleCall.encode(); -// let tx = Eip1559TransactionRequest::new().data(data).to(addr); -// let tx = TypedTransaction::Eip1559(tx); -// let res5 = client.call(&tx, None).await.unwrap(); -// let res5 = U256::from(res5.as_ref()); -// assert_eq!(res, 100.into()); -// assert_eq!(res, res2); -// assert_eq!(res, res3); -// assert_eq!(res, res4); -// assert_eq!(res, res5); -// } -// -// #[test] -// fn can_handle_unique_underscore_functions() { -// abigen!( -// ConsoleLog, -// r#"[ -// log(string, string) -// _log(string) -// _log_(string) -// __log__(string) -// __log2__(string) -// ]"# -// ); -// let call = LogCall("message".to_string(), "message".to_string()); -// let _contract_call = ConsoleLogCalls::Log(call); -// -// let call = _LogCall("message".to_string()); -// let _contract_call = ConsoleLogCalls::_Log(call); -// -// let call = _Log_Call("message".to_string()); -// let _contract_call = ConsoleLogCalls::_Log_(call); -// -// let call = __Log__Call("message".to_string()); -// let _contract_call = ConsoleLogCalls::__Log__(call); -// -// let call = Log2Call("message".to_string()); -// let _contract_call = ConsoleLogCalls::Log2(call); -// } -// -// #[test] -// fn can_handle_underscore_numeric() { +fn assert_event() {} + +#[test] +fn can_gen_human_readable() { + abigen!( + SimpleContract, + r#"[ + event ValueChanged(address indexed author, string oldValue, string newValue) + ]"#, + event_derives(serde::Deserialize, serde::Serialize) + ); + assert_eq!("ValueChanged", ValueChangedFilter::name()); + assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature()); +} + +#[test] +fn can_gen_not_human_readable() { + abigen!(VerifierAbiHardhatContract, "./tests/solidity-contracts/verifier_abi_hardhat.json"); +} + +#[test] +fn can_gen_human_readable_multiple() { + abigen!( + SimpleContract1, + r#"[ + event ValueChanged1(address indexed author, string oldValue, string newValue) + ]"#, + event_derives(serde::Deserialize, serde::Serialize); + + SimpleContract2, + r#"[ + event ValueChanged2(address indexed author, string oldValue, string newValue) + ]"#, + event_derives(serde::Deserialize, serde::Serialize) + ); + assert_eq!("ValueChanged1", ValueChanged1Filter::name()); + assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature()); + assert_eq!("ValueChanged2", ValueChanged2Filter::name()); + assert_eq!("ValueChanged2(address,string,string)", ValueChanged2Filter::abi_signature()); +} + +#[test] +fn can_gen_structs_readable() { + abigen!( + SimpleContract, + r#"[ + struct Value {address addr; string value;} + struct Addresses {address[] addr; string s;} + event ValueChanged(Value indexed old, Value newValue, Addresses _a) + ]"#, + event_derives(serde::Deserialize, serde::Serialize) + ); + let addr = Addresses { + addr: vec!["eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()], + s: "hello".to_string(), + }; + let token = addr.clone().into_token(); + assert_eq!(addr, Addresses::from_token(token).unwrap()); + + assert_eq!("ValueChanged", ValueChangedFilter::name()); + assert_eq!( + "ValueChanged((address,string),(address,string),(address[],string))", + ValueChangedFilter::abi_signature() + ); + + assert_codec::(); + assert_codec::(); + let encoded = addr.clone().encode(); + let other = Addresses::decode(&encoded).unwrap(); + assert_eq!(addr, other); +} + +#[test] +fn can_gen_structs_with_arrays_readable() { + abigen!( + SimpleContract, + r#"[ + struct Value {address addr; string value;} + struct Addresses {address[] addr; string s;} + event ValueChanged(Value indexed old, Value newValue, Addresses[] _a) + ]"#, + event_derives(serde::Deserialize, serde::Serialize) + ); + assert_eq!( + "ValueChanged((address,string),(address,string),(address[],string)[])", + ValueChangedFilter::abi_signature() + ); + + assert_codec::(); + assert_codec::(); +} + +#[test] +fn can_generate_internal_structs() { + abigen!( + VerifierContract, + "ethers-contract/tests/solidity-contracts/verifier_abi.json", + event_derives(serde::Deserialize, serde::Serialize) + ); + assert_tokenizeable::(); + assert_tokenizeable::(); + assert_tokenizeable::(); + + assert_codec::(); + assert_codec::(); + assert_codec::(); +} + +#[test] +fn can_generate_internal_structs_multiple() { + // NOTE: nesting here is necessary due to how tests are structured... + use contract::*; + mod contract { + use super::*; + abigen!( + VerifierContract, + "ethers-contract/tests/solidity-contracts/verifier_abi.json", + event_derives(serde::Deserialize, serde::Serialize); + + MyOtherVerifierContract, + "ethers-contract/tests/solidity-contracts/verifier_abi.json", + event_derives(serde::Deserialize, serde::Serialize); + ); + } + assert_tokenizeable::(); + assert_tokenizeable::(); + assert_tokenizeable::(); + + assert_codec::(); + assert_codec::(); + assert_codec::(); + + let (provider, _) = Provider::mocked(); + let client = Arc::new(provider); + + let g1 = G1Point { x: U256::zero(), y: U256::zero() }; + let g2 = G2Point { x: [U256::zero(), U256::zero()], y: [U256::zero(), U256::zero()] }; + let vk = VerifyingKey { + alfa_1: g1.clone(), + beta_2: g2.clone(), + gamma_2: g2.clone(), + delta_2: g2.clone(), + ic: vec![g1.clone()], + }; + let proof = Proof { a: g1.clone(), b: g2, c: g1 }; + + // ensure both contracts use the same types + let contract = VerifierContract::new(Address::zero(), client.clone()); + let _ = contract.verify(vec![], proof.clone(), vk.clone()); + let contract = MyOtherVerifierContract::new(Address::zero(), client); + let _ = contract.verify(vec![], proof, vk); +} + +#[test] +fn can_gen_return_struct() { + abigen!(MultiInputOutput, "ethers-contract/tests/solidity-contracts/MultiInputOutput.json"); + + fn verify( + binding: T, + ) { + let encoded = binding.clone().encode(); + let decoded = T::decode(&encoded).unwrap(); + assert_eq!(binding, decoded); + } + + // just make sure they are accessible and work + + let dupe = DupeIntReturn { out_one: 5.into(), out_two: 1234.into() }; + verify(dupe); + + let array = + ArrayRelayerReturn { outputs: vec![4.into(), 9.into(), 2.into()], some_number: 42.into() }; + verify(array); + + let single = SingleUnnamedReturn(4321.into()); + verify(single); + + // doesnt exist: + // let nonexistant = CallWithoutReturnDataReturn; +} + +#[test] +fn can_gen_human_readable_with_structs() { + abigen!( + SimpleContract, + r#"[ + struct Foo { uint256 x; } + function foo(Foo memory x) + function bar(uint256 x, uint256 y, address addr) + yeet(uint256,uint256,address) + ]"#, + event_derives(serde::Deserialize, serde::Serialize) + ); + assert_tokenizeable::(); + assert_codec::(); + + let (client, _mock) = Provider::mocked(); + let contract = SimpleContract::new(Address::default(), Arc::new(client)); + let f = Foo { x: 100u64.into() }; + let _ = contract.foo(f); + + let call = BarCall { x: 1u64.into(), y: 0u64.into(), addr: Address::random() }; + let encoded_call = contract.encode("bar", (call.x, call.y, call.addr)).unwrap(); + assert_eq!(encoded_call, call.clone().encode()); + let decoded_call = BarCall::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(call, decoded_call); + + let contract_call = SimpleContractCalls::Bar(call); + let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(contract_call, decoded_enum); + assert_eq!(encoded_call, contract_call.encode()); + + let call = YeetCall(1u64.into(), 0u64.into(), Address::zero()); + let encoded_call = contract.encode("yeet", (call.0, call.1, call.2)).unwrap(); + assert_eq!(encoded_call, call.clone().encode()); + let decoded_call = YeetCall::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(call, decoded_call); + + let contract_call = SimpleContractCalls::Yeet(call.clone()); + let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(contract_call, decoded_enum); + assert_eq!(contract_call, call.into()); + assert_eq!(encoded_call, contract_call.encode()); + + assert_call::(); + assert_call::(); +} + +#[test] +fn can_handle_overloaded_functions() { + abigen!( + SimpleContract, + r#"[ + getValue() (uint256) + getValue(uint256 otherValue) (uint256) + getValue(uint256 otherValue, address addr) (uint256) + setValue(string, string) + setValue(string) + ]"# + ); + + let (provider, _) = Provider::mocked(); + let client = Arc::new(provider); + let contract = SimpleContract::new(Address::zero(), client); + // ensure both functions are callable + let _ = contract.get_value(); + let _ = contract.get_value_with_other_value(1337u64.into()); + let _ = contract.get_value_with_other_value_and_addr(1337u64.into(), Address::zero()); + + let call = GetValueCall; + + let encoded_call = contract.encode("getValue", ()).unwrap(); + assert_eq!(encoded_call, call.clone().encode()); + let decoded_call = GetValueCall::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(call, decoded_call); + + let contract_call = SimpleContractCalls::GetValue(call); + let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(contract_call, decoded_enum); + assert_eq!(encoded_call, contract_call.encode()); + + let call = GetValueWithOtherValueCall { other_value: 420u64.into() }; + + let encoded_call = contract.encode_with_selector([15, 244, 201, 22], call.other_value).unwrap(); + assert_eq!(encoded_call, call.clone().encode()); + let decoded_call = GetValueWithOtherValueCall::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(call, decoded_call); + + let contract_call = SimpleContractCalls::GetValueWithOtherValue(call); + let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(contract_call, decoded_enum); + assert_eq!(encoded_call, contract_call.encode()); + + let call = + GetValueWithOtherValueAndAddrCall { other_value: 420u64.into(), addr: Address::random() }; + + let encoded_call = + contract.encode_with_selector([14, 97, 29, 56], (call.other_value, call.addr)).unwrap(); + let decoded_call = GetValueWithOtherValueAndAddrCall::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(call, decoded_call); + + let contract_call = SimpleContractCalls::GetValueWithOtherValueAndAddr(call); + let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(contract_call, decoded_enum); + assert_eq!(encoded_call, contract_call.encode()); + + let call = SetValue0Call("message".to_string()); + let _contract_call = SimpleContractCalls::SetValue0(call); + let call = SetValue1Call("message".to_string(), "message".to_string()); + let _contract_call = SimpleContractCalls::SetValue1(call); + + assert_call::(); + assert_call::(); + assert_call::(); +} + +#[test] +fn can_handle_even_more_overloaded_functions() { + abigen!( + ConsoleLog, + r#"[ + log() + log(string, string) + log(string) + ]"# + ); + + let _call = Log0Call; + let _contract_call = ConsoleLogCalls::Log0; + let call = Log1Call("message".to_string()); + let _contract_call = ConsoleLogCalls::Log1(call); + let call = Log2Call("message".to_string(), "message".to_string()); + let _contract_call = ConsoleLogCalls::Log2(call); +} + +#[tokio::test] +async fn can_handle_underscore_functions() { + abigen!( + SimpleStorage, + r#"[ + _hashPuzzle() (uint256) + ]"#; + + SimpleStorage2, + "ethers-contract/tests/solidity-contracts/simplestorage_abi.json", + ); + + // launch the network & connect to it + let anvil = Anvil::new().spawn(); + let from = anvil.addresses()[0]; + let provider = Provider::try_from(anvil.endpoint()) + .unwrap() + .with_sender(from) + .interval(std::time::Duration::from_millis(10)); + let client = Arc::new(provider); + + let contract = "SimpleStorage"; + let path = "./tests/solidity-contracts/SimpleStorage.sol"; + let compiled = Solc::default().compile_source(path).unwrap(); + let compiled = compiled.get(path, contract).unwrap(); + let factory = ethers_contract::ContractFactory::new( + compiled.abi.unwrap().clone(), + compiled.bytecode().unwrap().clone(), + client.clone(), + ); + let addr = factory.deploy("hi".to_string()).unwrap().legacy().send().await.unwrap().address(); + + // connect to the contract + let contract = SimpleStorage::new(addr, client.clone()); + let contract2 = SimpleStorage2::new(addr, client.clone()); + + let res = contract.hash_puzzle().call().await.unwrap(); + let res2 = contract2.hash_puzzle().call().await.unwrap(); + let res3 = contract.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); + let res4 = contract2.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap(); + + // Manual call construction + use ethers_providers::Middleware; + // TODO: How do we handle underscores for calls here? + let data = simple_storage::HashPuzzleCall.encode(); + let tx = Eip1559TransactionRequest::new().data(data).to(addr); + let tx = TypedTransaction::Eip1559(tx); + let res5 = client.call(&tx, None).await.unwrap(); + let res5 = U256::from(res5.as_ref()); + assert_eq!(res, 100.into()); + assert_eq!(res, res2); + assert_eq!(res, res3); + assert_eq!(res, res4); + assert_eq!(res, res5); +} + +#[test] +fn can_handle_unique_underscore_functions() { + abigen!( + ConsoleLog, + r#"[ + log(string, string) + _log(string) + _log_(string) + __log__(string) + __log2__(string) + ]"# + ); + let call = LogCall("message".to_string(), "message".to_string()); + let _contract_call = ConsoleLogCalls::Log(call); + + let call = _LogCall("message".to_string()); + let _contract_call = ConsoleLogCalls::_Log(call); + + let call = _Log_Call("message".to_string()); + let _contract_call = ConsoleLogCalls::_Log_(call); + + let call = __Log__Call("message".to_string()); + let _contract_call = ConsoleLogCalls::__Log__(call); + + let call = Log2Call("message".to_string()); + let _contract_call = ConsoleLogCalls::Log2(call); +} + +#[test] +fn can_handle_underscore_numeric() { + abigen!( + Test, + r#"[ + _100pct(string) + ]"# + ); + let _call = _100PctCall("message".to_string()); + + let provider = Arc::new(Provider::new(MockProvider::new())); + let contract = Test::new(Address::default(), Arc::clone(&provider)); + // NOTE: this seems to be weird behaviour of `Inflector::to_snake_case` which turns "100pct" -> + // "10_0pct" + let _call = contract._10_0pct("hello".to_string()); +} + +#[test] +fn can_handle_duplicates_with_same_name() { + abigen!( + ConsoleLog, + r#"[ + log() + log(uint p0) + log(string p0) + ]"# + ); + + let call = Log0Call; + let _contract_call = ConsoleLogCalls::Log0(call); + + let call = Log1Call { p_0: 100.into() }; + let _contract_call = ConsoleLogCalls::Log1(call); + + let call = Log2Call { p_0: "message".to_string() }; + let _contract_call = ConsoleLogCalls::Log2(call); +} + +#[test] +fn can_abigen_console_sol() { + abigen!(Console, "ethers-contract/tests/solidity-contracts/console.json",); +} + +#[test] +fn can_generate_nested_types() { + abigen!( + Test, + r#"[ + struct Outer {Inner inner; uint256[] arr;} + struct Inner {uint256 inner;} + function myfun(Outer calldata a) + ]"#, + ); + + assert_eq!(MyfunCall::abi_signature(), "myfun(((uint256),uint256[]))"); + + let (client, _mock) = Provider::mocked(); + let contract = Test::new(Address::default(), Arc::new(client)); + + let inner = Inner { inner: 100u64.into() }; + let a = Outer { inner, arr: vec![101u64.into()] }; + let _ = contract.myfun(a.clone()); + + let call = MyfunCall { a: a.clone() }; + let encoded_call = contract.encode("myfun", (a,)).unwrap(); + assert_eq!(encoded_call, call.clone().encode()); + let decoded_call = MyfunCall::decode(encoded_call.as_ref()).unwrap(); + assert_eq!(call, decoded_call); +} + +#[test] +fn can_handle_different_calls() { + abigen!( + Test, + r#"[ + function fooBar() + function FOO_BAR() + ]"#, + ); + + let (client, _mock) = Provider::mocked(); + let contract = Test::new(Address::default(), Arc::new(client)); + + let _ = contract.fooBar(); + let _ = contract.FOO_BAR(); +} + +#[test] +fn can_handle_case_sensitive_calls() { + abigen!( + StakedOHM, + r#"[ + index() + INDEX() + ]"#, + ); + + let (client, _mock) = Provider::mocked(); + let contract = StakedOHM::new(Address::default(), Arc::new(client)); + + let _ = contract.index(); + let _ = contract.INDEX(); +} + +#[tokio::test] +async fn can_deploy_greeter() { + abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",); + let anvil = Anvil::new().spawn(); + let from = anvil.addresses()[0]; + let provider = Provider::try_from(anvil.endpoint()) + .unwrap() + .with_sender(from) + .interval(std::time::Duration::from_millis(10)); + let client = Arc::new(provider); + + let greeter_contract = + Greeter::deploy(client, "Hello World!".to_string()).unwrap().legacy().send().await.unwrap(); + + let greeting = greeter_contract.greet().call().await.unwrap(); + assert_eq!("Hello World!", greeting); +} + +#[tokio::test] +async fn can_abiencoderv2_output() { + abigen!(AbiEncoderv2Test, "ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json",); + let anvil = Anvil::new().spawn(); + let from = anvil.addresses()[0]; + let provider = Provider::try_from(anvil.endpoint()) + .unwrap() + .with_sender(from) + .interval(std::time::Duration::from_millis(10)); + let client = Arc::new(provider); + + let contract = "AbiencoderV2Test"; + let path = "./tests/solidity-contracts/Abiencoderv2Test.sol"; + let compiled = Solc::default().compile_source(path).unwrap(); + let compiled = compiled.get(path, contract).unwrap(); + let factory = ethers_contract::ContractFactory::new( + compiled.abi.unwrap().clone(), + compiled.bytecode().unwrap().clone(), + client.clone(), + ); + let addr = factory.deploy(()).unwrap().legacy().send().await.unwrap().address(); + + let contract = AbiEncoderv2Test::new(addr, client.clone()); + let person = Person { name: "Alice".to_string(), age: 20u64.into() }; + + let res = contract.default_person().call().await.unwrap(); + assert_eq!(res, person); +} + +// 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() { // abigen!( -// Test, -// r#"[ -// _100pct(string) -// ]"# +// MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7"; +// MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2"; // ); -// let _call = _100PctCall("message".to_string()); // // let provider = Arc::new(Provider::new(MockProvider::new())); -// let contract = Test::new(Address::default(), Arc::clone(&provider)); -// // NOTE: this seems to be weird behaviour of `Inflector::to_snake_case` which turns "100pct" -// -> // "10_0pct" -// let _call = contract._10_0pct("hello".to_string()); -// } -// -// #[test] -// fn can_handle_duplicates_with_same_name() { -// abigen!( -// ConsoleLog, -// r#"[ -// log() -// log(uint p0) -// log(string p0) -// ]"# -// ); -// -// let call = Log0Call; -// let _contract_call = ConsoleLogCalls::Log0(call); -// -// let call = Log1Call { p_0: 100.into() }; -// let _contract_call = ConsoleLogCalls::Log1(call); -// -// let call = Log2Call { p_0: "message".to_string() }; -// let _contract_call = ConsoleLogCalls::Log2(call); -// } -// -// #[test] -// fn can_abigen_console_sol() { -// abigen!(Console, "ethers-contract/tests/solidity-contracts/console.json",); -// } -// -// #[test] -// fn can_generate_nested_types() { -// abigen!( -// Test, -// r#"[ -// struct Outer {Inner inner; uint256[] arr;} -// struct Inner {uint256 inner;} -// function myfun(Outer calldata a) -// ]"#, -// ); -// -// assert_eq!(MyfunCall::abi_signature(), "myfun(((uint256),uint256[]))"); -// -// let (client, _mock) = Provider::mocked(); -// let contract = Test::new(Address::default(), Arc::new(client)); -// -// let inner = Inner { inner: 100u64.into() }; -// let a = Outer { inner, arr: vec![101u64.into()] }; -// let _ = contract.myfun(a.clone()); -// -// let call = MyfunCall { a: a.clone() }; -// let encoded_call = contract.encode("myfun", (a,)).unwrap(); -// assert_eq!(encoded_call, call.clone().encode()); -// let decoded_call = MyfunCall::decode(encoded_call.as_ref()).unwrap(); -// assert_eq!(call, decoded_call); -// } -// -// #[test] -// fn can_handle_different_calls() { -// abigen!( -// Test, -// r#"[ -// function fooBar() -// function FOO_BAR() -// ]"#, -// ); -// -// let (client, _mock) = Provider::mocked(); -// let contract = Test::new(Address::default(), Arc::new(client)); -// -// let _ = contract.fooBar(); -// let _ = contract.FOO_BAR(); -// } -// -// #[test] -// fn can_handle_case_sensitive_calls() { -// abigen!( -// StakedOHM, -// r#"[ -// index() -// INDEX() -// ]"#, -// ); -// -// let (client, _mock) = Provider::mocked(); -// let contract = StakedOHM::new(Address::default(), Arc::new(client)); -// -// let _ = contract.index(); -// let _ = contract.INDEX(); -// } -// -// #[tokio::test] -// async fn can_deploy_greeter() { -// abigen!(Greeter, "ethers-contract/tests/solidity-contracts/greeter.json",); -// let anvil = Anvil::new().spawn(); -// let from = anvil.addresses()[0]; -// let provider = Provider::try_from(anvil.endpoint()) -// .unwrap() -// .with_sender(from) -// .interval(std::time::Duration::from_millis(10)); -// let client = Arc::new(provider); -// -// let greeter_contract = -// Greeter::deploy(client, "Hello -// World!".to_string()).unwrap().legacy().send().await.unwrap(); -// -// let greeting = greeter_contract.greet().call().await.unwrap(); -// assert_eq!("Hello World!", greeting); -// } -// -// #[tokio::test] -// async fn can_abiencoderv2_output() { -// abigen!(AbiEncoderv2Test, -// "ethers-contract/tests/solidity-contracts/abiencoderv2test_abi.json",); let anvil = -// Anvil::new().spawn(); let from = anvil.addresses()[0]; -// let provider = Provider::try_from(anvil.endpoint()) -// .unwrap() -// .with_sender(from) -// .interval(std::time::Duration::from_millis(10)); -// let client = Arc::new(provider); -// -// let contract = "AbiencoderV2Test"; -// let path = "./tests/solidity-contracts/Abiencoderv2Test.sol"; -// let compiled = Solc::default().compile_source(path).unwrap(); -// let compiled = compiled.get(path, contract).unwrap(); -// let factory = ethers_contract::ContractFactory::new( -// compiled.abi.unwrap().clone(), -// compiled.bytecode().unwrap().clone(), -// client.clone(), -// ); -// let addr = factory.deploy(()).unwrap().legacy().send().await.unwrap().address(); -// -// let contract = AbiEncoderv2Test::new(addr, client.clone()); -// let person = Person { name: "Alice".to_string(), age: 20u64.into() }; -// -// let res = contract.default_person().call().await.unwrap(); -// assert_eq!(res, person); -// } -// -// // 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() { -// // abigen!( -// // MyContract, "etherscan:0xdAC17F958D2ee523a2206206994597C13D831ec7"; -// // MyContract2, "etherscan:0x8418bb725b3ac45ec8fff3791dd8b4e0480cc2a2"; -// // ); -// // -// // let provider = Arc::new(Provider::new(MockProvider::new())); -// // let _contract = MyContract::new(Address::default(), Arc::clone(&provider)); -// // let _contract = MyContract2::new(Address::default(), provider); -// // } -// -// #[test] -// fn can_gen_reserved_word_field_names() { -// abigen!( -// Test, -// r#"[ -// struct Foo { uint256 ref; } -// ]"#, -// ); -// -// let _foo = Foo { ref_: U256::default() }; -// } -// -// #[test] -// fn can_handle_overloaded_events() { -// abigen!( -// SimpleContract, -// r#"[ -// event ActionPaused(string cToken, string action, bool pauseState) -// event ActionPaused(string action, bool pauseState) -// ]"# -// ); -// -// let _ev1 = ActionPaused1Filter { -// c_token: "ctoken".to_string(), -// action: "action".to_string(), -// pause_state: false, -// }; -// let _ev2 = ActionPaused2Filter { action: "action".to_string(), pause_state: false }; -// } -// -// #[tokio::test] -// #[cfg(not(feature = "celo"))] -// async fn can_send_struct_param() { -// abigen!(StructContract, "./tests/solidity-contracts/StructContract.json"); -// -// let server = Anvil::new().spawn(); -// let wallet: LocalWallet = server.keys()[0].clone().into(); -// let provider = Provider::try_from(server.endpoint()).unwrap(); -// let client = -// Arc::new(SignerMiddleware::new(provider, wallet.with_chain_id(Chain::AnvilHardhat))); -// -// let contract = StructContract::deploy(client, ()).unwrap().legacy().send().await.unwrap(); -// -// let point = Point { x: 1337u64.into(), y: 0u64.into() }; -// let tx = contract.submit_point(point).legacy(); -// let tx = tx.send().await.unwrap().await.unwrap().unwrap(); -// assert_eq!(tx.logs.len(), 1); -// -// let logs: Vec = contract.event().from_block(0u64).query().await.unwrap(); -// assert_eq!(logs.len(), 1); -// } -// -// #[test] -// fn can_gen_seaport() { -// abigen!(Seaport, "./tests/solidity-contracts/seaport.json"); -// -// assert_eq!( -// FulfillAdvancedOrderCall::abi_signature(), -// "fulfillAdvancedOrder(((address,address,(uint8,address,uint256,uint256,uint256)[],(uint8, -// address,uint256,uint256,uint256,address)[],uint8,uint256,uint256,bytes32,uint256,bytes32, -// uint256),uint120,uint120,bytes,bytes),(uint256,uint8,uint256,uint256,bytes32[])[],bytes32, -// address)" ); -// assert_eq!(hex::encode(FulfillAdvancedOrderCall::selector()), "e7acab24"); -// -// assert_codec::(); -// let err = SeaportErrors::BadContractSignature(BadContractSignature::default()); -// -// let encoded = err.clone().encode(); -// assert_eq!(err, SeaportErrors::decode(encoded).unwrap()); -// -// let err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet { -// order_index: U256::zero(), -// consideration_index: U256::zero(), -// shortfall_amount: U256::zero(), -// }); -// } -// -// #[test] -// fn can_generate_to_string_overload() { -// abigen!( -// ToString, -// r#"[ -// toString(bytes) -// toString(address) -// toString(uint256) -// toString(int256) -// toString(bytes32) -// toString(bool) -// ]"# -// ); -// -// match ToStringCalls::ToString0(ToString0Call(Default::default())) { -// ToStringCalls::ToString0(_) => {} -// ToStringCalls::ToString1(_) => {} -// ToStringCalls::ToString2(_) => {} -// ToStringCalls::ToString3(_) => {} -// ToStringCalls::ToString4(_) => {} -// ToStringCalls::ToString5(_) => {} -// }; -// } -// -// #[test] -// fn can_generate_large_event() { -// abigen!(NewSale, "ethers-contract/tests/solidity-contracts/sale.json"); -// } -// -// #[test] -// fn can_generate_large_output_struct() { -// abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json"); -// -// let r = GetByIdReturn(Info::default()); -// } -// -// #[test] -// fn gen_complex_function() { -// abigen!( -// WyvernExchangeV1, -// r#"[ -// function atomicMatch_(address[14] addrs, uint[18] uints, uint8[8] -// feeMethodsSidesKindsHowToCalls, bytes calldataBuy, bytes calldataSell, bytes -// replacementPatternBuy, bytes replacementPatternSell, bytes staticExtradataBuy, bytes -// staticExtradataSell, uint8[2] vs, bytes32[5] rssMetadata) public payable ]"#, -// ); -// } -// -// #[test] -// 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::(); +// let _contract = MyContract::new(Address::default(), Arc::clone(&provider)); +// let _contract = MyContract2::new(Address::default(), provider); // } +#[test] +fn can_gen_reserved_word_field_names() { + abigen!( + Test, + r#"[ + struct Foo { uint256 ref; } + ]"#, + ); + + let _foo = Foo { ref_: U256::default() }; +} + +#[test] +fn can_handle_overloaded_events() { + abigen!( + SimpleContract, + r#"[ + event ActionPaused(string cToken, string action, bool pauseState) + event ActionPaused(string action, bool pauseState) + ]"# + ); + + let _ev1 = ActionPaused1Filter { + c_token: "ctoken".to_string(), + action: "action".to_string(), + pause_state: false, + }; + let _ev2 = ActionPaused2Filter { action: "action".to_string(), pause_state: false }; +} + +#[tokio::test] +#[cfg(not(feature = "celo"))] +async fn can_send_struct_param() { + abigen!(StructContract, "./tests/solidity-contracts/StructContract.json"); + + let server = Anvil::new().spawn(); + let wallet: LocalWallet = server.keys()[0].clone().into(); + let provider = Provider::try_from(server.endpoint()).unwrap(); + let client = + Arc::new(SignerMiddleware::new(provider, wallet.with_chain_id(Chain::AnvilHardhat))); + + let contract = StructContract::deploy(client, ()).unwrap().legacy().send().await.unwrap(); + + let point = Point { x: 1337u64.into(), y: 0u64.into() }; + let tx = contract.submit_point(point).legacy(); + let tx = tx.send().await.unwrap().await.unwrap().unwrap(); + assert_eq!(tx.logs.len(), 1); + + let logs: Vec = contract.event().from_block(0u64).query().await.unwrap(); + assert_eq!(logs.len(), 1); +} + +#[test] +fn can_gen_seaport() { + abigen!(Seaport, "./tests/solidity-contracts/seaport.json"); + + assert_eq!( + FulfillAdvancedOrderCall::abi_signature(), + "fulfillAdvancedOrder(((address,address,(uint8,address,uint256,uint256,uint256)[],(uint8,address,uint256,uint256,uint256,address)[],uint8,uint256,uint256,bytes32,uint256,bytes32,uint256),uint120,uint120,bytes,bytes),(uint256,uint8,uint256,uint256,bytes32[])[],bytes32,address)" + ); + assert_eq!(hex::encode(FulfillAdvancedOrderCall::selector()), "e7acab24"); + + assert_codec::(); + let err = SeaportErrors::BadContractSignature(BadContractSignature::default()); + + let encoded = err.clone().encode(); + assert_eq!(err, SeaportErrors::decode(encoded).unwrap()); + + let err = SeaportErrors::ConsiderationNotMet(ConsiderationNotMet { + order_index: U256::zero(), + consideration_index: U256::zero(), + shortfall_amount: U256::zero(), + }); +} + +#[test] +fn can_generate_to_string_overload() { + abigen!( + ToString, + r#"[ + toString(bytes) + toString(address) + toString(uint256) + toString(int256) + toString(bytes32) + toString(bool) + ]"# + ); + + match ToStringCalls::ToString0(ToString0Call(Default::default())) { + ToStringCalls::ToString0(_) => {} + ToStringCalls::ToString1(_) => {} + ToStringCalls::ToString2(_) => {} + ToStringCalls::ToString3(_) => {} + ToStringCalls::ToString4(_) => {} + ToStringCalls::ToString5(_) => {} + }; +} + +#[test] +fn can_generate_large_event() { + abigen!(NewSale, "ethers-contract/tests/solidity-contracts/sale.json"); +} + +#[test] +fn can_generate_large_output_struct() { + abigen!(LargeOutputStruct, "ethers-contract/tests/solidity-contracts/LargeStruct.json"); + + let r = GetByIdReturn(Info::default()); +} + +#[test] +fn gen_complex_function() { + abigen!( + WyvernExchangeV1, + r#"[ + function atomicMatch_(address[14] addrs, uint[18] uints, uint8[8] feeMethodsSidesKindsHowToCalls, bytes calldataBuy, bytes calldataSell, bytes replacementPatternBuy, bytes replacementPatternSell, bytes staticExtradataBuy, bytes staticExtradataSell, uint8[2] vs, bytes32[5] rssMetadata) public payable + ]"#, + ); +} + +#[test] +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::(); +} + #[test] fn can_generate_event_with_structs() { + /* + contract MyContract { + struct MyStruct {uint256 a; uint256 b; } + event MyEvent(MyStruct, uint256); + } + */ abigen!(MyContract, "ethers-contract/tests/solidity-contracts/EventWithStruct.json"); -} + + let _filter = MyEventFilter { p0: MyStruct::default(), c: U256::zero() }; + assert_eq!("MyEvent((uint256,uint256),uint256)", MyEventFilter::abi_signature()); + assert_event::(); +} \ No newline at end of file diff --git a/ethers-core/src/abi/human_readable/mod.rs b/ethers-core/src/abi/human_readable/mod.rs index ca832e8f7..f4d7f126c 100644 --- a/ethers-core/src/abi/human_readable/mod.rs +++ b/ethers-core/src/abi/human_readable/mod.rs @@ -18,6 +18,11 @@ pub struct AbiParser { /// (function name, param name) -> struct which are the identifying properties we get the name /// from ethabi. pub function_params: HashMap<(String, String), String>, + /// (event name, idx) -> struct which are the identifying properties we get the name + /// from ethabi. + /// + /// Note: we need to map the index of the event here because events can contain nameless inputs + pub event_params: HashMap<(String, usize), String>, /// (function name) -> Vec all structs the function returns pub outputs: HashMap>, } @@ -170,12 +175,13 @@ impl AbiParser { structs: structs.into_iter().map(|s| (s.name().to_string(), s)).collect(), struct_tuples: HashMap::new(), function_params: Default::default(), + event_params: Default::default(), outputs: Default::default(), } } /// Parses a solidity event declaration from `event (args*) anonymous?` - pub fn parse_event(&self, s: &str) -> Result { + pub fn parse_event(&mut self, s: &str) -> Result { let mut event = s.trim(); if !event.starts_with("event ") { bail!("Not an event `{}`", s) @@ -208,8 +214,20 @@ impl AbiParser { .split(',') .map(|e| self.parse_event_arg(e)) .collect::, _>>()? + .into_iter() + .enumerate() + .map(|(idx, (input, struct_name))| { + if let Some(struct_name) = struct_name { + // keep track of the user defined struct of that param + self.event_params.insert((name.clone(), idx), struct_name); + } + input + }) + .collect() }; - return Ok(Event { name, inputs, anonymous }) + + let event = Event { name, inputs, anonymous }; + return Ok(event) } Some(' ') | Some('\t') => continue, Some(c) => { @@ -220,7 +238,9 @@ impl AbiParser { } /// Parse a single event param - fn parse_event_arg(&self, input: &str) -> Result { + /// + /// See [`Self::parse_type`] + fn parse_event_arg(&self, input: &str) -> Result<(EventParam, Option)> { let mut iter = input.trim().rsplitn(3, is_whitespace); let mut indexed = false; let mut name = @@ -246,7 +266,8 @@ impl AbiParser { name = ""; } - Ok(EventParam { name: name.to_string(), indexed, kind: self.parse_type(type_str)?.0 }) + let (kind, user_ty) = self.parse_type(type_str)?; + Ok((EventParam { name: name.to_string(), indexed, kind }, user_ty)) } /// Returns the parsed function from the input string From 7bd1cf135085c4669cdec1ed1bed323260bc949b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 6 Sep 2022 19:32:47 +0200 Subject: [PATCH 3/6] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec588d094..241c9ae1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ ### Unreleased +- Use corresponding rust structs for event fields if they're solidity structs [#1674](https://github.com/gakonst/ethers-rs/pull/1674) - Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564) - generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549) - Support overloaded events From e8f176a7b77c69421cc00ef7e41578c1df372a8b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 6 Sep 2022 19:33:14 +0200 Subject: [PATCH 4/6] chore: rustfmt --- ethers-contract/tests/it/abigen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethers-contract/tests/it/abigen.rs b/ethers-contract/tests/it/abigen.rs index 09abb6b79..d09c2b969 100644 --- a/ethers-contract/tests/it/abigen.rs +++ b/ethers-contract/tests/it/abigen.rs @@ -735,4 +735,4 @@ fn can_generate_event_with_structs() { let _filter = MyEventFilter { p0: MyStruct::default(), c: U256::zero() }; assert_eq!("MyEvent((uint256,uint256),uint256)", MyEventFilter::abi_signature()); assert_event::(); -} \ No newline at end of file +} From 8a13e97efa8a6ca28da1d6a3c3c1aefb52439a80 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 6 Sep 2022 19:35:32 +0200 Subject: [PATCH 5/6] fix broken tests --- ethers-core/src/abi/human_readable/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethers-core/src/abi/human_readable/mod.rs b/ethers-core/src/abi/human_readable/mod.rs index f4d7f126c..4b5125ffe 100644 --- a/ethers-core/src/abi/human_readable/mod.rs +++ b/ethers-core/src/abi/human_readable/mod.rs @@ -686,12 +686,12 @@ mod tests { #[test] fn parse_event_input() { assert_eq!( - AbiParser::default().parse_event_arg("address indexed x").unwrap(), + AbiParser::default().parse_event_arg("address indexed x").unwrap().0, EventParam { name: "x".to_string(), kind: ParamType::Address, indexed: true } ); assert_eq!( - AbiParser::default().parse_event_arg("address x").unwrap(), + AbiParser::default().parse_event_arg("address x").unwrap().0, EventParam { name: "x".to_string(), kind: ParamType::Address, indexed: false } ); } From 36c965168aae1b02a7a0cd19b6ff85c1c3d5c3db Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 6 Sep 2022 19:37:39 +0200 Subject: [PATCH 6/6] chore(clippy): make clippy happy --- .../ethers-contract-abigen/src/contract/structs.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ethers-contract/ethers-contract-abigen/src/contract/structs.rs b/ethers-contract/ethers-contract-abigen/src/contract/structs.rs index 0eaed5d9e..c99f84472 100644 --- a/ethers-contract/ethers-contract-abigen/src/contract/structs.rs +++ b/ethers-contract/ethers-contract-abigen/src/contract/structs.rs @@ -253,10 +253,10 @@ impl InternalStructs { let mut outputs = HashMap::new(); let mut event_params = HashMap::new(); let mut structs = HashMap::new(); - for item in abi.into_iter().filter(|item| match item.type_field.as_str() { - "constructor" | "function" | "event" => true, - _ => false, - }) { + for item in abi + .into_iter() + .filter(|item| matches!(item.type_field.as_str(), "constructor" | "function" | "event")) + { let is_event = item.type_field == "event"; if let Some(name) = item.name {