diff --git a/ethers-contract/tests/it/common/mod.rs b/ethers-contract/tests/it/common.rs similarity index 83% rename from ethers-contract/tests/it/common/mod.rs rename to ethers-contract/tests/it/common.rs index 1d6ab901..5ef32aaa 100644 --- a/ethers-contract/tests/it/common/mod.rs +++ b/ethers-contract/tests/it/common.rs @@ -1,17 +1,9 @@ -#![cfg(not(target_arch = "wasm32"))] -#![allow(dead_code)] - -#[cfg(feature = "abigen")] -use ethers_core::types::Address; - -#[cfg(feature = "abigen")] -use ethers_contract::EthEvent; - -#[cfg(feature = "abigen")] -mod derive; - -use ethers_contract::{Contract, ContractFactory}; -use ethers_core::{abi::Abi, types::Bytes, utils::AnvilInstance}; +use ethers_contract::{Contract, ContractFactory, EthEvent}; +use ethers_core::{ + abi::Abi, + types::{Address, Bytes}, + utils::AnvilInstance, +}; use ethers_providers::{Http, Middleware, Provider}; use ethers_solc::Solc; use std::{convert::TryFrom, sync::Arc, time::Duration}; diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs index a8cc689c..ccdef851 100644 --- a/ethers-contract/tests/it/contract.rs +++ b/ethers-contract/tests/it/contract.rs @@ -1,951 +1,927 @@ -#![allow(unused)] +use super::common::*; +use ethers_contract::{ + abigen, ContractFactory, ContractInstance, EthAbiType, EthEvent, LogMeta, Multicall, + MulticallError, MulticallVersion, +}; +use ethers_core::{ + abi::{encode, AbiEncode, Token, Tokenizable}, + types::{ + transaction::eip712::Eip712, Address, BlockId, Bytes, Filter, ValueOrArray, H160, H256, + I256, U256, + }, + utils::{keccak256, Anvil}, +}; +use ethers_derive_eip712::*; +use ethers_providers::{Http, Middleware, MiddlewareError, Provider, StreamExt}; +use ethers_signers::{LocalWallet, Signer}; +use std::{convert::TryFrom, iter::FromIterator, sync::Arc, time::Duration}; -pub use crate::common::*; -use ethers_contract::{abigen, ContractFactory, EthAbiType}; -use ethers_core::types::{Filter, ValueOrArray, H256}; +#[derive(Debug)] +pub struct NonClone { + m: M, +} -#[cfg(not(feature = "celo"))] -mod eth_tests { - use super::*; - use ethers_contract::{ - ContractInstance, EthEvent, LogMeta, Multicall, MulticallError, MulticallVersion, - }; - use ethers_core::{ - abi::{encode, AbiEncode, Detokenize, Token, Tokenizable}, - types::{transaction::eip712::Eip712, Address, BlockId, Bytes, H160, I256, U256}, - utils::{keccak256, Anvil}, - }; - use ethers_derive_eip712::*; - use ethers_providers::{ - Http, Middleware, MiddlewareError, PendingTransaction, Provider, StreamExt, - }; - use ethers_signers::{LocalWallet, Signer}; - use std::{convert::TryFrom, iter::FromIterator, sync::Arc, time::Duration}; +#[derive(Debug)] +pub struct MwErr(M::Error); - #[derive(Debug)] - pub struct NonClone { - m: M, +impl MiddlewareError for MwErr +where + M: Middleware, +{ + type Inner = M::Error; + + fn from_err(src: M::Error) -> Self { + Self(src) } - #[derive(Debug)] - pub struct MwErr(M::Error); - - impl MiddlewareError for MwErr - where - M: Middleware, - { - type Inner = M::Error; - - fn from_err(src: M::Error) -> Self { - Self(src) - } - - fn as_inner(&self) -> Option<&Self::Inner> { - Some(&self.0) - } - } - - impl std::fmt::Display for MwErr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Ok(()) - } - } - impl std::error::Error for MwErr {} - - impl Middleware for NonClone { - type Error = MwErr; - - type Provider = M::Provider; - - type Inner = M; - - fn inner(&self) -> &Self::Inner { - &self.m - } - } - - // this is not a test. It is a compile check. :) - // It exists to ensure that trait bounds on contract internal behave as - // expected. It should not be run - fn it_compiles() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - - // launch anvil - let anvil = Anvil::new().spawn(); - - let client = Provider::::try_from(anvil.endpoint()) - .unwrap() - .interval(Duration::from_millis(10u64)); - - // Works (B == M, M: Clone) - let c: ContractInstance<&Provider, Provider> = - ContractInstance::new(H160::default(), abi.clone(), &client); - - let _ = c.method::<(), ()>("notARealMethod", ()); - - // Works (B == &M, M: Clone) - let c: ContractInstance, Provider> = - ContractInstance::new(H160::default(), abi.clone(), client.clone()); - - let _ = c.method::<(), ()>("notARealMethod", ()); - - let non_clone_mware = NonClone { m: client }; - - // Works (B == &M, M: !Clone) - let c: ContractInstance<&NonClone>, NonClone>> = - ContractInstance::new(H160::default(), abi, &non_clone_mware); - - let _ = c.method::<(), ()>("notARealMethod", ()); - - // // Fails (B == M, M: !Clone) - // let c: ContractInternal>, NonClone>> = - // ContractInternal::new(H160::default(), abi, non_clone_mware); - - // let _ = c.method::<(), ()>("notARealMethod", ()); - } - - #[tokio::test] - async fn deploy_and_call_contract() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - - // launch anvil - let anvil = Anvil::new().spawn(); - - // Instantiate the clients. We assume that clients consume the provider and the wallet - // (which makes sense), so for multi-client tests, you must clone the provider. - let addrs = anvil.addresses().to_vec(); - let addr2 = addrs[1]; - let client = connect(&anvil, 0); - let client2 = connect(&anvil, 1); - - // create a factory which will be used to deploy instances of the contract - let factory = ContractFactory::new(abi, bytecode, client.clone()); - - // `send` consumes the deployer so it must be cloned for later re-use - // (practically it's not expected that you'll need to deploy multiple instances of - // the _same_ deployer, so it's fine to clone here from a dev UX vs perf tradeoff) - let deployer = factory.deploy("initial value".to_string()).unwrap().legacy(); - // dry runs the deployment of the contract. takes the deployer by reference, no need to - // clone. - deployer.call().await.unwrap(); - let (contract, receipt) = deployer.clone().send_with_receipt().await.unwrap(); - assert_eq!(receipt.contract_address.unwrap(), contract.address()); - - let get_value = contract.method::<_, String>("getValue", ()).unwrap(); - let last_sender = contract.method::<_, Address>("lastSender", ()).unwrap(); - - // the initial value must be the one set in the constructor - let value = get_value.clone().call().await.unwrap(); - assert_eq!(value, "initial value"); - - // need to declare the method first, and only then send it - // this is because it internally clones an Arc which would otherwise - // get immediately dropped - let contract_call = contract - .connect(client2.clone()) - .method::<_, H256>("setValue", "hi".to_owned()) - .unwrap(); - let calldata = contract_call.calldata().unwrap(); - let gas_estimate = contract_call.estimate_gas().await.unwrap(); - let contract_call = contract_call.legacy(); - let pending_tx = contract_call.send().await.unwrap(); - let tx = client.get_transaction(*pending_tx).await.unwrap().unwrap(); - let tx_receipt = pending_tx.await.unwrap().unwrap(); - assert_eq!(last_sender.clone().call().await.unwrap(), addr2); - assert_eq!(get_value.clone().call().await.unwrap(), "hi"); - assert_eq!(tx.input, calldata); - - // we can also call contract methods at other addresses with the `at` call - // (useful when interacting with multiple ERC20s for example) - let contract2_addr = deployer.send().await.unwrap().address(); - let contract2 = contract.at(contract2_addr); - let init_value: String = - contract2.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); - let init_address = - contract2.method::<_, Address>("lastSender", ()).unwrap().call().await.unwrap(); - assert_eq!(init_address, Address::zero()); - assert_eq!(init_value, "initial value"); - - // methods with multiple args also work - let _tx_hash = contract - .method::<_, H256>("setValues", ("hi".to_owned(), "bye".to_owned())) - .unwrap() - .legacy() - .send() - .await - .unwrap() - .await - .unwrap(); - } - - #[tokio::test] - #[cfg(feature = "abigen")] - async fn get_past_events() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - let anvil = Anvil::new().spawn(); - let client = connect(&anvil, 0); - let address = client.get_accounts().await.unwrap()[0]; - let contract = deploy(client.clone(), abi, bytecode).await; - - // make a call with `client` - let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().legacy(); - let tx = func.send().await.unwrap(); - let _receipt = tx.await.unwrap(); - - // and we can fetch the events - let logs: Vec = contract - .event() - .from_block(0u64) - .topic1(address) // Corresponds to the first indexed parameter - .query() - .await - .unwrap(); - assert_eq!(logs[0].new_value, "initial value"); - assert_eq!(logs[1].new_value, "hi"); - assert_eq!(logs.len(), 2); - - // and we can fetch the events at a block hash - let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap(); - let logs: Vec = contract - .event() - .at_block_hash(hash) - .topic1(address) // Corresponds to the first indexed parameter - .query() - .await - .unwrap(); - assert_eq!(logs[0].new_value, "initial value"); - assert_eq!(logs.len(), 1); - } - - #[tokio::test] - #[cfg(feature = "abigen")] - async fn get_events_with_meta() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - let anvil = Anvil::new().spawn(); - let client = connect(&anvil, 0); - let address = anvil.addresses()[0]; - let contract = deploy(client.clone(), abi, bytecode).await; - - // and we can fetch the events - let logs: Vec<(ValueChanged, LogMeta)> = contract - .event() - .from_block(0u64) - .topic1(address) // Corresponds to the first indexed parameter - .query_with_meta() - .await - .unwrap(); - - assert_eq!(logs.len(), 1); - let (log, meta) = &logs[0]; - assert_eq!(log.new_value, "initial value"); - - assert_eq!(meta.address, contract.address()); - assert_eq!(meta.log_index, 0.into()); - assert_eq!(meta.block_number, 1.into()); - let block = client.get_block(1).await.unwrap().unwrap(); - assert_eq!(meta.block_hash, block.hash.unwrap()); - assert_eq!(block.transactions.len(), 1); - let tx = block.transactions[0]; - assert_eq!(meta.transaction_hash, tx); - assert_eq!(meta.transaction_index, 0.into()); - } - - #[tokio::test] - async fn call_past_state() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - let anvil = Anvil::new().spawn(); - let client = connect(&anvil, 0); - let contract = deploy(client.clone(), abi, bytecode).await; - let deployed_block = client.get_block_number().await.unwrap(); - - // assert initial state - let value = - contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap(); - assert_eq!(value, "initial value"); - - // make a call with `client` - let _tx_hash = *contract - .method::<_, H256>("setValue", "hi".to_owned()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); - - // assert new value - let value = - contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap(); - assert_eq!(value, "hi"); - - // assert previous value - let value = contract - .method::<_, String>("getValue", ()) - .unwrap() - .legacy() - .block(BlockId::Number(deployed_block.into())) - .call() - .await - .unwrap(); - assert_eq!(value, "initial value"); - - // Here would be the place to test EIP-1898, specifying the `BlockId` of `call` as the - // first block hash. However, Ganache does not implement this :/ - - // let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap(); - // let value = contract - // .method::<_, String>("getValue", ()) - // .unwrap() - // .block(BlockId::Hash(hash)) - // .call() - // .await - // .unwrap(); - // assert_eq!(value, "initial value"); - } - - #[tokio::test] - #[ignore] - async fn call_past_hash_test() { - // geth --dev --http --http.api eth,web3 - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - let provider = Provider::::try_from("http://localhost:8545").unwrap(); - let deployer = provider.get_accounts().await.unwrap()[0]; - - let client = Arc::new(provider.with_sender(deployer)); - let contract = deploy(client.clone(), abi, bytecode).await; - let deployed_block = client.get_block_number().await.unwrap(); - - // assert initial state - let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); - assert_eq!(value, "initial value"); - - // make a call with `client` - let _tx_hash = - *contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().send().await.unwrap(); - - // assert new value - let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); - assert_eq!(value, "hi"); - - // assert previous value using block hash - let hash = client.get_block(deployed_block).await.unwrap().unwrap().hash.unwrap(); - let value = contract - .method::<_, String>("getValue", ()) - .unwrap() - .block(BlockId::Hash(hash)) - .call() - .await - .unwrap(); - assert_eq!(value, "initial value"); - } - - #[tokio::test] - #[cfg(feature = "abigen")] - async fn watch_events() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - let anvil = Anvil::new().spawn(); - let client = connect(&anvil, 0); - let contract = deploy(client.clone(), abi.clone(), bytecode).await; - - // We spawn the event listener: - let event = contract.event::(); - let mut stream = event.stream().await.unwrap(); - - // Also set up a subscription for the same thing - let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap(); - let contract2 = ethers_contract::Contract::new(contract.address(), abi, ws.into()); - let event2 = contract2.event::(); - let mut subscription = event2.subscribe().await.unwrap(); - - let mut subscription_meta = event2.subscribe().await.unwrap().with_meta(); - - let num_calls = 3u64; - - // and we make a few calls - let num = client.get_block_number().await.unwrap(); - for i in 0..num_calls { - let call = contract.method::<_, H256>("setValue", i.to_string()).unwrap().legacy(); - let pending_tx = call.send().await.unwrap(); - let _receipt = pending_tx.await.unwrap(); - } - - for i in 0..num_calls { - // unwrap the option of the stream, then unwrap the decoding result - let log = stream.next().await.unwrap().unwrap(); - let log2 = subscription.next().await.unwrap().unwrap(); - let (log3, meta) = subscription_meta.next().await.unwrap().unwrap(); - assert_eq!(log.new_value, log3.new_value); - assert_eq!(log.new_value, log2.new_value); - assert_eq!(log.new_value, i.to_string()); - assert_eq!(meta.block_number, num + i + 1); - let hash = client.get_block(num + i + 1).await.unwrap().unwrap().hash.unwrap(); - assert_eq!(meta.block_hash, hash); - } - } - - #[tokio::test] - async fn watch_subscription_events_multiple_addresses() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - let anvil = Anvil::new().spawn(); - let client = connect(&anvil, 0); - let contract_1 = deploy(client.clone(), abi.clone(), bytecode.clone()).await; - let contract_2 = deploy(client.clone(), abi.clone(), bytecode).await; - - let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap(); - let filter = Filter::new() - .address(ValueOrArray::Array(vec![contract_1.address(), contract_2.address()])); - let mut stream = ws.subscribe_logs(&filter).await.unwrap(); - - // and we make a few calls - let call = contract_1.method::<_, H256>("setValue", "1".to_string()).unwrap().legacy(); - let pending_tx = call.send().await.unwrap(); - let _receipt = pending_tx.await.unwrap(); - - let call = contract_2.method::<_, H256>("setValue", "2".to_string()).unwrap().legacy(); - let pending_tx = call.send().await.unwrap(); - let _receipt = pending_tx.await.unwrap(); - - // unwrap the option of the stream, then unwrap the decoding result - let log_1 = stream.next().await.unwrap(); - let log_2 = stream.next().await.unwrap(); - assert_eq!(log_1.address, contract_1.address()); - assert_eq!(log_2.address, contract_2.address()); - } - - #[tokio::test] - async fn build_event_of_type() { - abigen!( - AggregatorInterface, - r#"[ - event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt) - ]"#, - ); - - let anvil = Anvil::new().spawn(); - let client = connect(&anvil, 0); - let event = ethers_contract::Contract::event_of_type::(client); - assert_eq!(event.filter, Filter::new().event(&AnswerUpdatedFilter::abi_signature())); - } - - #[tokio::test] - async fn signer_on_node() { - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - // spawn anvil - let anvil = Anvil::new().spawn(); - - // connect - let provider = Provider::::try_from(anvil.endpoint()) - .unwrap() - .interval(std::time::Duration::from_millis(50u64)); - - // get the first account - let deployer = provider.get_accounts().await.unwrap()[0]; - let client = Arc::new(provider.with_sender(deployer)); - - let contract = deploy(client, abi, bytecode).await; - - // make a call without the signer - let _receipt = contract - .method::<_, H256>("setValue", "hi".to_owned()) - .unwrap() - .legacy() - .send() - .await - .unwrap() - .await - .unwrap(); - let value: String = - contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); - assert_eq!(value, "hi"); - } - - #[tokio::test] - async fn multicall_aggregate() { - // get ABI and bytecode for the Multicall contract - let (multicall_abi, multicall_bytecode) = compile_contract("Multicall3", "Multicall.sol"); - - // get ABI and bytecode for the NotSoSimpleStorage contract - let (not_so_simple_abi, not_so_simple_bytecode) = - compile_contract("NotSoSimpleStorage", "NotSoSimpleStorage.sol"); - - // get ABI and bytecode for the SimpleStorage contract - let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); - - // launch anvil - let anvil = Anvil::new().spawn(); - - // Instantiate the clients. We assume that clients consume the provider and the wallet - // (which makes sense), so for multi-client tests, you must clone the provider. - // `client` is used to deploy the Multicall contract - // `client2` is used to deploy the first SimpleStorage contract - // `client3` is used to deploy the second SimpleStorage contract - // `client4` is used to make the aggregate call - let addrs = anvil.addresses().to_vec(); - let addr2 = addrs[1]; - let addr3 = addrs[2]; - let client = connect(&anvil, 0); - let client2 = connect(&anvil, 1); - let client3 = connect(&anvil, 2); - let client4 = connect(&anvil, 3); - - // create a factory which will be used to deploy instances of the contract - let multicall_factory = - ContractFactory::new(multicall_abi, multicall_bytecode, client.clone()); - let simple_factory = ContractFactory::new(abi.clone(), bytecode.clone(), client2.clone()); - let not_so_simple_factory = - ContractFactory::new(not_so_simple_abi, not_so_simple_bytecode, client3.clone()); - - let multicall_contract = - multicall_factory.deploy(()).unwrap().legacy().send().await.unwrap(); - let addr = multicall_contract.address(); - - let simple_contract = simple_factory - .deploy("the first one".to_string()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); - let not_so_simple_contract = not_so_simple_factory - .deploy("the second one".to_string()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); - - // Client2 and Client3 broadcast txs to set the values for both contracts - simple_contract - .connect(client2.clone()) - .method::<_, H256>("setValue", "reset first".to_owned()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); - - not_so_simple_contract - .connect(client3.clone()) - .method::<_, H256>("setValue", "reset second".to_owned()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); - - // get the calls for `value` and `last_sender` for both SimpleStorage contracts - let value = simple_contract.method::<_, String>("getValue", ()).unwrap(); - let value2 = - not_so_simple_contract.method::<_, (String, Address)>("getValues", ()).unwrap(); - let last_sender = simple_contract.method::<_, Address>("lastSender", ()).unwrap(); - let last_sender2 = not_so_simple_contract.method::<_, Address>("lastSender", ()).unwrap(); - - // initiate the Multicall instance and add calls one by one in builder style - let mut multicall = Multicall::new(client4.clone(), Some(addr)).await.unwrap(); - - // Set version to 1 - multicall = multicall.version(MulticallVersion::Multicall); - - multicall - .add_call(value, false) - .add_call(value2, false) - .add_call(last_sender, false) - .add_call(last_sender2, false); - - let return_data: (String, (String, Address), Address, Address) = - multicall.call().await.unwrap(); - - assert_eq!(return_data.0, "reset first"); - assert_eq!((return_data.1).0, "reset second"); - assert_eq!((return_data.1).1, addr3); - assert_eq!(return_data.2, addr2); - assert_eq!(return_data.3, addr3); - - // construct broadcast transactions that will be batched and broadcast via Multicall - let broadcast = simple_contract - .connect(client4.clone()) - .method::<_, H256>("setValue", "first reset again".to_owned()) - .unwrap(); - let broadcast2 = not_so_simple_contract - .connect(client4.clone()) - .method::<_, H256>("setValue", "second reset again".to_owned()) - .unwrap(); - - // use the already initialised Multicall instance, clearing the previous calls and adding - // new calls. Previously we used the `.call()` functionality to do a batch of calls in one - // go. Now we will use the `.send()` functionality to broadcast a batch of transactions - // in one go - let mut multicall_send = multicall.clone(); - multicall_send.clear_calls().add_call(broadcast, false).add_call(broadcast2, false); - - // broadcast the transaction and wait for it to be mined - let _tx_receipt = multicall_send.legacy().send().await.unwrap().await.unwrap(); - - // Do another multicall to check the updated return values - // The `getValue` calls should return the last value we set in the batched broadcast - // The `lastSender` calls should return the address of the Multicall contract, as it is - // the one acting as proxy and calling our SimpleStorage contracts (msg.sender) - let return_data: (String, (String, Address), Address, Address) = - multicall.call().await.unwrap(); - - assert_eq!(return_data.0, "first reset again"); - assert_eq!((return_data.1).0, "second reset again"); - assert_eq!((return_data.1).1, multicall_contract.address()); - assert_eq!(return_data.2, multicall_contract.address()); - assert_eq!(return_data.3, multicall_contract.address()); - - let addrs = anvil.addresses(); - // query ETH balances of multiple addresses - // these keys haven't been used to do any tx - // so should have 100 ETH - multicall - .clear_calls() - .add_get_eth_balance(addrs[4], false) - .add_get_eth_balance(addrs[5], false) - .add_get_eth_balance(addrs[6], false); - - let valid_balances = [ - U256::from(10_000_000_000_000_000_000_000u128), - U256::from(10_000_000_000_000_000_000_000u128), - U256::from(10_000_000_000_000_000_000_000u128), - ]; - - let balances: (U256, U256, U256) = multicall.call().await.unwrap(); - assert_eq!(balances.0, valid_balances[0]); - assert_eq!(balances.1, valid_balances[1]); - assert_eq!(balances.2, valid_balances[2]); - - // call_array - multicall - .clear_calls() - .add_get_eth_balance(addrs[4], false) - .add_get_eth_balance(addrs[5], false) - .add_get_eth_balance(addrs[6], false); - - let balances: Vec = multicall.call_array().await.unwrap(); - assert_eq!(balances, Vec::from_iter(valid_balances.iter().copied())); - - // clear multicall so we can test `call_raw` w/ >16 calls - multicall.clear_calls(); - - // clear the current value - simple_contract - .connect(client2.clone()) - .method::<_, H256>("setValue", "many".to_owned()) - .unwrap() - .legacy() - .send() - .await - .unwrap(); - - multicall.add_calls( - false, - std::iter::repeat(simple_contract.method::<_, String>("getValue", ()).unwrap()) - .take(17), - ); - - let tokens = multicall.call_raw().await.unwrap(); - let results: Vec = tokens - .into_iter() - .map(|result| { - // decode manually using Tokenizable method - String::from_token(result.unwrap()).unwrap() - }) - .collect(); - assert_eq!(results, ["many"; 17]); - - // test version 2 - multicall = multicall.version(MulticallVersion::Multicall2); - - // deploy contract with reverting methods - let reverting_contract = { - let (abi, bytecode) = - compile_contract("SimpleRevertingStorage", "SimpleRevertingStorage.sol"); - let f = ContractFactory::new(abi, bytecode, client.clone()); - f.deploy("This contract can revert".to_string()).unwrap().send().await.unwrap() - }; - - // reset value - reverting_contract - .connect(client2.clone()) - .method::<_, H256>("setValue", ("reset third".to_owned(), false)) - .unwrap() - .send() - .await - .unwrap(); - - // create calls - let set_value_call = reverting_contract - .connect(client.clone()) - .method::<_, H256>("setValue", ("this didn't revert".to_owned(), false)) - .unwrap(); - let set_value_reverting_call = reverting_contract - .connect(client3.clone()) - .method::<_, H256>("setValue", ("this reverted".to_owned(), true)) - .unwrap(); - let get_value_call = reverting_contract - .connect(client2.clone()) - .method::<_, String>("getValue", false) - .unwrap(); - let get_value_reverting_call = reverting_contract - .connect(client.clone()) - .method::<_, String>("getValue", true) - .unwrap(); - - // .send reverts - // don't allow revert - multicall - .clear_calls() - .add_call(set_value_reverting_call.clone(), false) - .add_call(set_value_call.clone(), false); - multicall.send().await.unwrap_err(); - - // value has not changed - assert_eq!(get_value_call.clone().call().await.unwrap(), "reset third"); - - // allow revert - multicall - .clear_calls() - .add_call(set_value_reverting_call.clone(), true) - .add_call(set_value_call.clone(), false); - multicall.send().await.unwrap(); - - // value has changed - assert_eq!(get_value_call.clone().call().await.unwrap(), "this didn't revert"); - - // reset value again - reverting_contract - .connect(client2.clone()) - .method::<_, H256>("setValue", ("reset third again".to_owned(), false)) - .unwrap() - .send() - .await - .unwrap(); - - // .call reverts - // don't allow revert - multicall - .clear_calls() - .add_call(get_value_reverting_call.clone(), false) - .add_call(get_value_call.clone(), false); - let res = multicall.call::<(String, String)>().await; - let err = res.unwrap_err(); - - assert!(err.is_revert()); - let message = err.decode_revert::().unwrap(); - assert!(message.contains("Multicall3: call failed")); - - // allow revert -> call doesn't revert, but returns Err(_) in raw tokens - let expected = Bytes::from_static(b"getValue revert").encode(); - multicall.clear_calls().add_call(get_value_reverting_call.clone(), true); - assert_eq!(multicall.call_raw().await.unwrap()[0].as_ref().unwrap_err()[4..], expected[..]); - assert_eq!( - multicall.call::<(String,)>().await.unwrap_err().as_revert().unwrap()[4..], - expected[..] - ); - - // v2 illegal revert - multicall - .clear_calls() - .add_call(get_value_reverting_call.clone(), false) // don't allow revert - .add_call(get_value_call.clone(), true); // true here will result in `tryAggregate(false, ...)` - assert!(matches!( - multicall.call::<(String, String)>().await.unwrap_err(), - MulticallError::IllegalRevert - )); - - // test version 3 - // aggregate3 is the same as try_aggregate except with allowing failure on a per-call basis. - // no need to test that - multicall = multicall.version(MulticallVersion::Multicall3); - - // .send with value - let amount = U256::from(100); - let value_tx = reverting_contract.method::<_, H256>("deposit", ()).unwrap().value(amount); - let rc_addr = reverting_contract.address(); - - let (bal_before,): (U256,) = - multicall.clear_calls().add_get_eth_balance(rc_addr, false).call().await.unwrap(); - - // send 2 value_tx - multicall.clear_calls().add_call(value_tx.clone(), false).add_call(value_tx.clone(), false); - multicall.send().await.unwrap(); - - let (bal_after,): (U256,) = - multicall.clear_calls().add_get_eth_balance(rc_addr, false).call().await.unwrap(); - - assert_eq!(bal_after, bal_before + U256::from(2) * amount); - - // test specific revert cases - // empty revert - let empty_revert = reverting_contract.method::<_, H256>("emptyRevert", ()).unwrap(); - multicall.clear_calls().add_call(empty_revert.clone(), true); - assert!(multicall.call::<(String,)>().await.unwrap_err().as_revert().unwrap().is_empty()); - - // string revert - let string_revert = - reverting_contract.method::<_, H256>("stringRevert", ("String".to_string())).unwrap(); - multicall.clear_calls().add_call(string_revert, true); - assert_eq!( - multicall.call::<(String,)>().await.unwrap_err().as_revert().unwrap()[4..], - Bytes::from_static(b"String").encode()[..] - ); - - // custom error revert - let custom_error = reverting_contract.method::<_, H256>("customError", ()).unwrap(); - multicall.clear_calls().add_call(custom_error, true); - assert_eq!( - multicall.call::<(Bytes,)>().await.unwrap_err().as_revert().unwrap()[..], - keccak256("CustomError()")[..4] - ); - - // custom error with data revert - let custom_error_with_data = reverting_contract - .method::<_, H256>("customErrorWithData", ("Data".to_string())) - .unwrap(); - multicall.clear_calls().add_call(custom_error_with_data, true); - let err = multicall.call::<(Bytes,)>().await.unwrap_err(); - let bytes = err.as_revert().unwrap(); - assert_eq!(bytes[..4], keccak256("CustomErrorWithData(string)")[..4]); - assert_eq!(bytes[4..], encode(&[Token::String("Data".to_string())])); - } - - #[tokio::test] - async fn test_derive_eip712() { - // Generate Contract ABI Bindings - abigen!( - DeriveEip712Test, - "./ethers-contract/tests/solidity-contracts/derive_eip712_abi.json", - event_derives(serde::Deserialize, serde::Serialize) - ); - - // Create derived structs - - #[derive(Debug, Clone, Eip712, EthAbiType)] - #[eip712( - name = "Eip712Test", - version = "1", - chain_id = 1, - verifying_contract = "0x0000000000000000000000000000000000000001", - salt = "eip712-test-75F0CCte" - )] - struct FooBar { - foo: I256, - bar: U256, - fizz: Bytes, - buzz: [u8; 32], - far: String, - out: Address, - } - - // get ABI and bytecode for the DeriveEip712Test contract - let (abi, bytecode) = compile_contract("DeriveEip712Test", "DeriveEip712Test.sol"); - - // 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 wallet: LocalWallet = anvil.keys()[0].clone().into(); - - let factory = ContractFactory::new(abi.clone(), bytecode.clone(), client.clone()); - - let contract = factory - .deploy(()) - .expect("failed to deploy DeriveEip712Test contract") - .legacy() - .send() - .await - .expect("failed to instantiate factory for DeriveEip712 contract"); - - let addr = contract.address(); - - let contract = DeriveEip712Test::new(addr, client.clone()); - - let foo_bar = FooBar { - foo: I256::from(10u64), - bar: U256::from(20u64), - fizz: b"fizz".into(), - buzz: keccak256("buzz"), - far: String::from("space"), - out: Address::from([0; 20]), - }; - - let derived_foo_bar = derive_eip_712_test::FooBar { - foo: foo_bar.foo, - bar: foo_bar.bar, - fizz: foo_bar.fizz.clone(), - buzz: foo_bar.buzz, - far: foo_bar.far.clone(), - out: foo_bar.out, - }; - - let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data"); - - let r = <[u8; 32]>::try_from(sig.r) - .expect("failed to parse 'r' value from signature into [u8; 32]"); - let s = <[u8; 32]>::try_from(sig.s) - .expect("failed to parse 's' value from signature into [u8; 32]"); - let v = u8::try_from(sig.v).expect("failed to parse 'v' value from signature into u8"); - - let domain_separator = contract - .domain_separator() - .call() - .await - .expect("failed to retrieve domain_separator from contract"); - let type_hash = - contract.type_hash().call().await.expect("failed to retrieve type_hash from contract"); - let struct_hash = contract - .struct_hash(derived_foo_bar.clone()) - .call() - .await - .expect("failed to retrieve struct_hash from contract"); - let encoded = contract - .encode_eip_712(derived_foo_bar.clone()) - .call() - .await - .expect("failed to retrieve eip712 encoded hash from contract"); - let verify = contract - .verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v) - .call() - .await - .expect("failed to verify signed typed data eip712 payload"); - - assert_eq!( - domain_separator, - foo_bar - .domain() - .expect("failed to return domain_separator from Eip712 implemented struct") - .separator(), - "domain separator does not match contract domain separator!" - ); - - assert_eq!( - type_hash, - FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"), - "type hash does not match contract struct type hash!" - ); - - assert_eq!( - struct_hash, - foo_bar - .clone() - .struct_hash() - .expect("failed to return struct_hash from Eip712 implemented struct"), - "struct hash does not match contract struct hash!" - ); - - assert_eq!( - encoded, - foo_bar - .encode_eip712() - .expect("failed to return domain_separator from Eip712 implemented struct"), - "Encoded value does not match!" - ); - - assert!(verify, "typed data signature failed!"); + fn as_inner(&self) -> Option<&Self::Inner> { + Some(&self.0) } } + +impl std::fmt::Display for MwErr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Ok(()) + } +} +impl std::error::Error for MwErr {} + +impl Middleware for NonClone { + type Error = MwErr; + + type Provider = M::Provider; + + type Inner = M; + + fn inner(&self) -> &Self::Inner { + &self.m + } +} + +// this is not a test. It is a compile check. :) +// It exists to ensure that trait bounds on contract internal behave as +// expected. It should not be run +fn it_compiles() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + + // launch anvil + let anvil = Anvil::new().spawn(); + + let client = Provider::::try_from(anvil.endpoint()) + .unwrap() + .interval(Duration::from_millis(10u64)); + + // Works (B == M, M: Clone) + let c: ContractInstance<&Provider, Provider> = + ContractInstance::new(H160::default(), abi.clone(), &client); + + let _ = c.method::<(), ()>("notARealMethod", ()); + + // Works (B == &M, M: Clone) + let c: ContractInstance, Provider> = + ContractInstance::new(H160::default(), abi.clone(), client.clone()); + + let _ = c.method::<(), ()>("notARealMethod", ()); + + let non_clone_mware = NonClone { m: client }; + + // Works (B == &M, M: !Clone) + let c: ContractInstance<&NonClone>, NonClone>> = + ContractInstance::new(H160::default(), abi, &non_clone_mware); + + let _ = c.method::<(), ()>("notARealMethod", ()); + + // // Fails (B == M, M: !Clone) + // let c: ContractInternal>, NonClone>> = + // ContractInternal::new(H160::default(), abi, non_clone_mware); + + // let _ = c.method::<(), ()>("notARealMethod", ()); +} + +#[tokio::test] +async fn deploy_and_call_contract() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + + // launch anvil + let anvil = Anvil::new().spawn(); + + // Instantiate the clients. We assume that clients consume the provider and the wallet + // (which makes sense), so for multi-client tests, you must clone the provider. + let addrs = anvil.addresses().to_vec(); + let addr2 = addrs[1]; + let client = connect(&anvil, 0); + let client2 = connect(&anvil, 1); + + // create a factory which will be used to deploy instances of the contract + let factory = ContractFactory::new(abi, bytecode, client.clone()); + + // `send` consumes the deployer so it must be cloned for later re-use + // (practically it's not expected that you'll need to deploy multiple instances of + // the _same_ deployer, so it's fine to clone here from a dev UX vs perf tradeoff) + let deployer = factory.deploy("initial value".to_string()).unwrap().legacy(); + // dry runs the deployment of the contract. takes the deployer by reference, no need to + // clone. + deployer.call().await.unwrap(); + let (contract, receipt) = deployer.clone().send_with_receipt().await.unwrap(); + assert_eq!(receipt.contract_address.unwrap(), contract.address()); + + let get_value = contract.method::<_, String>("getValue", ()).unwrap(); + let last_sender = contract.method::<_, Address>("lastSender", ()).unwrap(); + + // the initial value must be the one set in the constructor + let value = get_value.clone().call().await.unwrap(); + assert_eq!(value, "initial value"); + + // need to declare the method first, and only then send it + // this is because it internally clones an Arc which would otherwise + // get immediately dropped + let contract_call = + contract.connect(client2.clone()).method::<_, H256>("setValue", "hi".to_owned()).unwrap(); + let calldata = contract_call.calldata().unwrap(); + let gas_estimate = contract_call.estimate_gas().await.unwrap(); + let contract_call = contract_call.legacy(); + let pending_tx = contract_call.send().await.unwrap(); + let tx = client.get_transaction(*pending_tx).await.unwrap().unwrap(); + let tx_receipt = pending_tx.await.unwrap().unwrap(); + assert_eq!(last_sender.clone().call().await.unwrap(), addr2); + assert_eq!(get_value.clone().call().await.unwrap(), "hi"); + assert_eq!(tx.input, calldata); + + // we can also call contract methods at other addresses with the `at` call + // (useful when interacting with multiple ERC20s for example) + let contract2_addr = deployer.send().await.unwrap().address(); + let contract2 = contract.at(contract2_addr); + let init_value: String = + contract2.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); + let init_address = + contract2.method::<_, Address>("lastSender", ()).unwrap().call().await.unwrap(); + assert_eq!(init_address, Address::zero()); + assert_eq!(init_value, "initial value"); + + // methods with multiple args also work + let _tx_hash = contract + .method::<_, H256>("setValues", ("hi".to_owned(), "bye".to_owned())) + .unwrap() + .legacy() + .send() + .await + .unwrap() + .await + .unwrap(); +} + +#[tokio::test] +#[cfg(feature = "abigen")] +async fn get_past_events() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let anvil = Anvil::new().spawn(); + let client = connect(&anvil, 0); + let address = client.get_accounts().await.unwrap()[0]; + let contract = deploy(client.clone(), abi, bytecode).await; + + // make a call with `client` + let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().legacy(); + let tx = func.send().await.unwrap(); + let _receipt = tx.await.unwrap(); + + // and we can fetch the events + let logs: Vec = contract + .event() + .from_block(0u64) + .topic1(address) // Corresponds to the first indexed parameter + .query() + .await + .unwrap(); + assert_eq!(logs[0].new_value, "initial value"); + assert_eq!(logs[1].new_value, "hi"); + assert_eq!(logs.len(), 2); + + // and we can fetch the events at a block hash + let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap(); + let logs: Vec = contract + .event() + .at_block_hash(hash) + .topic1(address) // Corresponds to the first indexed parameter + .query() + .await + .unwrap(); + assert_eq!(logs[0].new_value, "initial value"); + assert_eq!(logs.len(), 1); +} + +#[tokio::test] +#[cfg(feature = "abigen")] +async fn get_events_with_meta() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let anvil = Anvil::new().spawn(); + let client = connect(&anvil, 0); + let address = anvil.addresses()[0]; + let contract = deploy(client.clone(), abi, bytecode).await; + + // and we can fetch the events + let logs: Vec<(ValueChanged, LogMeta)> = contract + .event() + .from_block(0u64) + .topic1(address) // Corresponds to the first indexed parameter + .query_with_meta() + .await + .unwrap(); + + assert_eq!(logs.len(), 1); + let (log, meta) = &logs[0]; + assert_eq!(log.new_value, "initial value"); + + assert_eq!(meta.address, contract.address()); + assert_eq!(meta.log_index, 0.into()); + assert_eq!(meta.block_number, 1.into()); + let block = client.get_block(1).await.unwrap().unwrap(); + assert_eq!(meta.block_hash, block.hash.unwrap()); + assert_eq!(block.transactions.len(), 1); + let tx = block.transactions[0]; + assert_eq!(meta.transaction_hash, tx); + assert_eq!(meta.transaction_index, 0.into()); +} + +#[tokio::test] +async fn call_past_state() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let anvil = Anvil::new().spawn(); + let client = connect(&anvil, 0); + let contract = deploy(client.clone(), abi, bytecode).await; + let deployed_block = client.get_block_number().await.unwrap(); + + // assert initial state + let value = + contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap(); + assert_eq!(value, "initial value"); + + // make a call with `client` + let _tx_hash = *contract + .method::<_, H256>("setValue", "hi".to_owned()) + .unwrap() + .legacy() + .send() + .await + .unwrap(); + + // assert new value + let value = + contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap(); + assert_eq!(value, "hi"); + + // assert previous value + let value = contract + .method::<_, String>("getValue", ()) + .unwrap() + .legacy() + .block(BlockId::Number(deployed_block.into())) + .call() + .await + .unwrap(); + assert_eq!(value, "initial value"); + + // Here would be the place to test EIP-1898, specifying the `BlockId` of `call` as the + // first block hash. However, Ganache does not implement this :/ + + // let hash = client.get_block(1).await.unwrap().unwrap().hash.unwrap(); + // let value = contract + // .method::<_, String>("getValue", ()) + // .unwrap() + // .block(BlockId::Hash(hash)) + // .call() + // .await + // .unwrap(); + // assert_eq!(value, "initial value"); +} + +#[tokio::test] +#[ignore] +async fn call_past_hash_test() { + // geth --dev --http --http.api eth,web3 + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let provider = Provider::::try_from("http://localhost:8545").unwrap(); + let deployer = provider.get_accounts().await.unwrap()[0]; + + let client = Arc::new(provider.with_sender(deployer)); + let contract = deploy(client.clone(), abi, bytecode).await; + let deployed_block = client.get_block_number().await.unwrap(); + + // assert initial state + let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); + assert_eq!(value, "initial value"); + + // make a call with `client` + let _tx_hash = + *contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().send().await.unwrap(); + + // assert new value + let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); + assert_eq!(value, "hi"); + + // assert previous value using block hash + let hash = client.get_block(deployed_block).await.unwrap().unwrap().hash.unwrap(); + let value = contract + .method::<_, String>("getValue", ()) + .unwrap() + .block(BlockId::Hash(hash)) + .call() + .await + .unwrap(); + assert_eq!(value, "initial value"); +} + +#[tokio::test] +#[cfg(feature = "abigen")] +async fn watch_events() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let anvil = Anvil::new().spawn(); + let client = connect(&anvil, 0); + let contract = deploy(client.clone(), abi.clone(), bytecode).await; + + // We spawn the event listener: + let event = contract.event::(); + let mut stream = event.stream().await.unwrap(); + + // Also set up a subscription for the same thing + let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap(); + let contract2 = ethers_contract::Contract::new(contract.address(), abi, ws.into()); + let event2 = contract2.event::(); + let mut subscription = event2.subscribe().await.unwrap(); + + let mut subscription_meta = event2.subscribe().await.unwrap().with_meta(); + + let num_calls = 3u64; + + // and we make a few calls + let num = client.get_block_number().await.unwrap(); + for i in 0..num_calls { + let call = contract.method::<_, H256>("setValue", i.to_string()).unwrap().legacy(); + let pending_tx = call.send().await.unwrap(); + let _receipt = pending_tx.await.unwrap(); + } + + for i in 0..num_calls { + // unwrap the option of the stream, then unwrap the decoding result + let log = stream.next().await.unwrap().unwrap(); + let log2 = subscription.next().await.unwrap().unwrap(); + let (log3, meta) = subscription_meta.next().await.unwrap().unwrap(); + assert_eq!(log.new_value, log3.new_value); + assert_eq!(log.new_value, log2.new_value); + assert_eq!(log.new_value, i.to_string()); + assert_eq!(meta.block_number, num + i + 1); + let hash = client.get_block(num + i + 1).await.unwrap().unwrap().hash.unwrap(); + assert_eq!(meta.block_hash, hash); + } +} + +#[tokio::test] +async fn watch_subscription_events_multiple_addresses() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + let anvil = Anvil::new().spawn(); + let client = connect(&anvil, 0); + let contract_1 = deploy(client.clone(), abi.clone(), bytecode.clone()).await; + let contract_2 = deploy(client.clone(), abi.clone(), bytecode).await; + + let ws = Provider::connect(anvil.ws_endpoint()).await.unwrap(); + let filter = Filter::new() + .address(ValueOrArray::Array(vec![contract_1.address(), contract_2.address()])); + let mut stream = ws.subscribe_logs(&filter).await.unwrap(); + + // and we make a few calls + let call = contract_1.method::<_, H256>("setValue", "1".to_string()).unwrap().legacy(); + let pending_tx = call.send().await.unwrap(); + let _receipt = pending_tx.await.unwrap(); + + let call = contract_2.method::<_, H256>("setValue", "2".to_string()).unwrap().legacy(); + let pending_tx = call.send().await.unwrap(); + let _receipt = pending_tx.await.unwrap(); + + // unwrap the option of the stream, then unwrap the decoding result + let log_1 = stream.next().await.unwrap(); + let log_2 = stream.next().await.unwrap(); + assert_eq!(log_1.address, contract_1.address()); + assert_eq!(log_2.address, contract_2.address()); +} + +#[tokio::test] +async fn build_event_of_type() { + abigen!( + AggregatorInterface, + r#"[ + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt) + ]"#, + ); + + let anvil = Anvil::new().spawn(); + let client = connect(&anvil, 0); + let event = ethers_contract::Contract::event_of_type::(client); + assert_eq!(event.filter, Filter::new().event(&AnswerUpdatedFilter::abi_signature())); +} + +#[tokio::test] +async fn signer_on_node() { + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + // spawn anvil + let anvil = Anvil::new().spawn(); + + // connect + let provider = Provider::::try_from(anvil.endpoint()) + .unwrap() + .interval(std::time::Duration::from_millis(50u64)); + + // get the first account + let deployer = provider.get_accounts().await.unwrap()[0]; + let client = Arc::new(provider.with_sender(deployer)); + + let contract = deploy(client, abi, bytecode).await; + + // make a call without the signer + let _receipt = contract + .method::<_, H256>("setValue", "hi".to_owned()) + .unwrap() + .legacy() + .send() + .await + .unwrap() + .await + .unwrap(); + let value: String = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap(); + assert_eq!(value, "hi"); +} + +#[tokio::test] +async fn multicall_aggregate() { + // get ABI and bytecode for the Multicall contract + let (multicall_abi, multicall_bytecode) = compile_contract("Multicall3", "Multicall.sol"); + + // get ABI and bytecode for the NotSoSimpleStorage contract + let (not_so_simple_abi, not_so_simple_bytecode) = + compile_contract("NotSoSimpleStorage", "NotSoSimpleStorage.sol"); + + // get ABI and bytecode for the SimpleStorage contract + let (abi, bytecode) = compile_contract("SimpleStorage", "SimpleStorage.sol"); + + // launch anvil + let anvil = Anvil::new().spawn(); + + // Instantiate the clients. We assume that clients consume the provider and the wallet + // (which makes sense), so for multi-client tests, you must clone the provider. + // `client` is used to deploy the Multicall contract + // `client2` is used to deploy the first SimpleStorage contract + // `client3` is used to deploy the second SimpleStorage contract + // `client4` is used to make the aggregate call + let addrs = anvil.addresses().to_vec(); + let addr2 = addrs[1]; + let addr3 = addrs[2]; + let client = connect(&anvil, 0); + let client2 = connect(&anvil, 1); + let client3 = connect(&anvil, 2); + let client4 = connect(&anvil, 3); + + // create a factory which will be used to deploy instances of the contract + let multicall_factory = ContractFactory::new(multicall_abi, multicall_bytecode, client.clone()); + let simple_factory = ContractFactory::new(abi.clone(), bytecode.clone(), client2.clone()); + let not_so_simple_factory = + ContractFactory::new(not_so_simple_abi, not_so_simple_bytecode, client3.clone()); + + let multicall_contract = multicall_factory.deploy(()).unwrap().legacy().send().await.unwrap(); + let addr = multicall_contract.address(); + + let simple_contract = + simple_factory.deploy("the first one".to_string()).unwrap().legacy().send().await.unwrap(); + let not_so_simple_contract = not_so_simple_factory + .deploy("the second one".to_string()) + .unwrap() + .legacy() + .send() + .await + .unwrap(); + + // Client2 and Client3 broadcast txs to set the values for both contracts + simple_contract + .connect(client2.clone()) + .method::<_, H256>("setValue", "reset first".to_owned()) + .unwrap() + .legacy() + .send() + .await + .unwrap(); + + not_so_simple_contract + .connect(client3.clone()) + .method::<_, H256>("setValue", "reset second".to_owned()) + .unwrap() + .legacy() + .send() + .await + .unwrap(); + + // get the calls for `value` and `last_sender` for both SimpleStorage contracts + let value = simple_contract.method::<_, String>("getValue", ()).unwrap(); + let value2 = not_so_simple_contract.method::<_, (String, Address)>("getValues", ()).unwrap(); + let last_sender = simple_contract.method::<_, Address>("lastSender", ()).unwrap(); + let last_sender2 = not_so_simple_contract.method::<_, Address>("lastSender", ()).unwrap(); + + // initiate the Multicall instance and add calls one by one in builder style + let mut multicall = Multicall::new(client4.clone(), Some(addr)).await.unwrap(); + + // Set version to 1 + multicall = multicall.version(MulticallVersion::Multicall); + + multicall + .add_call(value, false) + .add_call(value2, false) + .add_call(last_sender, false) + .add_call(last_sender2, false); + + let return_data: (String, (String, Address), Address, Address) = + multicall.call().await.unwrap(); + + assert_eq!(return_data.0, "reset first"); + assert_eq!((return_data.1).0, "reset second"); + assert_eq!((return_data.1).1, addr3); + assert_eq!(return_data.2, addr2); + assert_eq!(return_data.3, addr3); + + // construct broadcast transactions that will be batched and broadcast via Multicall + let broadcast = simple_contract + .connect(client4.clone()) + .method::<_, H256>("setValue", "first reset again".to_owned()) + .unwrap(); + let broadcast2 = not_so_simple_contract + .connect(client4.clone()) + .method::<_, H256>("setValue", "second reset again".to_owned()) + .unwrap(); + + // use the already initialised Multicall instance, clearing the previous calls and adding + // new calls. Previously we used the `.call()` functionality to do a batch of calls in one + // go. Now we will use the `.send()` functionality to broadcast a batch of transactions + // in one go + let mut multicall_send = multicall.clone(); + multicall_send.clear_calls().add_call(broadcast, false).add_call(broadcast2, false); + + // broadcast the transaction and wait for it to be mined + let _tx_receipt = multicall_send.legacy().send().await.unwrap().await.unwrap(); + + // Do another multicall to check the updated return values + // The `getValue` calls should return the last value we set in the batched broadcast + // The `lastSender` calls should return the address of the Multicall contract, as it is + // the one acting as proxy and calling our SimpleStorage contracts (msg.sender) + let return_data: (String, (String, Address), Address, Address) = + multicall.call().await.unwrap(); + + assert_eq!(return_data.0, "first reset again"); + assert_eq!((return_data.1).0, "second reset again"); + assert_eq!((return_data.1).1, multicall_contract.address()); + assert_eq!(return_data.2, multicall_contract.address()); + assert_eq!(return_data.3, multicall_contract.address()); + + let addrs = anvil.addresses(); + // query ETH balances of multiple addresses + // these keys haven't been used to do any tx + // so should have 100 ETH + multicall + .clear_calls() + .add_get_eth_balance(addrs[4], false) + .add_get_eth_balance(addrs[5], false) + .add_get_eth_balance(addrs[6], false); + + let valid_balances = [ + U256::from(10_000_000_000_000_000_000_000u128), + U256::from(10_000_000_000_000_000_000_000u128), + U256::from(10_000_000_000_000_000_000_000u128), + ]; + + let balances: (U256, U256, U256) = multicall.call().await.unwrap(); + assert_eq!(balances.0, valid_balances[0]); + assert_eq!(balances.1, valid_balances[1]); + assert_eq!(balances.2, valid_balances[2]); + + // call_array + multicall + .clear_calls() + .add_get_eth_balance(addrs[4], false) + .add_get_eth_balance(addrs[5], false) + .add_get_eth_balance(addrs[6], false); + + let balances: Vec = multicall.call_array().await.unwrap(); + assert_eq!(balances, Vec::from_iter(valid_balances.iter().copied())); + + // clear multicall so we can test `call_raw` w/ >16 calls + multicall.clear_calls(); + + // clear the current value + simple_contract + .connect(client2.clone()) + .method::<_, H256>("setValue", "many".to_owned()) + .unwrap() + .legacy() + .send() + .await + .unwrap(); + + multicall.add_calls( + false, + std::iter::repeat(simple_contract.method::<_, String>("getValue", ()).unwrap()).take(17), + ); + + let tokens = multicall.call_raw().await.unwrap(); + let results: Vec = tokens + .into_iter() + .map(|result| { + // decode manually using Tokenizable method + String::from_token(result.unwrap()).unwrap() + }) + .collect(); + assert_eq!(results, ["many"; 17]); + + // test version 2 + multicall = multicall.version(MulticallVersion::Multicall2); + + // deploy contract with reverting methods + let reverting_contract = { + let (abi, bytecode) = + compile_contract("SimpleRevertingStorage", "SimpleRevertingStorage.sol"); + let f = ContractFactory::new(abi, bytecode, client.clone()); + f.deploy("This contract can revert".to_string()).unwrap().send().await.unwrap() + }; + + // reset value + reverting_contract + .connect(client2.clone()) + .method::<_, H256>("setValue", ("reset third".to_owned(), false)) + .unwrap() + .send() + .await + .unwrap(); + + // create calls + let set_value_call = reverting_contract + .connect(client.clone()) + .method::<_, H256>("setValue", ("this didn't revert".to_owned(), false)) + .unwrap(); + let set_value_reverting_call = reverting_contract + .connect(client3.clone()) + .method::<_, H256>("setValue", ("this reverted".to_owned(), true)) + .unwrap(); + let get_value_call = + reverting_contract.connect(client2.clone()).method::<_, String>("getValue", false).unwrap(); + let get_value_reverting_call = + reverting_contract.connect(client.clone()).method::<_, String>("getValue", true).unwrap(); + + // .send reverts + // don't allow revert + multicall + .clear_calls() + .add_call(set_value_reverting_call.clone(), false) + .add_call(set_value_call.clone(), false); + multicall.send().await.unwrap_err(); + + // value has not changed + assert_eq!(get_value_call.clone().call().await.unwrap(), "reset third"); + + // allow revert + multicall + .clear_calls() + .add_call(set_value_reverting_call.clone(), true) + .add_call(set_value_call.clone(), false); + multicall.send().await.unwrap(); + + // value has changed + assert_eq!(get_value_call.clone().call().await.unwrap(), "this didn't revert"); + + // reset value again + reverting_contract + .connect(client2.clone()) + .method::<_, H256>("setValue", ("reset third again".to_owned(), false)) + .unwrap() + .send() + .await + .unwrap(); + + // .call reverts + // don't allow revert + multicall + .clear_calls() + .add_call(get_value_reverting_call.clone(), false) + .add_call(get_value_call.clone(), false); + let res = multicall.call::<(String, String)>().await; + let err = res.unwrap_err(); + + assert!(err.is_revert()); + let message = err.decode_revert::().unwrap(); + assert!(message.contains("Multicall3: call failed")); + + // allow revert -> call doesn't revert, but returns Err(_) in raw tokens + let expected = Bytes::from_static(b"getValue revert").encode(); + multicall.clear_calls().add_call(get_value_reverting_call.clone(), true); + assert_eq!(multicall.call_raw().await.unwrap()[0].as_ref().unwrap_err()[4..], expected[..]); + assert_eq!( + multicall.call::<(String,)>().await.unwrap_err().as_revert().unwrap()[4..], + expected[..] + ); + + // v2 illegal revert + multicall + .clear_calls() + .add_call(get_value_reverting_call.clone(), false) // don't allow revert + .add_call(get_value_call.clone(), true); // true here will result in `tryAggregate(false, ...)` + assert!(matches!( + multicall.call::<(String, String)>().await.unwrap_err(), + MulticallError::IllegalRevert + )); + + // test version 3 + // aggregate3 is the same as try_aggregate except with allowing failure on a per-call basis. + // no need to test that + multicall = multicall.version(MulticallVersion::Multicall3); + + // .send with value + let amount = U256::from(100); + let value_tx = reverting_contract.method::<_, H256>("deposit", ()).unwrap().value(amount); + let rc_addr = reverting_contract.address(); + + let (bal_before,): (U256,) = + multicall.clear_calls().add_get_eth_balance(rc_addr, false).call().await.unwrap(); + + // send 2 value_tx + multicall.clear_calls().add_call(value_tx.clone(), false).add_call(value_tx.clone(), false); + multicall.send().await.unwrap(); + + let (bal_after,): (U256,) = + multicall.clear_calls().add_get_eth_balance(rc_addr, false).call().await.unwrap(); + + assert_eq!(bal_after, bal_before + U256::from(2) * amount); + + // test specific revert cases + // empty revert + let empty_revert = reverting_contract.method::<_, H256>("emptyRevert", ()).unwrap(); + multicall.clear_calls().add_call(empty_revert.clone(), true); + assert!(multicall.call::<(String,)>().await.unwrap_err().as_revert().unwrap().is_empty()); + + // string revert + let string_revert = + reverting_contract.method::<_, H256>("stringRevert", ("String".to_string())).unwrap(); + multicall.clear_calls().add_call(string_revert, true); + assert_eq!( + multicall.call::<(String,)>().await.unwrap_err().as_revert().unwrap()[4..], + Bytes::from_static(b"String").encode()[..] + ); + + // custom error revert + let custom_error = reverting_contract.method::<_, H256>("customError", ()).unwrap(); + multicall.clear_calls().add_call(custom_error, true); + assert_eq!( + multicall.call::<(Bytes,)>().await.unwrap_err().as_revert().unwrap()[..], + keccak256("CustomError()")[..4] + ); + + // custom error with data revert + let custom_error_with_data = + reverting_contract.method::<_, H256>("customErrorWithData", ("Data".to_string())).unwrap(); + multicall.clear_calls().add_call(custom_error_with_data, true); + let err = multicall.call::<(Bytes,)>().await.unwrap_err(); + let bytes = err.as_revert().unwrap(); + assert_eq!(bytes[..4], keccak256("CustomErrorWithData(string)")[..4]); + assert_eq!(bytes[4..], encode(&[Token::String("Data".to_string())])); +} + +#[tokio::test] +async fn test_derive_eip712() { + // Generate Contract ABI Bindings + abigen!( + DeriveEip712Test, + "./ethers-contract/tests/solidity-contracts/derive_eip712_abi.json", + event_derives(serde::Deserialize, serde::Serialize) + ); + + // Create derived structs + + #[derive(Debug, Clone, Eip712, EthAbiType)] + #[eip712( + name = "Eip712Test", + version = "1", + chain_id = 1, + verifying_contract = "0x0000000000000000000000000000000000000001", + salt = "eip712-test-75F0CCte" + )] + struct FooBar { + foo: I256, + bar: U256, + fizz: Bytes, + buzz: [u8; 32], + far: String, + out: Address, + } + + // get ABI and bytecode for the DeriveEip712Test contract + let (abi, bytecode) = compile_contract("DeriveEip712Test", "DeriveEip712Test.sol"); + + // 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 wallet: LocalWallet = anvil.keys()[0].clone().into(); + + let factory = ContractFactory::new(abi.clone(), bytecode.clone(), client.clone()); + + let contract = factory + .deploy(()) + .expect("failed to deploy DeriveEip712Test contract") + .legacy() + .send() + .await + .expect("failed to instantiate factory for DeriveEip712 contract"); + + let addr = contract.address(); + + let contract = DeriveEip712Test::new(addr, client.clone()); + + let foo_bar = FooBar { + foo: I256::from(10u64), + bar: U256::from(20u64), + fizz: b"fizz".into(), + buzz: keccak256("buzz"), + far: String::from("space"), + out: Address::from([0; 20]), + }; + + let derived_foo_bar = derive_eip_712_test::FooBar { + foo: foo_bar.foo, + bar: foo_bar.bar, + fizz: foo_bar.fizz.clone(), + buzz: foo_bar.buzz, + far: foo_bar.far.clone(), + out: foo_bar.out, + }; + + let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data"); + + let r = <[u8; 32]>::try_from(sig.r) + .expect("failed to parse 'r' value from signature into [u8; 32]"); + let s = <[u8; 32]>::try_from(sig.s) + .expect("failed to parse 's' value from signature into [u8; 32]"); + let v = u8::try_from(sig.v).expect("failed to parse 'v' value from signature into u8"); + + let domain_separator = contract + .domain_separator() + .call() + .await + .expect("failed to retrieve domain_separator from contract"); + let type_hash = + contract.type_hash().call().await.expect("failed to retrieve type_hash from contract"); + let struct_hash = contract + .struct_hash(derived_foo_bar.clone()) + .call() + .await + .expect("failed to retrieve struct_hash from contract"); + let encoded = contract + .encode_eip_712(derived_foo_bar.clone()) + .call() + .await + .expect("failed to retrieve eip712 encoded hash from contract"); + let verify = contract + .verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v) + .call() + .await + .expect("failed to verify signed typed data eip712 payload"); + + assert_eq!( + domain_separator, + foo_bar + .domain() + .expect("failed to return domain_separator from Eip712 implemented struct") + .separator(), + "domain separator does not match contract domain separator!" + ); + + assert_eq!( + type_hash, + FooBar::type_hash().expect("failed to return type_hash from Eip712 implemented struct"), + "type hash does not match contract struct type hash!" + ); + + assert_eq!( + struct_hash, + foo_bar + .clone() + .struct_hash() + .expect("failed to return struct_hash from Eip712 implemented struct"), + "struct hash does not match contract struct hash!" + ); + + assert_eq!( + encoded, + foo_bar + .encode_eip712() + .expect("failed to return domain_separator from Eip712 implemented struct"), + "Encoded value does not match!" + ); + + assert!(verify, "typed data signature failed!"); +} diff --git a/ethers-contract/tests/it/common/derive.rs b/ethers-contract/tests/it/derive.rs similarity index 100% rename from ethers-contract/tests/it/common/derive.rs rename to ethers-contract/tests/it/derive.rs diff --git a/ethers-contract/tests/it/main.rs b/ethers-contract/tests/it/main.rs index bdf63ef0..135d053a 100644 --- a/ethers-contract/tests/it/main.rs +++ b/ethers-contract/tests/it/main.rs @@ -1,10 +1,14 @@ -// #![allow(clippy::extra_unused_type_parameters)] +#![allow(clippy::extra_unused_type_parameters)] +#![cfg(feature = "abigen")] -#[cfg(feature = "abigen")] mod abigen; -pub(crate) mod common; -#[cfg(feature = "abigen")] -mod contract; +mod derive; mod contract_call; + +#[cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] +mod common; + +#[cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] +mod contract; diff --git a/ethers-core/ethers-derive-eip712/tests/derive_eip712.rs b/ethers-core/ethers-derive-eip712/tests/derive_eip712.rs index 1f821e9f..7018f1dc 100644 --- a/ethers-core/ethers-derive-eip712/tests/derive_eip712.rs +++ b/ethers-core/ethers-derive-eip712/tests/derive_eip712.rs @@ -1,3 +1,5 @@ +#![allow(clippy::extra_unused_type_parameters)] + use ethers_contract_derive::EthAbiType; use ethers_core::{ types::{ diff --git a/ethers-middleware/tests/builder.rs b/ethers-middleware/tests/it/builder.rs similarity index 98% rename from ethers-middleware/tests/builder.rs rename to ethers-middleware/tests/it/builder.rs index d99c603e..a0c504e0 100644 --- a/ethers-middleware/tests/builder.rs +++ b/ethers-middleware/tests/it/builder.rs @@ -1,5 +1,3 @@ -#![cfg(not(target_arch = "wasm32"))] - use ethers_core::{rand::thread_rng, types::U64}; use ethers_middleware::{ builder::MiddlewareBuilder, diff --git a/ethers-middleware/tests/gas_escalator.rs b/ethers-middleware/tests/it/gas_escalator.rs similarity index 97% rename from ethers-middleware/tests/gas_escalator.rs rename to ethers-middleware/tests/it/gas_escalator.rs index 66d7d4ff..38ed7dda 100644 --- a/ethers-middleware/tests/gas_escalator.rs +++ b/ethers-middleware/tests/it/gas_escalator.rs @@ -1,5 +1,3 @@ -#![cfg(not(target_arch = "wasm32"))] - use ethers_core::types::*; use ethers_middleware::{ gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, diff --git a/ethers-middleware/tests/gas_oracle.rs b/ethers-middleware/tests/it/gas_oracle.rs similarity index 99% rename from ethers-middleware/tests/gas_oracle.rs rename to ethers-middleware/tests/it/gas_oracle.rs index 9464fb3d..5974c130 100644 --- a/ethers-middleware/tests/gas_oracle.rs +++ b/ethers-middleware/tests/it/gas_oracle.rs @@ -1,5 +1,3 @@ -#![cfg(not(target_arch = "wasm32"))] - use async_trait::async_trait; use ethers_core::{types::*, utils::Anvil}; use ethers_middleware::gas_oracle::{ diff --git a/ethers-middleware/tests/it/main.rs b/ethers-middleware/tests/it/main.rs new file mode 100644 index 00000000..66535dc7 --- /dev/null +++ b/ethers-middleware/tests/it/main.rs @@ -0,0 +1,19 @@ +#![allow(clippy::extra_unused_type_parameters)] +#![cfg(not(target_arch = "wasm32"))] + +mod builder; + +mod gas_escalator; + +mod gas_oracle; + +mod signer; + +#[cfg(not(feature = "celo"))] +mod nonce_manager; + +#[cfg(not(feature = "celo"))] +mod stack; + +#[cfg(not(feature = "celo"))] +mod transformer; diff --git a/ethers-middleware/tests/nonce_manager.rs b/ethers-middleware/tests/it/nonce_manager.rs similarity index 95% rename from ethers-middleware/tests/nonce_manager.rs rename to ethers-middleware/tests/it/nonce_manager.rs index 85368aa2..b4c2041d 100644 --- a/ethers-middleware/tests/nonce_manager.rs +++ b/ethers-middleware/tests/it/nonce_manager.rs @@ -1,5 +1,3 @@ -#![cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] - use ethers_core::{types::*, utils::Anvil}; use ethers_middleware::MiddlewareBuilder; use ethers_providers::{Http, Middleware, Provider}; diff --git a/ethers-middleware/tests/it/signer.rs b/ethers-middleware/tests/it/signer.rs new file mode 100644 index 00000000..92a3d41c --- /dev/null +++ b/ethers-middleware/tests/it/signer.rs @@ -0,0 +1,354 @@ +use ethers_contract::ContractFactory; +use ethers_core::{abi::Abi, types::*, utils::parse_ether}; +use ethers_middleware::signer::SignerMiddleware; +use ethers_providers::{Http, JsonRpcClient, Middleware, Provider}; +use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; +use ethers_solc::Solc; +use once_cell::sync::Lazy; +use std::{ + convert::TryFrom, + sync::{atomic::AtomicU8, Arc}, + time::Duration, +}; + +#[allow(dead_code)] +static WALLETS: Lazy = Lazy::new(|| { + TestWallets { + mnemonic: MnemonicBuilder::default() + // Please don't drain this :) + .phrase("impose air often almost medal sudden finish quote dwarf devote theme layer"), + next: Default::default(), + } +}); + +#[derive(Debug, Default)] +#[allow(dead_code)] +struct TestWallets { + mnemonic: MnemonicBuilder, + next: AtomicU8, +} + +#[allow(dead_code)] +impl TestWallets { + /// Helper for funding the wallets with an instantiated provider + #[allow(unused)] + pub async fn fund>(&self, provider: &Provider, n: U) { + let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::>(); + // hardcoded funder address private key, GOERLI + let signer = "39aa18eeb5d12c071e5f19d8e9375a872e90cb1f2fa640384ffd8800a2f3e8f1" + .parse::() + .unwrap() + .with_chain_id(provider.get_chainid().await.unwrap().as_u64()); + let provider = SignerMiddleware::new(provider, signer); + let addr = provider.address(); + + let mut nonce = provider.get_transaction_count(addr, None).await.unwrap(); + let mut pending_txs = Vec::new(); + for addr in addrs { + println!("Funding wallet {addr:?}"); + let tx = TransactionRequest::new() + .nonce(nonce) + .to(addr) + // 0.1 eth per wallet + .value(parse_ether("1").unwrap()); + pending_txs.push( + provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(), + ); + nonce += 1.into(); + } + + futures_util::future::join_all(pending_txs).await; + } + + pub fn next(&self) -> LocalWallet { + let idx = self.next.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + + // println!("Got wallet {:?}", wallet.address()); + self.get(idx) + } + + pub fn get>(&self, idx: T) -> LocalWallet { + self.mnemonic + .clone() + .index(idx) + .expect("index not found") + .build() + .expect("cannot build wallet") + } +} + +#[cfg(not(feature = "celo"))] +mod eth_tests { + use super::*; + use ethers_core::utils::Anvil; + use ethers_providers::GOERLI; + + #[tokio::test] + async fn send_eth() { + let anvil = Anvil::new().spawn(); + + // this private key belongs to the above mnemonic + let wallet: LocalWallet = anvil.keys()[0].clone().into(); + let wallet2: LocalWallet = anvil.keys()[1].clone().into(); + + // connect to the network + let provider = Provider::::try_from(anvil.endpoint()) + .unwrap() + .interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + let provider = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap(); + + // craft the transaction + let tx = TransactionRequest::new().to(wallet2.address()).value(10000).chain_id(chain_id); + + let balance_before = provider.get_balance(provider.address(), None).await.unwrap(); + + // send it! + provider.send_transaction(tx, None).await.unwrap(); + + let balance_after = provider.get_balance(provider.address(), None).await.unwrap(); + + assert!(balance_before > balance_after); + } + + // hardhat compatibility test, to show hardhat rejects tx signed for other chains + #[tokio::test] + #[ignore] + async fn send_with_chain_id_hardhat() { + let wallet: LocalWallet = + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap(); + let provider = Provider::try_from("http://localhost:8545").unwrap(); + let client = SignerMiddleware::new(provider, wallet); + + let tx = TransactionRequest::new().to(Address::random()).value(100u64); + let res = client.send_transaction(tx, None).await; + + let err = res.unwrap_err(); + assert!(err.to_string().contains( + "Trying to send an incompatible EIP-155 transaction, signed for another chain." + )); + } + + #[tokio::test] + #[ignore] + async fn send_with_chain_id_anvil() { + let wallet: LocalWallet = + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap(); + let provider = Provider::try_from("http://localhost:8545").unwrap(); + let client = SignerMiddleware::new(provider, wallet); + + let tx = TransactionRequest::new().to(Address::random()).value(100u64); + let res = client.send_transaction(tx, None).await; + + let _err = res.unwrap_err(); + } + + #[tokio::test] + async fn pending_txs_with_confirmations_testnet() { + let provider = GOERLI.provider().interval(Duration::from_millis(3000)); + let chain_id = provider.get_chainid().await.unwrap(); + let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); + let address = wallet.address(); + let provider = SignerMiddleware::new(provider, wallet); + generic_pending_txs_test(provider, address).await; + } + + // different keys to avoid nonce errors + #[tokio::test] + async fn websocket_pending_txs_with_confirmations_testnet() { + let provider = GOERLI.ws().await.interval(Duration::from_millis(3000)); + let chain_id = provider.get_chainid().await.unwrap(); + let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); + let address = wallet.address(); + let provider = SignerMiddleware::new(provider, wallet); + generic_pending_txs_test(provider, address).await; + } + + async fn generic_pending_txs_test(provider: M, who: Address) { + let tx = TransactionRequest::new().to(who).from(who); + let pending_tx = provider.send_transaction(tx, None).await.unwrap(); + let tx_hash = *pending_tx; + let receipt = pending_tx.confirmations(1).await.unwrap().unwrap(); + // got the correct receipt + assert_eq!(receipt.transaction_hash, tx_hash); + } + + #[tokio::test] + async fn typed_txs() { + let provider = GOERLI.provider(); + + let chain_id = provider.get_chainid().await.unwrap(); + let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); + let address = wallet.address(); + // our wallet + let provider = SignerMiddleware::new(provider, wallet); + + // Uncomment the below and run this test to re-fund the wallets if they get drained. + // Would be ideal if we'd have a way to do this automatically, but this should be + // happening rarely enough that it doesn't matter. + // WALLETS.fund(provider.provider(), 10u32).await; + + async fn check_tx( + pending_tx: ethers_providers::PendingTransaction<'_, P>, + expected: u64, + ) { + let provider = pending_tx.provider(); + let receipt = pending_tx.await.unwrap().unwrap(); + let tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); + assert_eq!(receipt.transaction_type, Some(expected.into())); + assert_eq!(tx.transaction_type, Some(expected.into())); + } + + let nonce = provider.get_transaction_count(address, None).await.unwrap(); + let bn = Some(BlockNumber::Pending.into()); + let gas_price = provider.get_gas_price().await.unwrap() * 125 / 100; + + let tx = + TransactionRequest::new().from(address).to(address).nonce(nonce).gas_price(gas_price); + let tx1 = provider.send_transaction(tx.clone(), bn).await.unwrap(); + + let tx = tx.clone().from(address).to(address).nonce(nonce + 1).with_access_list(vec![]); + let tx2 = provider.send_transaction(tx, bn).await.unwrap(); + + let tx = Eip1559TransactionRequest::new() + .from(address) + .to(address) + .nonce(nonce + 2) + .max_fee_per_gas(gas_price) + .max_priority_fee_per_gas(gas_price); + let tx3 = provider.send_transaction(tx, bn).await.unwrap(); + + futures_util::join!(check_tx(tx1, 0), check_tx(tx2, 1), check_tx(tx3, 2),); + } + + #[tokio::test] + async fn send_transaction_handles_tx_from_field() { + // launch anvil + let anvil = Anvil::new().spawn(); + + // grab 2 wallets + let signer: LocalWallet = anvil.keys()[0].clone().into(); + let other: LocalWallet = anvil.keys()[1].clone().into(); + + // connect to the network + let provider = Provider::try_from(anvil.endpoint()).unwrap(); + let provider = + SignerMiddleware::new_with_provider_chain(provider, signer.clone()).await.unwrap(); + + // sending a TransactionRequest with a from field of None should result + // in a transaction from the signer address + let request_from_none = TransactionRequest::new(); + let receipt = provider + .send_transaction(request_from_none, None) + .await + .unwrap() + .await + .unwrap() + .unwrap(); + let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); + + assert_eq!(sent_tx.from, signer.address()); + + // sending a TransactionRequest with the signer as the from address should + // result in a transaction from the signer address + let request_from_signer = TransactionRequest::new().from(signer.address()); + let receipt = provider + .send_transaction(request_from_signer, None) + .await + .unwrap() + .await + .unwrap() + .unwrap(); + let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); + + assert_eq!(sent_tx.from, signer.address()); + + // sending a TransactionRequest with a from address that is not the signer + // should result in a transaction from the specified address + let request_from_other = TransactionRequest::new().from(other.address()); + let receipt = provider + .send_transaction(request_from_other, None) + .await + .unwrap() + .await + .unwrap() + .unwrap(); + let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); + + assert_eq!(sent_tx.from, other.address()); + } +} + +#[cfg(feature = "celo")] +mod celo_tests { + use super::*; + + #[tokio::test] + async fn test_send_transaction() { + // Celo testnet + let provider = Provider::::try_from("https://alfajores-forno.celo-testnet.org") + .unwrap() + .interval(Duration::from_millis(3000u64)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + + // Funded with https://celo.org/developers/faucet + // Please do not drain this account :) + let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1" + .parse::() + .unwrap() + .with_chain_id(chain_id); + let client = SignerMiddleware::new(provider, wallet); + + let balance_before = client.get_balance(client.address(), None).await.unwrap(); + let tx = TransactionRequest::pay(client.address(), 100); + let _receipt = + client.send_transaction(tx, None).await.unwrap().confirmations(3).await.unwrap(); + let balance_after = client.get_balance(client.address(), None).await.unwrap(); + assert!(balance_before > balance_after); + } + + #[tokio::test] + async fn deploy_and_call_contract() { + // compiles the given contract and returns the ABI and Bytecode + fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) { + let path = format!("./tests/solidity-contracts/{path}"); + let compiled = Solc::default().compile_source(&path).unwrap(); + let contract = compiled.get(&path, name).expect("could not find contract"); + let (abi, bin, _) = contract.into_parts_or_default(); + (abi, bin) + } + + let (abi, bytecode) = compile_contract("SimpleStorage.sol", "SimpleStorage"); + + // Celo testnet + let provider = Provider::::try_from("https://alfajores-forno.celo-testnet.org") + .unwrap() + .interval(Duration::from_millis(6000)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + + // Funded with https://celo.org/developers/faucet + let wallet = "58ea5643a78c36926ad5128a6b0d8dfcc7fc705788a993b1c724be3469bc9697" + .parse::() + .unwrap() + .with_chain_id(chain_id); + let client = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap(); + let client = Arc::new(client); + + let factory = ContractFactory::new(abi, bytecode, client); + let deployer = factory.deploy(()).unwrap().legacy(); + let contract = deployer.block(BlockNumber::Pending).send().await.unwrap(); + + let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap(); + assert_eq!(value, 0.into()); + + // make a state mutating transaction + // gas estimation costs are sometimes under-reported on celo, + // so we manually set it to avoid failures + let call = contract.method::<_, H256>("setValue", U256::from(1)).unwrap().gas(100000); + let pending_tx = call.send().await.unwrap(); + let _receipt = pending_tx.await.unwrap(); + + let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap(); + assert_eq!(value, 1.into()); + } +} diff --git a/ethers-middleware/tests/solidity-contracts/DSProxy.sol b/ethers-middleware/tests/it/solidity-contracts/DSProxy.sol similarity index 100% rename from ethers-middleware/tests/solidity-contracts/DSProxy.sol rename to ethers-middleware/tests/it/solidity-contracts/DSProxy.sol diff --git a/ethers-middleware/tests/solidity-contracts/SimpleStorage.sol b/ethers-middleware/tests/it/solidity-contracts/SimpleStorage.sol similarity index 100% rename from ethers-middleware/tests/solidity-contracts/SimpleStorage.sol rename to ethers-middleware/tests/it/solidity-contracts/SimpleStorage.sol diff --git a/ethers-middleware/tests/stack.rs b/ethers-middleware/tests/it/stack.rs similarity index 98% rename from ethers-middleware/tests/stack.rs rename to ethers-middleware/tests/it/stack.rs index bf70056f..d0512d86 100644 --- a/ethers-middleware/tests/stack.rs +++ b/ethers-middleware/tests/it/stack.rs @@ -1,5 +1,3 @@ -#![cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))] - use ethers_core::{rand::thread_rng, types::TransactionRequest, utils::Anvil}; use ethers_middleware::{ gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice}, diff --git a/ethers-middleware/tests/transformer.rs b/ethers-middleware/tests/it/transformer.rs similarity index 100% rename from ethers-middleware/tests/transformer.rs rename to ethers-middleware/tests/it/transformer.rs diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs deleted file mode 100644 index a628ccb2..00000000 --- a/ethers-middleware/tests/signer.rs +++ /dev/null @@ -1,339 +0,0 @@ -#![allow(unused)] - -use ethers_contract::ContractFactory; -use ethers_core::{ - abi::Abi, - types::*, - utils::{parse_ether, parse_units, Anvil}, -}; -use ethers_middleware::signer::SignerMiddleware; -use ethers_providers::{Http, JsonRpcClient, Middleware, Provider, GOERLI}; -use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; -use ethers_solc::Solc; -use once_cell::sync::Lazy; -use std::{ - convert::TryFrom, - iter::Cycle, - sync::{atomic::AtomicU8, Arc}, - time::Duration, -}; - -static WALLETS: Lazy = Lazy::new(|| { - TestWallets { - mnemonic: MnemonicBuilder::default() - // Please don't drain this :) - .phrase("impose air often almost medal sudden finish quote dwarf devote theme layer"), - next: Default::default(), - } -}); - -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn send_eth() { - let anvil = Anvil::new().spawn(); - - // this private key belongs to the above mnemonic - let wallet: LocalWallet = anvil.keys()[0].clone().into(); - let wallet2: LocalWallet = anvil.keys()[1].clone().into(); - - // connect to the network - let provider = Provider::::try_from(anvil.endpoint()) - .unwrap() - .interval(Duration::from_millis(10u64)); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); - let provider = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap(); - - // craft the transaction - let tx = TransactionRequest::new().to(wallet2.address()).value(10000).chain_id(chain_id); - - let balance_before = provider.get_balance(provider.address(), None).await.unwrap(); - - // send it! - provider.send_transaction(tx, None).await.unwrap(); - - let balance_after = provider.get_balance(provider.address(), None).await.unwrap(); - - assert!(balance_before > balance_after); -} - -// hardhat compatibility test, to show hardhat rejects tx signed for other chains -#[tokio::test] -#[cfg(not(feature = "celo"))] -#[ignore] -async fn send_with_chain_id_hardhat() { - let wallet: LocalWallet = - "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap(); - let provider = Provider::try_from("http://localhost:8545").unwrap(); - let client = SignerMiddleware::new(provider, wallet); - - let tx = TransactionRequest::new().to(Address::random()).value(100u64); - let res = client.send_transaction(tx, None).await; - - let err = res.unwrap_err(); - assert!(err - .to_string() - .contains("Trying to send an incompatible EIP-155 transaction, signed for another chain.")); -} - -#[tokio::test] -#[cfg(not(feature = "celo"))] -#[ignore] -async fn send_with_chain_id_anvil() { - let wallet: LocalWallet = - "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap(); - let provider = Provider::try_from("http://localhost:8545").unwrap(); - let client = SignerMiddleware::new(provider, wallet); - - let tx = TransactionRequest::new().to(Address::random()).value(100u64); - let res = client.send_transaction(tx, None).await; - - let _err = res.unwrap_err(); -} - -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn pending_txs_with_confirmations_testnet() { - let provider = GOERLI.provider().interval(Duration::from_millis(3000)); - let chain_id = provider.get_chainid().await.unwrap(); - let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); - let address = wallet.address(); - let provider = SignerMiddleware::new(provider, wallet); - generic_pending_txs_test(provider, address).await; -} - -// different keys to avoid nonce errors -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn websocket_pending_txs_with_confirmations_testnet() { - let provider = GOERLI.ws().await.interval(Duration::from_millis(3000)); - let chain_id = provider.get_chainid().await.unwrap(); - let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); - let address = wallet.address(); - let provider = SignerMiddleware::new(provider, wallet); - generic_pending_txs_test(provider, address).await; -} - -#[cfg(not(feature = "celo"))] -async fn generic_pending_txs_test(provider: M, who: Address) { - let tx = TransactionRequest::new().to(who).from(who); - let pending_tx = provider.send_transaction(tx, None).await.unwrap(); - let tx_hash = *pending_tx; - let receipt = pending_tx.confirmations(1).await.unwrap().unwrap(); - // got the correct receipt - assert_eq!(receipt.transaction_hash, tx_hash); -} - -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn typed_txs() { - let provider = GOERLI.provider(); - - let chain_id = provider.get_chainid().await.unwrap(); - let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); - let address = wallet.address(); - // our wallet - let provider = SignerMiddleware::new(provider, wallet); - - // Uncomment the below and run this test to re-fund the wallets if they get drained. - // Would be ideal if we'd have a way to do this automatically, but this should be - // happening rarely enough that it doesn't matter. - // WALLETS.fund(provider.provider(), 10u32).await; - - async fn check_tx( - pending_tx: ethers_providers::PendingTransaction<'_, P>, - expected: u64, - ) { - let provider = pending_tx.provider(); - let receipt = pending_tx.await.unwrap().unwrap(); - let tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); - assert_eq!(receipt.transaction_type, Some(expected.into())); - assert_eq!(tx.transaction_type, Some(expected.into())); - } - - let nonce = provider.get_transaction_count(address, None).await.unwrap(); - let bn = Some(BlockNumber::Pending.into()); - let gas_price = provider.get_gas_price().await.unwrap() * 125 / 100; - - let tx = TransactionRequest::new().from(address).to(address).nonce(nonce).gas_price(gas_price); - let tx1 = provider.send_transaction(tx.clone(), bn).await.unwrap(); - - let tx = tx.clone().from(address).to(address).nonce(nonce + 1).with_access_list(vec![]); - let tx2 = provider.send_transaction(tx, bn).await.unwrap(); - - let tx = Eip1559TransactionRequest::new() - .from(address) - .to(address) - .nonce(nonce + 2) - .max_fee_per_gas(gas_price) - .max_priority_fee_per_gas(gas_price); - let tx3 = provider.send_transaction(tx, bn).await.unwrap(); - - futures_util::join!(check_tx(tx1, 0), check_tx(tx2, 1), check_tx(tx3, 2),); -} - -#[tokio::test] -#[cfg(feature = "celo")] -async fn test_send_transaction() { - // Celo testnet - let provider = Provider::::try_from("https://alfajores-forno.celo-testnet.org") - .unwrap() - .interval(Duration::from_millis(3000u64)); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); - - // Funded with https://celo.org/developers/faucet - // Please do not drain this account :) - let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1" - .parse::() - .unwrap() - .with_chain_id(chain_id); - let client = SignerMiddleware::new(provider, wallet); - - let balance_before = client.get_balance(client.address(), None).await.unwrap(); - let tx = TransactionRequest::pay(client.address(), 100); - let _receipt = client.send_transaction(tx, None).await.unwrap().confirmations(3).await.unwrap(); - let balance_after = client.get_balance(client.address(), None).await.unwrap(); - assert!(balance_before > balance_after); -} - -#[tokio::test] -#[cfg(not(feature = "celo"))] -async fn send_transaction_handles_tx_from_field() { - // launch anvil - let anvil = Anvil::new().spawn(); - - // grab 2 wallets - let signer: LocalWallet = anvil.keys()[0].clone().into(); - let other: LocalWallet = anvil.keys()[1].clone().into(); - - // connect to the network - let provider = Provider::try_from(anvil.endpoint()).unwrap(); - let provider = - SignerMiddleware::new_with_provider_chain(provider, signer.clone()).await.unwrap(); - - // sending a TransactionRequest with a from field of None should result - // in a transaction from the signer address - let request_from_none = TransactionRequest::new(); - let receipt = - provider.send_transaction(request_from_none, None).await.unwrap().await.unwrap().unwrap(); - let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); - - assert_eq!(sent_tx.from, signer.address()); - - // sending a TransactionRequest with the signer as the from address should - // result in a transaction from the signer address - let request_from_signer = TransactionRequest::new().from(signer.address()); - let receipt = - provider.send_transaction(request_from_signer, None).await.unwrap().await.unwrap().unwrap(); - let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); - - assert_eq!(sent_tx.from, signer.address()); - - // sending a TransactionRequest with a from address that is not the signer - // should result in a transaction from the specified address - let request_from_other = TransactionRequest::new().from(other.address()); - let receipt = - provider.send_transaction(request_from_other, None).await.unwrap().await.unwrap().unwrap(); - let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap(); - - assert_eq!(sent_tx.from, other.address()); -} - -#[tokio::test] -#[cfg(feature = "celo")] -async fn deploy_and_call_contract() { - // compiles the given contract and returns the ABI and Bytecode - fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) { - let path = format!("./tests/solidity-contracts/{path}"); - let compiled = Solc::default().compile_source(&path).unwrap(); - let contract = compiled.get(&path, name).expect("could not find contract"); - let (abi, bin, _) = contract.into_parts_or_default(); - (abi, bin) - } - - let (abi, bytecode) = compile_contract("SimpleStorage.sol", "SimpleStorage"); - - // Celo testnet - let provider = Provider::::try_from("https://alfajores-forno.celo-testnet.org") - .unwrap() - .interval(Duration::from_millis(6000)); - let chain_id = provider.get_chainid().await.unwrap().as_u64(); - - // Funded with https://celo.org/developers/faucet - let wallet = "58ea5643a78c36926ad5128a6b0d8dfcc7fc705788a993b1c724be3469bc9697" - .parse::() - .unwrap() - .with_chain_id(chain_id); - let client = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap(); - let client = Arc::new(client); - - let factory = ContractFactory::new(abi, bytecode, client); - let deployer = factory.deploy(()).unwrap().legacy(); - let contract = deployer.block(BlockNumber::Pending).send().await.unwrap(); - - let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap(); - assert_eq!(value, 0.into()); - - // make a state mutating transaction - // gas estimation costs are sometimes under-reported on celo, - // so we manually set it to avoid failures - let call = contract.method::<_, H256>("setValue", U256::from(1)).unwrap().gas(100000); - let pending_tx = call.send().await.unwrap(); - let _receipt = pending_tx.await.unwrap(); - - let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap(); - assert_eq!(value, 1.into()); -} - -#[derive(Debug, Default)] -struct TestWallets { - mnemonic: MnemonicBuilder, - next: AtomicU8, -} - -impl TestWallets { - /// Helper for funding the wallets with an instantiated provider - #[allow(unused)] - pub async fn fund>(&self, provider: &Provider, n: U) { - let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::>(); - // hardcoded funder address private key, GOERLI - let signer = "39aa18eeb5d12c071e5f19d8e9375a872e90cb1f2fa640384ffd8800a2f3e8f1" - .parse::() - .unwrap() - .with_chain_id(provider.get_chainid().await.unwrap().as_u64()); - let provider = SignerMiddleware::new(provider, signer); - let addr = provider.address(); - - let mut nonce = provider.get_transaction_count(addr, None).await.unwrap(); - let mut pending_txs = Vec::new(); - for addr in addrs { - println!("Funding wallet {addr:?}"); - let tx = TransactionRequest::new() - .nonce(nonce) - .to(addr) - // 0.1 eth per wallet - .value(parse_ether("1").unwrap()); - pending_txs.push( - provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(), - ); - nonce += 1.into(); - } - - futures_util::future::join_all(pending_txs).await; - } - - pub fn next(&self) -> LocalWallet { - let idx = self.next.fetch_add(1, std::sync::atomic::Ordering::SeqCst); - - // println!("Got wallet {:?}", wallet.address()); - self.get(idx) - } - - pub fn get>(&self, idx: T) -> LocalWallet { - self.mnemonic - .clone() - .index(idx) - .expect("index not found") - .build() - .expect("cannot build wallet") - } -} diff --git a/ethers-providers/tests/it/main.rs b/ethers-providers/tests/it/main.rs new file mode 100644 index 00000000..959eaf03 --- /dev/null +++ b/ethers-providers/tests/it/main.rs @@ -0,0 +1,8 @@ +#![cfg(not(target_arch = "wasm32"))] + +mod provider; + +mod txpool; + +#[cfg(not(feature = "celo"))] +mod ws_errors; diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/it/provider.rs similarity index 99% rename from ethers-providers/tests/provider.rs rename to ethers-providers/tests/it/provider.rs index 4878964b..dcd3dd94 100644 --- a/ethers-providers/tests/provider.rs +++ b/ethers-providers/tests/it/provider.rs @@ -1,4 +1,3 @@ -#![cfg(not(target_arch = "wasm32"))] use ethers_providers::{Http, Middleware, Provider}; use std::{convert::TryFrom, time::Duration}; diff --git a/ethers-providers/tests/txpool.rs b/ethers-providers/tests/it/txpool.rs similarity index 97% rename from ethers-providers/tests/txpool.rs rename to ethers-providers/tests/it/txpool.rs index 868e57f6..4c06ba74 100644 --- a/ethers-providers/tests/txpool.rs +++ b/ethers-providers/tests/it/txpool.rs @@ -1,4 +1,3 @@ -#![cfg(not(target_arch = "wasm32"))] use ethers_core::{ types::{TransactionRequest, U256}, utils::Anvil, diff --git a/ethers-providers/tests/ws_errors.rs b/ethers-providers/tests/it/ws_errors.rs similarity index 98% rename from ethers-providers/tests/ws_errors.rs rename to ethers-providers/tests/it/ws_errors.rs index 170771a8..019d6165 100644 --- a/ethers-providers/tests/ws_errors.rs +++ b/ethers-providers/tests/it/ws_errors.rs @@ -1,5 +1,3 @@ -#![cfg(not(feature = "celo"))] - use ethers_providers::{Middleware, Provider, StreamExt, Ws}; use futures_util::SinkExt; use std::time::Duration;