diff --git a/CHANGELOG.md b/CHANGELOG.md index 7751252b..3208a490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -291,6 +291,8 @@ ### Unreleased +- Return pending transaction from `Multicall::send` + [#2044](https://github.com/gakonst/ethers-rs/pull/2044) - Add abigen to default features [#1684](https://github.com/gakonst/ethers-rs/pull/1684) - Add extra Multicall helper methods diff --git a/ethers-contract/src/contract.rs b/ethers-contract/src/contract.rs index 5f417611..09a2d713 100644 --- a/ethers-contract/src/contract.rs +++ b/ethers-contract/src/contract.rs @@ -4,21 +4,18 @@ use crate::{ event::{EthEvent, Event}, EthLogDecode, }; - use ethers_core::{ abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize}, types::{Address, Filter, Selector, ValueOrArray}, }; +use ethers_providers::Middleware; +use std::{marker::PhantomData, sync::Arc}; #[cfg(not(feature = "legacy"))] use ethers_core::types::Eip1559TransactionRequest; #[cfg(feature = "legacy")] use ethers_core::types::TransactionRequest; -use ethers_providers::Middleware; - -use std::{fmt::Debug, marker::PhantomData, sync::Arc}; - /// A Contract is an abstraction of an executable program on the Ethereum Blockchain. /// It has code (called byte code) as well as allocated long-term memory /// (called storage). Every deployed Contract has an address, which is used to connect @@ -161,6 +158,7 @@ pub struct Contract { impl std::ops::Deref for Contract { type Target = BaseContract; + fn deref(&self) -> &Self::Target { &self.base_contract } @@ -177,19 +175,24 @@ impl Clone for Contract { } impl Contract { - /// Returns the contract's address + /// Returns the contract's address. pub fn address(&self) -> Address { self.address } - /// Returns a reference to the contract's ABI + /// Returns a reference to the contract's ABI. pub fn abi(&self) -> &Abi { &self.base_contract.abi } - /// Returns a pointer to the contract's client + /// Returns a pointer to the contract's client. pub fn client(&self) -> Arc { - self.client.clone() + Arc::clone(&self.client) + } + + /// Returns a reference to the contract's client. + pub fn client_ref(&self) -> &M { + Arc::as_ref(&self.client) } } @@ -301,10 +304,7 @@ impl Contract { /// /// Clones `self` internally #[must_use] - pub fn at>(&self, address: T) -> Self - where - M: Clone, - { + pub fn at>(&self, address: T) -> Self { let mut this = self.clone(); this.address = address.into(); this @@ -314,10 +314,7 @@ impl Contract { /// /// Clones `self` internally #[must_use] - pub fn connect(&self, client: Arc) -> Contract - where - N: Clone, - { + pub fn connect(&self, client: Arc) -> Contract { Contract { base_contract: self.base_contract.clone(), client, address: self.address } } } diff --git a/ethers-contract/src/multicall/mod.rs b/ethers-contract/src/multicall/mod.rs index 0b5bc56f..73c67a39 100644 --- a/ethers-contract/src/multicall/mod.rs +++ b/ethers-contract/src/multicall/mod.rs @@ -4,9 +4,9 @@ use crate::{ }; use ethers_core::{ abi::{AbiDecode, Detokenize, Function, Token}, - types::{Address, BlockNumber, Bytes, Chain, NameOrAddress, TxHash, H160, U256}, + types::{Address, BlockNumber, Bytes, Chain, NameOrAddress, H160, U256}, }; -use ethers_providers::Middleware; +use ethers_providers::{Middleware, PendingTransaction}; use std::{convert::TryFrom, sync::Arc}; pub mod multicall_contract; @@ -223,8 +223,7 @@ impl TryFrom for MulticallVersion { /// /// // `await`ing the `send` method waits for the transaction to be broadcast, which also /// // returns the transaction hash -/// let tx_hash = multicall.send().await?; -/// let _tx_receipt = PendingTransaction::new(tx_hash, &client).await?; +/// let _tx_receipt = multicall.send().await?.await.expect("tx dropped"); /// /// // you can also query ETH balances of multiple addresses /// let address_1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".parse::
()?; @@ -569,7 +568,7 @@ impl Multicall { /// .add_call(broadcast_1, false) /// .add_call(broadcast_2, false); /// - /// let _tx_hash = multicall.send().await?; + /// let _tx_receipt = multicall.send().await?.await.expect("tx dropped"); /// /// # let call_1 = contract.method::<_, String>("getValue", ())?; /// # let call_2 = contract.method::<_, Address>("lastSender", ())?; @@ -735,7 +734,7 @@ impl Multicall { v @ (MulticallVersion::Multicall2 | MulticallVersion::Multicall3) => { let is_v2 = v == MulticallVersion::Multicall2; let call = if is_v2 { self.as_try_aggregate() } else { self.as_aggregate_3() }; - let return_data = call.call().await?; + let return_data = ContractCall::call(&call).await?; self.calls .iter() .zip(return_data.into_iter()) @@ -789,7 +788,7 @@ impl Multicall { } /// Signs and broadcasts a batch of transactions by using the Multicall contract as proxy, - /// returning the transaction hash once the transaction confirms. + /// returning the pending transaction. /// /// Note: this method will broadcast a transaction from an account, meaning it must have /// sufficient funds for gas and transaction value. @@ -811,32 +810,18 @@ impl Multicall { /// # Ok(()) /// # } /// ``` - pub async fn send(&self) -> Result { - // Broadcast transaction and return the transaction hash - // TODO: Can we make this return a PendingTransaction directly instead? - // Seems hard due to `returns a value referencing data owned by the current function` - - // running clippy --fix on this throws E0597 - #[allow(clippy::let_and_return)] - let tx_hash = match self.version { - MulticallVersion::Multicall => { - let call = self.as_aggregate(); - let hash = *call.send().await?; - hash - } - MulticallVersion::Multicall2 => { - let call = self.as_try_aggregate(); - let hash = *call.send().await?; - hash - } - MulticallVersion::Multicall3 => { - let call = self.as_aggregate_3_value(); - let hash = *call.send().await?; - hash - } + pub async fn send(&self) -> Result, M> { + let tx = match self.version { + MulticallVersion::Multicall => self.as_aggregate().tx, + MulticallVersion::Multicall2 => self.as_try_aggregate().tx, + MulticallVersion::Multicall3 => self.as_aggregate_3_value().tx, }; - Ok(tx_hash) + self.contract + .client_ref() + .send_transaction(tx, self.block.map(Into::into)) + .await + .map_err(|e| MulticallError::ContractError(ContractError::MiddlewareError(e))) } /// v1 diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs index 8fdd0469..c4f58fba 100644 --- a/ethers-contract/tests/it/contract.rs +++ b/ethers-contract/tests/it/contract.rs @@ -491,8 +491,7 @@ mod eth_tests { multicall_send.clear_calls().add_call(broadcast, false).add_call(broadcast2, false); // broadcast the transaction and wait for it to be mined - let tx_hash = multicall_send.legacy().send().await.unwrap(); - let _tx_receipt = PendingTransaction::new(tx_hash, client.provider()).await.unwrap(); + 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