feat(contract): return multicall pending transaction (#2044)
* feat(contract): return multicall pending transaction * fix: tests * docs: update CHANGELOG.md
This commit is contained in:
parent
b4b153a364
commit
015eeabea8
|
@ -291,6 +291,8 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- Return pending transaction from `Multicall::send`
|
||||||
|
[#2044](https://github.com/gakonst/ethers-rs/pull/2044)
|
||||||
- Add abigen to default features
|
- Add abigen to default features
|
||||||
[#1684](https://github.com/gakonst/ethers-rs/pull/1684)
|
[#1684](https://github.com/gakonst/ethers-rs/pull/1684)
|
||||||
- Add extra Multicall helper methods
|
- Add extra Multicall helper methods
|
||||||
|
|
|
@ -4,21 +4,18 @@ use crate::{
|
||||||
event::{EthEvent, Event},
|
event::{EthEvent, Event},
|
||||||
EthLogDecode,
|
EthLogDecode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize},
|
abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize},
|
||||||
types::{Address, Filter, Selector, ValueOrArray},
|
types::{Address, Filter, Selector, ValueOrArray},
|
||||||
};
|
};
|
||||||
|
use ethers_providers::Middleware;
|
||||||
|
use std::{marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
#[cfg(not(feature = "legacy"))]
|
#[cfg(not(feature = "legacy"))]
|
||||||
use ethers_core::types::Eip1559TransactionRequest;
|
use ethers_core::types::Eip1559TransactionRequest;
|
||||||
#[cfg(feature = "legacy")]
|
#[cfg(feature = "legacy")]
|
||||||
use ethers_core::types::TransactionRequest;
|
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.
|
/// 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
|
/// 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
|
/// (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> {
|
impl<M> std::ops::Deref for Contract<M> {
|
||||||
type Target = BaseContract;
|
type Target = BaseContract;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.base_contract
|
&self.base_contract
|
||||||
}
|
}
|
||||||
|
@ -177,19 +175,24 @@ impl<M> Clone for Contract<M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> Contract<M> {
|
impl<M> Contract<M> {
|
||||||
/// Returns the contract's address
|
/// Returns the contract's address.
|
||||||
pub fn address(&self) -> Address {
|
pub fn address(&self) -> 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 {
|
pub fn abi(&self) -> &Abi {
|
||||||
&self.base_contract.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> {
|
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
|
/// Clones `self` internally
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn at<T: Into<Address>>(&self, address: T) -> Self
|
pub fn at<T: Into<Address>>(&self, address: T) -> Self {
|
||||||
where
|
|
||||||
M: Clone,
|
|
||||||
{
|
|
||||||
let mut this = self.clone();
|
let mut this = self.clone();
|
||||||
this.address = address.into();
|
this.address = address.into();
|
||||||
this
|
this
|
||||||
|
@ -314,10 +314,7 @@ impl<M: Middleware> Contract<M> {
|
||||||
///
|
///
|
||||||
/// Clones `self` internally
|
/// Clones `self` internally
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn connect<N>(&self, client: Arc<N>) -> Contract<N>
|
pub fn connect<N>(&self, client: Arc<N>) -> Contract<N> {
|
||||||
where
|
|
||||||
N: Clone,
|
|
||||||
{
|
|
||||||
Contract { base_contract: self.base_contract.clone(), client, address: self.address }
|
Contract { base_contract: self.base_contract.clone(), client, address: self.address }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ use crate::{
|
||||||
};
|
};
|
||||||
use ethers_core::{
|
use ethers_core::{
|
||||||
abi::{AbiDecode, Detokenize, Function, Token},
|
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};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
|
|
||||||
pub mod multicall_contract;
|
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
|
/// // `await`ing the `send` method waits for the transaction to be broadcast, which also
|
||||||
/// // returns the transaction hash
|
/// // returns the transaction hash
|
||||||
/// let tx_hash = multicall.send().await?;
|
/// let _tx_receipt = multicall.send().await?.await.expect("tx dropped");
|
||||||
/// let _tx_receipt = PendingTransaction::new(tx_hash, &client).await?;
|
|
||||||
///
|
///
|
||||||
/// // you can also query ETH balances of multiple addresses
|
/// // you can also query ETH balances of multiple addresses
|
||||||
/// let address_1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".parse::<Address>()?;
|
/// let address_1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".parse::<Address>()?;
|
||||||
|
@ -569,7 +568,7 @@ impl<M: Middleware> Multicall<M> {
|
||||||
/// .add_call(broadcast_1, false)
|
/// .add_call(broadcast_1, false)
|
||||||
/// .add_call(broadcast_2, 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_1 = contract.method::<_, String>("getValue", ())?;
|
||||||
/// # let call_2 = contract.method::<_, Address>("lastSender", ())?;
|
/// # let call_2 = contract.method::<_, Address>("lastSender", ())?;
|
||||||
|
@ -735,7 +734,7 @@ impl<M: Middleware> Multicall<M> {
|
||||||
v @ (MulticallVersion::Multicall2 | MulticallVersion::Multicall3) => {
|
v @ (MulticallVersion::Multicall2 | MulticallVersion::Multicall3) => {
|
||||||
let is_v2 = v == MulticallVersion::Multicall2;
|
let is_v2 = v == MulticallVersion::Multicall2;
|
||||||
let call = if is_v2 { self.as_try_aggregate() } else { self.as_aggregate_3() };
|
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
|
self.calls
|
||||||
.iter()
|
.iter()
|
||||||
.zip(return_data.into_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,
|
/// 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
|
/// Note: this method will broadcast a transaction from an account, meaning it must have
|
||||||
/// sufficient funds for gas and transaction value.
|
/// sufficient funds for gas and transaction value.
|
||||||
|
@ -811,32 +810,18 @@ impl<M: Middleware> Multicall<M> {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn send(&self) -> Result<TxHash, M> {
|
pub async fn send(&self) -> Result<PendingTransaction<'_, M::Provider>, M> {
|
||||||
// Broadcast transaction and return the transaction hash
|
let tx = match self.version {
|
||||||
// TODO: Can we make this return a PendingTransaction directly instead?
|
MulticallVersion::Multicall => self.as_aggregate().tx,
|
||||||
// Seems hard due to `returns a value referencing data owned by the current function`
|
MulticallVersion::Multicall2 => self.as_try_aggregate().tx,
|
||||||
|
MulticallVersion::Multicall3 => self.as_aggregate_3_value().tx,
|
||||||
// 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
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
/// v1
|
||||||
|
|
|
@ -491,8 +491,7 @@ mod eth_tests {
|
||||||
multicall_send.clear_calls().add_call(broadcast, false).add_call(broadcast2, false);
|
multicall_send.clear_calls().add_call(broadcast, false).add_call(broadcast2, false);
|
||||||
|
|
||||||
// broadcast the transaction and wait for it to be mined
|
// broadcast the transaction and wait for it to be mined
|
||||||
let tx_hash = multicall_send.legacy().send().await.unwrap();
|
let _tx_receipt = multicall_send.legacy().send().await.unwrap().await.unwrap();
|
||||||
let _tx_receipt = PendingTransaction::new(tx_hash, client.provider()).await.unwrap();
|
|
||||||
|
|
||||||
// Do another multicall to check the updated return values
|
// Do another multicall to check the updated return values
|
||||||
// The `getValue` calls should return the last value we set in the batched broadcast
|
// The `getValue` calls should return the last value we set in the batched broadcast
|
||||||
|
|
Loading…
Reference in New Issue