feat(contract): return multicall pending transaction (#2044)

* feat(contract): return multicall pending transaction

* fix: tests

* docs: update CHANGELOG.md
This commit is contained in:
DaniPopes 2023-01-12 04:30:56 +01:00 committed by GitHub
parent b4b153a364
commit 015eeabea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 50 deletions

View File

@ -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

View File

@ -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<M> {
impl<M> std::ops::Deref for Contract<M> {
type Target = BaseContract;
fn deref(&self) -> &Self::Target {
&self.base_contract
}
@ -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)
}
}
@ -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
@ -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 }
}
}

View File

@ -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<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>()?;
@ -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", ())?;
@ -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())
@ -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.
@ -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

View File

@ -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