From 72449c09e1fba536a1899780e42b11bbad452f22 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 5 Sep 2022 18:54:49 +0200 Subject: [PATCH] feat(contract): add extra Multicall helper methods (#1666) * feat(contract): add extra Multicall helper methods * docs: update CHANGELOG.md * normalize helper methods' names --- CHANGELOG.md | 6 +- ethers-contract/src/multicall/mod.rs | 133 +++++++++++++++++++-------- ethers-contract/tests/it/contract.rs | 14 +-- 3 files changed, 109 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa26373d..c544f355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/ethers-contract/src/multicall/mod.rs b/ethers-contract/src/multicall/mod.rs index 15e77c2c..2754e819 100644 --- a/ethers-contract/src/multicall/mod.rs +++ b/ethers-contract/src/multicall/mod.rs @@ -96,6 +96,17 @@ pub enum MulticallError { pub type Result = std::result::Result>; +/// 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 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 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 { version: MulticallVersion, @@ -257,18 +269,7 @@ pub struct Multicall { contract: MulticallContract, } -impl Clone for Multicall { - 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 std::fmt::Debug for Multicall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Multicall") @@ -281,17 +282,6 @@ impl std::fmt::Debug for Multicall { } } -/// 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 Multicall { /// 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 Multicall { 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) -> 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 Multicall { } } - /// 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) -> &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
, + 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. diff --git a/ethers-contract/tests/it/contract.rs b/ethers-contract/tests/it/contract.rs index 915a17b9..756210b2 100644 --- a/ethers-contract/tests/it/contract.rs +++ b/ethers-contract/tests/it/contract.rs @@ -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();