Example: multicall
Example
To run this example:
- Clone the examples repository:
git clone git@github.com:alloy-rs/examples.git
- Run:
cargo run --example multicall
//! This example demonstrates how to use the [`MulticallBuilder`] to make multicalls using the
//! [`IMulticall3`] contract.
use alloy::{
primitives::{address, U256},
providers::{CallItemBuilder, Failure, Provider, ProviderBuilder},
sol,
};
use IWETH9::IWETH9Instance;
sol!(
#[allow(missing_docs)]
#[sol(rpc)]
#[derive(Debug)]
IWETH9,
"examples/abi/IWETH9.json"
);
#[tokio::main]
async fn main() -> eyre::Result<()> {
// Create a new provider
let provider = ProviderBuilder::new()
.on_anvil_with_wallet_and_config(|a| a.fork("https://eth.merkle.io"))?;
// Deploy the Multicall3 contract
// let multicall3 = deploy_multicall3(&provider).await;
// Deploy the WETH contract
// let weth = deploy_weth(&provider).await;
let weth =
IWETH9Instance::new(address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), &provider);
let alice = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8");
let bob = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
let multicall = provider
.multicall()
// Set the address of the Multicall3 contract. If unset it uses the default address from <https://github.com/mds1/multicall>: 0xcA11bde05977b3631167028862bE2a173976CA11
// .address(multicall3)
// Get the total supply of WETH on our anvil fork.
.add(weth.totalSupply())
// Get Alice's WETH balance.
.add(weth.balanceOf(alice))
// Also fetch Alice's ETH balance.
.get_eth_balance(alice);
let (init_total_supply, alice_weth, alice_eth_bal) = multicall.aggregate().await?;
println!(
"Initial total supply: {}, Alice's WETH balance: {}, Alice's ETH balance: {}",
init_total_supply._0, alice_weth._0, alice_eth_bal.balance
);
// Simulate a transfer of WETH from Alice to Bob.
let wad = U256::from(20);
// This would fail as Alice doesn't have any WETH.
let tx = CallItemBuilder::new(weth.transfer(bob, U256::from(10))).allow_failure(true);
let deposit = CallItemBuilder::new(weth.deposit()).value(wad); // Set the amount of eth that should be deposited into the contract.
let multicall = provider
.multicall()
// Bob's intial WETH balance.
.add(weth.balanceOf(bob))
// Attempted WETH transfer from Alice to Bob which would fail.
.add_call(tx.clone())
// Alices deposits ETH and mints WETH.
.add_call(deposit)
// Attempt transfer again. Succeeds!
.add_call(tx)
// Alice's WETH balance after the transfer.
.add(weth.balanceOf(alice))
// Bob's final balance.
.add(weth.balanceOf(bob));
assert_eq!(multicall.len(), 6);
// It is important to use `aggregate3_value` as we're trying to simulate calls to payable
// functions that should be sent a value, using any other multicall3 method would result in an
// error.
let (init_bob, failed_transfer, deposit, succ_transfer, alice_weth, bob_weth) =
multicall.aggregate3_value().await?;
// Since, `aggregate3_value` allows for calls to fail without reverting, it returns a tuple of
// results which contain the decoded return value in Ok(_) variant and the `Failure` type in the
// Err(_) variant.
assert!(matches!(failed_transfer.unwrap_err(), Failure { idx: 1, return_data: _ }));
let init_bob = init_bob?;
assert_eq!(init_bob._0, U256::ZERO);
assert!(deposit.is_ok());
assert!(succ_transfer.is_ok());
let alice_weth = alice_weth?;
let bob_weth = bob_weth?;
println!("Alice's WETH balance: {}, Bob's WETH balance: {}", alice_weth._0, bob_weth._0);
Ok(())
}
Find the source code on Github here.