feat(contract): add extra Multicall helper methods (#1666)

* feat(contract): add extra Multicall helper methods

* docs: update CHANGELOG.md

* normalize helper methods' names
This commit is contained in:
DaniPopes 2022-09-05 18:54:49 +02:00 committed by GitHub
parent 4e1462423f
commit 72449c09e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 44 deletions

View File

@ -4,7 +4,7 @@
### Unreleased
- Fix RLP encoding of `TransactionReceipt`
- Fix RLP encoding of `TransactionReceipt` [#1661](https://github.com/gakonst/ethers-rs/pull/1661)
- Add `Unit8` helper type [#1639](https://github.com/gakonst/ethers-rs/pull/1639)
- Add `evm.deployedBytecode.immutableReferences` output selector [#1523](https://github.com/gakonst/ethers-rs/pull/1523)
- Added `get_erc1155_token_transfer_events` function for etherscan client [#1503](https://github.com/gakonst/ethers-rs/pull/1503)
@ -263,6 +263,10 @@
### Unreleased
- Add extra Multicall helper methods
[#1666](https://github.com/gakonst/ethers-rs/pull/1666)
- Update Multicall to Multicall3
[#1584](https://github.com/gakonst/ethers-rs/pull/1584)
- Add `Event::stream_with_meta` and `Event::subscribe_with_meta`
[#1483](https://github.com/gakonst/ethers-rs/pull/1483)
- Added tx builder methods to `ContractFactory`

View File

@ -96,6 +96,17 @@ pub enum MulticallError<M: Middleware> {
pub type Result<T, M> = std::result::Result<T, MulticallError<M>>;
/// Helper struct for managing calls to be made to the `function` in smart contract `target`
/// with `data`.
#[derive(Clone, Debug)]
pub struct Call {
target: Address,
data: Bytes,
value: U256,
allow_failure: bool,
function: Function,
}
/// The version of the [`Multicall`](super::Multicall).
/// Used to determine which methods of the Multicall smart contract to use:
/// - [`Multicall`] : `aggregate((address,bytes)[])`
@ -224,16 +235,16 @@ impl TryFrom<u8> for MulticallVersion {
/// multicall = multicall.version(MulticallVersion::Multicall);
/// multicall
/// .clear_calls()
/// .eth_balance_of(address_1, false)
/// .eth_balance_of(address_2, false);
/// .add_get_eth_balance(address_1, false)
/// .add_get_eth_balance(address_2, false);
/// let _balances: (U256, U256) = multicall.call().await?;
///
/// // or with version 2 and above
/// multicall = multicall.version(MulticallVersion::Multicall3);
/// multicall
/// .clear_calls()
/// .eth_balance_of(address_1, false)
/// .eth_balance_of(address_2, false);
/// .add_get_eth_balance(address_1, false)
/// .add_get_eth_balance(address_2, false);
/// let _balances: ((bool, U256), (bool, U256)) = multicall.call().await?;
///
/// # Ok(())
@ -248,6 +259,7 @@ impl TryFrom<u8> for MulticallVersion {
/// [`add_call`]: #method.add_call
/// [`call`]: #method.call
/// [`send`]: #method.send
#[derive(Clone)]
#[must_use = "Multicall does nothing unless you use `call` or `send`"]
pub struct Multicall<M> {
version: MulticallVersion,
@ -257,18 +269,7 @@ pub struct Multicall<M> {
contract: MulticallContract<M>,
}
impl<M> Clone for Multicall<M> {
fn clone(&self) -> Self {
Multicall {
calls: self.calls.clone(),
block: self.block,
contract: self.contract.clone(),
legacy: self.legacy,
version: self.version,
}
}
}
// Manually implement Debug due to Middleware trait bounds.
impl<M: Middleware> std::fmt::Debug for Multicall<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Multicall")
@ -281,17 +282,6 @@ impl<M: Middleware> std::fmt::Debug for Multicall<M> {
}
}
/// Helper struct for managing calls to be made to the `function` in smart contract `target`
/// with `data`.
#[derive(Clone, Debug)]
pub struct Call {
target: Address,
data: Bytes,
value: U256,
allow_failure: bool,
function: Function,
}
impl<M: Middleware> Multicall<M> {
/// Creates a new Multicall instance from the provided client. If provided with an `address`,
/// it instantiates the Multicall contract with that address, otherwise it defaults to
@ -410,13 +400,13 @@ impl<M: Middleware> Multicall<M> {
self
}
/// Sets the `block` field for the multicall aggregate call.
/// Sets the `block` field of the Multicall aggregate call.
pub fn block(mut self, block: impl Into<BlockNumber>) -> Self {
self.block = Some(block.into());
self
}
/// Appends a `call` to the list of calls for the Multicall instance.
/// Appends a `call` to the list of calls of the Multicall instance.
///
/// Version specific details:
/// - 1: `allow_failure` is ignored.
@ -447,18 +437,89 @@ impl<M: Middleware> Multicall<M> {
}
}
/// Appends a `call` to the list of calls for the Multicall instance for querying
/// the ETH balance of an address
/// Appends a `call` to the list of calls of the Multicall instance for querying the block hash
/// of a given block number.
///
/// # Panics
/// Note: this call will return 0 if `block_number` is not one of the most recent 256 blocks.
/// ([Reference](https://docs.soliditylang.org/en/latest/units-and-global-variables.html?highlight=blockhash#block-and-transaction-properties))
pub fn add_get_block_hash(&mut self, block_number: impl Into<U256>) -> &mut Self {
let call = self.contract.get_block_hash(block_number.into());
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the current
/// block number.
pub fn add_get_block_number(&mut self) -> &mut Self {
let call = self.contract.get_block_number();
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the current
/// block coinbase address.
pub fn add_get_current_block_coinbase(&mut self) -> &mut Self {
let call = self.contract.get_current_block_coinbase();
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the current
/// block difficulty.
///
/// If more than the maximum number of supported calls are added (16). The maximum limit is
/// constrained due to tokenization/detokenization support for tuples.
pub fn eth_balance_of(&mut self, addr: Address, allow_failure: bool) -> &mut Self {
let call = self.contract.get_eth_balance(addr);
/// Note: in a post-merge environment, the return value of this call will be the output of the
/// randomness beacon provided by the beacon chain.
/// ([Reference](https://eips.ethereum.org/EIPS/eip-4399#abstract))
pub fn add_get_current_block_difficulty(&mut self) -> &mut Self {
let call = self.contract.get_current_block_difficulty();
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the current
/// block gas limit.
pub fn add_get_current_block_gas_limit(&mut self) -> &mut Self {
let call = self.contract.get_current_block_gas_limit();
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the current
/// block timestamp.
pub fn add_get_current_block_timestamp(&mut self) -> &mut Self {
let call = self.contract.get_current_block_timestamp();
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the ETH
/// balance of an address.
pub fn add_get_eth_balance(
&mut self,
address: impl Into<Address>,
allow_failure: bool,
) -> &mut Self {
let call = self.contract.get_eth_balance(address.into());
self.add_call(call, allow_failure)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the last
/// block hash.
pub fn add_get_last_block_hash(&mut self) -> &mut Self {
let call = self.contract.get_last_block_hash();
self.add_call(call, false)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the current
/// block base fee.
///
/// Note: this call will fail if the chain that it is called on does not implement the
/// [BASEFEE opcode](https://eips.ethereum.org/EIPS/eip-3198).
pub fn add_get_basefee(&mut self, allow_failure: bool) -> &mut Self {
let call = self.contract.get_basefee();
self.add_call(call, allow_failure)
}
/// Appends a `call` to the list of calls of the Multicall instance for querying the chain id.
pub fn add_get_chain_id(&mut self) -> &mut Self {
let call = self.contract.get_chain_id();
self.add_call(call, false)
}
/// Clears the batch of calls from the Multicall instance.
/// Re-use the already instantiated Multicall to send a different batch of transactions or do
/// another aggregate query.

View File

@ -499,9 +499,9 @@ mod eth_tests {
// so should have 100 ETH
multicall
.clear_calls()
.eth_balance_of(addrs[4], false)
.eth_balance_of(addrs[5], false)
.eth_balance_of(addrs[6], false);
.add_get_eth_balance(addrs[4], false)
.add_get_eth_balance(addrs[5], false)
.add_get_eth_balance(addrs[6], false);
let balances: (U256, U256, U256) = multicall.call().await.unwrap();
assert_eq!(balances.0, U256::from(10_000_000_000_000_000_000_000u128));
@ -653,8 +653,8 @@ mod eth_tests {
// ((bool, U256)) == (bool, U256)
let bal_before: ((bool, U256), (bool, U256)) = multicall
.clear_calls()
.eth_balance_of(rc_addr, false)
.eth_balance_of(rc_addr, false)
.add_get_eth_balance(rc_addr, false)
.add_get_eth_balance(rc_addr, false)
.call()
.await
.unwrap();
@ -665,8 +665,8 @@ mod eth_tests {
let bal_after: ((bool, U256), (bool, U256)) = multicall
.clear_calls()
.eth_balance_of(rc_addr, false)
.eth_balance_of(rc_addr, false)
.add_get_eth_balance(rc_addr, false)
.add_get_eth_balance(rc_addr, false)
.call()
.await
.unwrap();