feat(contract): add_calls and call_array for multicall (#1941)
This commit is contained in:
parent
5bc9ee73b2
commit
279aea6323
|
@ -103,6 +103,7 @@
|
||||||
- [#842](https://github.com/gakonst/ethers-rs/issues/842) Add support for I256 types in `parse_units` and `format_units`.
|
- [#842](https://github.com/gakonst/ethers-rs/issues/842) Add support for I256 types in `parse_units` and `format_units`.
|
||||||
Added `twos_complement` function for I256.
|
Added `twos_complement` function for I256.
|
||||||
- [#1934](https://github.com/gakonst/ethers-rs/pull/1934) Allow 16 calls in multicall.
|
- [#1934](https://github.com/gakonst/ethers-rs/pull/1934) Allow 16 calls in multicall.
|
||||||
|
- [#1941](https://github.com/gakonst/ethers-rs/pull/1941) Add `add_calls` and `call_array` for `Multicall`.
|
||||||
|
|
||||||
## ethers-contract-abigen
|
## ethers-contract-abigen
|
||||||
|
|
||||||
|
|
|
@ -437,6 +437,27 @@ impl<M: Middleware> Multicall<M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Appends multiple `call`s to the list of calls of the Multicall instance.
|
||||||
|
///
|
||||||
|
/// Version specific details:
|
||||||
|
/// - 1: `allow_failure` is ignored.
|
||||||
|
/// - >=2: `allow_failure` specifies whether or not this call is allowed to revert in the
|
||||||
|
/// multicall.
|
||||||
|
/// - 3: Transaction values are used when broadcasting transactions with [`send`], otherwise
|
||||||
|
/// they are always ignored.
|
||||||
|
///
|
||||||
|
/// [`send`]: #method.send
|
||||||
|
pub fn add_calls<D: Detokenize>(
|
||||||
|
&mut self,
|
||||||
|
allow_failure: bool,
|
||||||
|
calls: impl IntoIterator<Item = ContractCall<M, D>>,
|
||||||
|
) -> &mut Self {
|
||||||
|
for call in calls {
|
||||||
|
self.add_call(call, allow_failure);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Appends a `call` to the list of calls of the Multicall instance for querying the block hash
|
/// Appends a `call` to the list of calls of the Multicall instance for querying the block hash
|
||||||
/// of a given block number.
|
/// of a given block number.
|
||||||
///
|
///
|
||||||
|
@ -615,6 +636,45 @@ impl<M: Middleware> Multicall<M> {
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Queries the Ethereum blockchain using `eth_call`, but via the Multicall contract, assuming
|
||||||
|
/// that every call returns same data type.
|
||||||
|
///
|
||||||
|
/// Note: this method _does not_ send a transaction from your account.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns a [`MulticallError`] if there are any errors in the RPC call or while detokenizing
|
||||||
|
/// the tokens back to the expected return type.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// The return type must be annotated while calling this method:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// # use ethers_core::types::{U256, Address};
|
||||||
|
/// # use ethers_providers::{Provider, Http};
|
||||||
|
/// # use ethers_contract::Multicall;
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
|
/// #
|
||||||
|
/// # let client = Provider::<Http>::try_from("http://localhost:8545")?;
|
||||||
|
/// #
|
||||||
|
/// # let multicall = Multicall::new(client, None).await?;
|
||||||
|
/// // If the all Solidity function calls `returns (uint256)`:
|
||||||
|
/// let result: Vec<U256> = multicall.call().await?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn call_array<D: Detokenize>(&self) -> Result<Vec<D>, M> {
|
||||||
|
let tokens = self.call_raw().await?;
|
||||||
|
let res: std::result::Result<Vec<D>, ContractError<M>> = tokens
|
||||||
|
.into_iter()
|
||||||
|
.map(|token| D::from_tokens(vec![token]).map_err(ContractError::DetokenizationError))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(res?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Queries the Ethereum blockchain using `eth_call`, but via the Multicall contract and
|
/// Queries the Ethereum blockchain using `eth_call`, but via the Multicall contract and
|
||||||
/// without detokenization.
|
/// without detokenization.
|
||||||
///
|
///
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod eth_tests {
|
||||||
use ethers_derive_eip712::*;
|
use ethers_derive_eip712::*;
|
||||||
use ethers_providers::{Http, Middleware, PendingTransaction, Provider, StreamExt};
|
use ethers_providers::{Http, Middleware, PendingTransaction, Provider, StreamExt};
|
||||||
use ethers_signers::{LocalWallet, Signer};
|
use ethers_signers::{LocalWallet, Signer};
|
||||||
use std::{convert::TryFrom, sync::Arc, time::Duration};
|
use std::{convert::TryFrom, iter::FromIterator, sync::Arc, time::Duration};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn deploy_and_call_contract() {
|
async fn deploy_and_call_contract() {
|
||||||
|
@ -517,10 +517,26 @@ mod eth_tests {
|
||||||
.add_get_eth_balance(addrs[5], false)
|
.add_get_eth_balance(addrs[5], false)
|
||||||
.add_get_eth_balance(addrs[6], false);
|
.add_get_eth_balance(addrs[6], false);
|
||||||
|
|
||||||
|
let valid_balances = [
|
||||||
|
U256::from(10_000_000_000_000_000_000_000u128),
|
||||||
|
U256::from(10_000_000_000_000_000_000_000u128),
|
||||||
|
U256::from(10_000_000_000_000_000_000_000u128),
|
||||||
|
];
|
||||||
|
|
||||||
let balances: (U256, U256, U256) = multicall.call().await.unwrap();
|
let balances: (U256, U256, U256) = multicall.call().await.unwrap();
|
||||||
assert_eq!(balances.0, U256::from(10_000_000_000_000_000_000_000u128));
|
assert_eq!(balances.0, valid_balances[0]);
|
||||||
assert_eq!(balances.1, U256::from(10_000_000_000_000_000_000_000u128));
|
assert_eq!(balances.1, valid_balances[1]);
|
||||||
assert_eq!(balances.2, U256::from(10_000_000_000_000_000_000_000u128));
|
assert_eq!(balances.2, valid_balances[2]);
|
||||||
|
|
||||||
|
// call_array
|
||||||
|
multicall
|
||||||
|
.clear_calls()
|
||||||
|
.add_get_eth_balance(addrs[4], false)
|
||||||
|
.add_get_eth_balance(addrs[5], false)
|
||||||
|
.add_get_eth_balance(addrs[6], false);
|
||||||
|
|
||||||
|
let balances: Vec<U256> = multicall.call_array().await.unwrap();
|
||||||
|
assert_eq!(balances, Vec::from_iter(valid_balances.iter().copied()));
|
||||||
|
|
||||||
// clear multicall so we can test `call_raw` w/ >16 calls
|
// clear multicall so we can test `call_raw` w/ >16 calls
|
||||||
multicall.clear_calls();
|
multicall.clear_calls();
|
||||||
|
@ -536,10 +552,11 @@ mod eth_tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// build up a list of calls greater than the 16 max restriction
|
// build up a list of calls greater than the 16 max restriction
|
||||||
for i in 0..=16 {
|
multicall.add_calls(
|
||||||
let call = simple_contract.method::<_, String>("getValue", ()).unwrap();
|
false,
|
||||||
multicall.add_call(call, false);
|
std::iter::repeat(simple_contract.method::<_, String>("getValue", ()).unwrap())
|
||||||
}
|
.take(17), // .collect(),
|
||||||
|
);
|
||||||
|
|
||||||
// must use `call_raw` as `.calls` > 16
|
// must use `call_raw` as `.calls` > 16
|
||||||
let tokens = multicall.call_raw().await.unwrap();
|
let tokens = multicall.call_raw().await.unwrap();
|
||||||
|
|
Loading…
Reference in New Issue