Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat(contract): return multicall pending transaction #2044

Merged
merged 3 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,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
Expand Down
31 changes: 14 additions & 17 deletions ethers-contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -161,6 +158,7 @@ pub struct Contract<M> {

impl<M> std::ops::Deref for Contract<M> {
type Target = BaseContract;

fn deref(&self) -> &Self::Target {
&self.base_contract
}
Expand All @@ -177,19 +175,24 @@ impl<M> Clone for Contract<M> {
}

impl<M> Contract<M> {
/// 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<M> {
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)
}
}

Expand Down Expand Up @@ -301,10 +304,7 @@ impl<M: Middleware> Contract<M> {
///
/// Clones `self` internally
#[must_use]
pub fn at<T: Into<Address>>(&self, address: T) -> Self
where
M: Clone,
{
pub fn at<T: Into<Address>>(&self, address: T) -> Self {
let mut this = self.clone();
this.address = address.into();
this
Expand All @@ -314,10 +314,7 @@ impl<M: Middleware> Contract<M> {
///
/// Clones `self` internally
#[must_use]
pub fn connect<N>(&self, client: Arc<N>) -> Contract<N>
where
N: Clone,
{
pub fn connect<N>(&self, client: Arc<N>) -> Contract<N> {
Contract { base_contract: self.base_contract.clone(), client, address: self.address }
}
}
47 changes: 16 additions & 31 deletions ethers-contract/src/multicall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -223,8 +223,7 @@ impl TryFrom<u8> 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::<Address>()?;
Expand Down Expand Up @@ -569,7 +568,7 @@ impl<M: Middleware> Multicall<M> {
/// .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", ())?;
Expand Down Expand Up @@ -735,7 +734,7 @@ impl<M: Middleware> Multicall<M> {
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())
Expand Down Expand Up @@ -789,7 +788,7 @@ impl<M: Middleware> Multicall<M> {
}

/// 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.
Expand All @@ -811,32 +810,18 @@ impl<M: Middleware> Multicall<M> {
/// # Ok(())
/// # }
/// ```
pub async fn send(&self) -> Result<TxHash, M> {
// 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<PendingTransaction<'_, M::Provider>, 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
Expand Down
3 changes: 1 addition & 2 deletions ethers-contract/tests/it/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down