pub struct Multicall<M> { /* private fields */ }
Available on crate feature abigen only.
Expand description

A Multicall is an abstraction for sending batched calls/transactions to the Ethereum blockchain. It stores an instance of the Multicall smart contract and the user provided list of transactions to be called or executed on chain.

Multicall can be instantiated asynchronously from the chain ID of the provided client using new or synchronously by providing a chain ID in new_with_chain. This, by default, uses MULTICALL_ADDRESS, but can be overridden by providing Some(address). A list of all the supported chains is available here.

Set the contract’s version by using version.

The block number can be provided for the call by using block.

Transactions default to EIP1559. This can be changed by using legacy.

Build on the Multicall instance by adding calls using add_call and call or broadcast them all at once by using call and send respectively.

Example

Using Multicall (version 1):

use ethers_core::{
    abi::Abi,
    types::{Address, H256, U256},
};
use ethers_contract::{Contract, Multicall, MulticallVersion};
use ethers_providers::{Middleware, Http, Provider, PendingTransaction};
use std::{convert::TryFrom, sync::Arc};

// this is a dummy address used for illustration purposes
let address = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse::<Address>()?;

// (ugly way to write the ABI inline, you can otherwise read it from a file)
let abi: Abi = serde_json::from_str(r#"[{"inputs":[{"internalType":"string","name":"value","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"string","name":"oldValue","type":"string"},{"indexed":false,"internalType":"string","name":"newValue","type":"string"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"getValue","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"value","type":"string"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"}]"#)?;

// connect to the network
let client = Provider::<Http>::try_from("https://kovan.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27")?;

// create the contract object. This will be used to construct the calls for multicall
let client = Arc::new(client);
let contract = Contract::<Provider<Http>>::new(address, abi, client.clone());

// note that these [`ContractCall`]s are futures, and need to be `.await`ed to resolve.
// But we will let `Multicall` to take care of that for us
let first_call = contract.method::<_, String>("getValue", ())?;
let second_call = contract.method::<_, Address>("lastSender", ())?;

// Since this example connects to the Kovan testnet, we need not provide an address for
// the Multicall contract and we set that to `None`. If you wish to provide the address
// for the Multicall contract, you can pass the `Some(multicall_addr)` argument.
// Construction of the `Multicall` instance follows the builder pattern:
let mut multicall = Multicall::new(client.clone(), None).await?.version(MulticallVersion::Multicall);
multicall
    .add_call(first_call, false)
    .add_call(second_call, false);

// `await`ing on the `call` method lets us fetch the return values of both the above calls
// in one single RPC call
let _return_data: (String, Address) = multicall.call().await?;

// using Multicall2 (version 2) or Multicall3 (version 3) differs when parsing `.call()` results
multicall = multicall.version(MulticallVersion::Multicall3);

// each call returns the results in a tuple, with the success status as the first element
let _return_data: ((bool, String), (bool, Address)) = multicall.call().await?;

// the same `Multicall` instance can be re-used to do a different batch of transactions.
// Say we wish to broadcast (send) a couple of transactions via the Multicall contract.
let first_broadcast = contract.method::<_, H256>("setValue", "some value".to_owned())?;
let second_broadcast = contract.method::<_, H256>("setValue", "new value".to_owned())?;
multicall
    .clear_calls()
    .add_call(first_broadcast, false)
    .add_call(second_broadcast, false);

// `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?;

// you can also query ETH balances of multiple addresses
let address_1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".parse::<Address>()?;
let address_2 = "ffffffffffffffffffffffffffffffffffffffff".parse::<Address>()?;

// using version 1
multicall = multicall.version(MulticallVersion::Multicall);
multicall
    .clear_calls()
    .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()
    .add_get_eth_balance(address_1, false)
    .add_get_eth_balance(address_2, false);
let _balances: ((bool, U256), (bool, U256)) = multicall.call().await?;

Implementations§

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 MULTICALL_ADDRESS.

Errors

Returns a MulticallError if the provider returns an error while getting network_version.

Panics

If a None address is provided and the client’s network is not supported.

Creates a new Multicall instance synchronously from the provided client and address or chain ID. Uses the default multicall address if no address is provided.

Errors

Returns a MulticallError if the provided chain_id is not in the supported networks.

Panics

If neither an address or chain_id are provided. Since this is not an async function, it will not be able to query net_version to check if it is supported by the default multicall address. Use new(client, None).await instead.

Changes which functions to use when making the contract call. The default is 3. Version differences (adapted from here):

  • Multicall (v1): This is the recommended version for simple calls. The original contract containing an aggregate method to batch calls. Each call returns only the return data and none are allowed to fail.

  • Multicall2 (v2): The same as Multicall, but provides additional methods that allow either all or no calls within the batch to fail. Included for backward compatibility. Use v3 to allow failure on a per-call basis.

  • Multicall3 (v3): This is the recommended version for allowing failing calls. It’s cheaper to use (so you can fit more calls into a single request), and it adds an aggregate3 method so you can specify whether calls are allowed to fail on a per-call basis.

Note: all these versions are available in the same contract address (MULTICALL_ADDRESS) so changing version just changes the methods used, not the contract address.

Makes a legacy transaction instead of an EIP-1559 one.

Sets the block field of the Multicall aggregate call.

Appends a call 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.

Appends a call to the list of calls of the Multicall instance for querying the block hash of a given block number.

Note: this call will return 0 if block_number is not one of the most recent 256 blocks. (Reference)

Appends a call to the list of calls of the Multicall instance for querying the current block number.

Appends a call to the list of calls of the Multicall instance for querying the current block coinbase address.

Appends a call to the list of calls of the Multicall instance for querying the current block difficulty.

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)

Appends a call to the list of calls of the Multicall instance for querying the current block gas limit.

Appends a call to the list of calls of the Multicall instance for querying the current block timestamp.

Appends a call to the list of calls of 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 last block hash.

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.

Appends a call to the list of calls of the Multicall instance for querying the chain id.

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.

Examples
let mut multicall = Multicall::new(client, None).await?;
multicall
    .add_call(broadcast_1, false)
    .add_call(broadcast_2, false);

let _tx_hash = multicall.send().await?;

multicall
    .clear_calls()
    .add_call(call_1, false)
    .add_call(call_2, false);
// Version 1:
let return_data: (String, Address) = multicall.call().await?;
// Version 2 and above (each call returns also the success status as the first element):
let return_data: ((bool, String), (bool, Address)) = multicall.call().await?;

Queries the Ethereum blockchain using eth_call, but via the Multicall contract.

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.

Panics

If more than the maximum number of supported calls are added (16). The maximum limit is constrained due to tokenization/detokenization support for tuples.

Examples

The return type must be annotated while calling this method:

// If the Solidity function calls has the following return types:
// 1. `returns (uint256)`
// 2. `returns (string, address)`
// 3. `returns (bool)`
// Version 1:
let result: (U256, (String, Address), bool) = multicall.call().await?;
// Version 2 and above (each call returns also the success status as the first element):
let result: ((bool, U256), (bool, (String, Address)), (bool, bool)) = multicall.call().await?;

Queries the Ethereum blockchain using eth_call, but via the Multicall contract and without detokenization.

Errors

Returns a MulticallError if there are any errors in the RPC call.

Examples
// The consumer of the API is responsible for detokenizing the results
// as the results will be a Vec<Token>
let tokens = multicall.call_raw().await?;

Note: this method does not send a transaction from your account

Signs and broadcasts a batch of transactions by using the Multicall contract as proxy, returning the transaction hash once the transaction confirms.

Note: this method will broadcast a transaction from an account, meaning it must have sufficient funds for gas and transaction value.

Errors

Returns a MulticallError if there are any errors in the RPC call.

Examples
let tx_hash = multicall.send().await?;

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Should always be Self
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more