Multicall aggregates results from multiple contract constant function calls.
This reduces the number of separate JSON RPC requests that need to be sent (especially useful if using remote nodes like Infura), while also providing the guarantee that all values returned are from the same block (like an atomic read) and returning the block number the values are from (giving them important context so that results from old blocks can be ignored if they're from an out-of-date node).
There are three contracts in this repository:
Multicall
: The original contract containing anaggregate
method to batch callsMulticall2
: The same as Multicall, but provides additional functions that allow calls within the batch to fail. Useful for situations where a call may fail depending on the state of the contract.Multicall3
: This is the recommended version. It's ABI is backwards compatible with Multicall and Multicall2, but it's cheaper to use (so you can fit more calls into a single request), and it adds anaggregate3
method so you can specify whether calls are allowed to fail on a per-call basis. Additionally, it's deployed on every network at the same address.
These contracts can also be used to batch on-chain transactions.
If using them for this purpose, be aware these contracts are unaudited so use them at your own risk.
Additionally, make sure you understand how msg.sender
works when calling vs. delegatecalling to the Multicall contract, as well as the risks of using msg.value
in a multicall.
To learn more about the latter, see here and here.
You can obtain the ABI for the Multicall contracts in the following ways:
- Download the ABI from the releases page
- Copy the ABI from Etherscan
- Install Foundry and run
cast interface 0xcA11bde05977b3631167028862bE2a173976CA11
- Copy the human-readable ABI below for use with ethers.js.
Multicall3 contains the following improvements over prior multicall contracts:
- Cheaper to use: fit more calls into a single request before hitting the RPC's
eth_call
gas limit. - Backwards compatible: it can be dropped in to existing code by simply changing the address.
- Uses the same, memorable deployment address on the 50+ networks it's deployed to.
If there is a network Multicall3 is not yet deployed on, please open an issue with a link to the block explorer. You can speed up the new deploy by sending funds to cover the deploy cost to the deployer account: 0x05f32B3cC3888453ff71B01135B34FF8e41263F2
Multicall3 is the recommended version for most use cases, but deployment addresses for Multicall and Multicall2 are retained below for posterity. The Multicall smart contract was originally intended to be used with Multicall.js in front-end dapps. However, that library has not been updated to work with Multicall2 and Multicall3, so it will likely only work for the original Multicall contract.
The deployed Multicall contract can be found in commit bb309a9
or earlier. After that commit, the contract was updated to a more recent Solidity version (with minimal improvements), primarily for compatibility with the test suite.
The deployed Multicall2 contract can be found in commit bb309a9
or earlier. After that commit, the contract was updated to a more recent Solidity version (with minimal improvements), primarily for compatibility with the test suite.
Multicall2 is the same as Multicall, but provides additional functions that allow calls within the batch to fail. Useful for situations where a call may fail depending on the state of the contract.
Chain | Address |
---|---|
Mainnet | 0x5ba1e12693dc8f9c48aad8770482f4739beed696 |
Kovan | 0x5ba1e12693dc8f9c48aad8770482f4739beed696 |
Rinkeby | 0x5ba1e12693dc8f9c48aad8770482f4739beed696 |
Görli | 0x5ba1e12693dc8f9c48aad8770482f4739beed696 |
Ropsten | 0x5ba1e12693dc8f9c48aad8770482f4739beed696 |
The following addresses have been submitted by external contributors and have not been vetted by Multicall maintainers.
Chain | Address |
---|---|
RSK Mainnet | 0x6c62bf5440de2cb157205b15c424bceb5c3368f5 |
RSK Testnet | 0x9e469e1fc7fb4c5d17897b68eaf1afc9df39f103 |
BSC Mainnet | 0x41263cba59eb80dc200f3e2544eda4ed6a90e76c |
BSC Testnet | 0xae11C5B5f29A6a25e955F0CB8ddCc416f522AF5C |
This repo uses Foundry for development and testing and git submodules for dependency management.
Clone the repo and run forge install
to install dependencies and forge test
to run tests.
If you don't have Foundry installed, run the command below to get foundryup
, the Foundry toolchain installer:
curl -L https://foundry.paradigm.xyz | bash
Then, in a new terminal session or after reloading your PATH
, run foundryup
to get the latest forge
and cast
binaries.
To learn more about Foundry:
- Visit the repo
- Check out the Foundry book
- Learn advanced ways to use
foundryup
in the foundryup package - Check out the awesome-foundry repo
Below is a list of some of the optimizations used by Multicall3's aggregate3
and aggregate3Value
methods:
- In for loops, array length is cached to avoid reading the length on each loop iteration
- In for loops, the counter is incremented within an
unchecked
block - In for loops, the counter is incremented with the prefix increment (
++i
) instead of a postfix increment (i++
) - All revert strings fit within a single 32 byte slot
- Function parameters use
calldata
instead ofmemory
- Instead of requiring
call.allowFailure || result.success
, we use assembly'sor()
instruction to avoid aJUMPI
andiszero()
since it's cheaper to evaluate both conditions - Methods are given a
payable
modifier which removes a check thatmsg.value == 0
when calling a method - Calldata and memory pointers are used to cache values so they are not read multiple times within a loop
- No block data (e.g. block number, hash, or timestamp) is returned by default, and is instead left up to the caller
- The value accumulator in
aggregate3Value
is within anunchecked
block
Read more about Solidity gas optimization tips:
- Generic writeup about common gas optimizations, etc. by Harikrishnan Mulackal
- Yul (and Some Solidity) Optimizations and Tricks by ControlCplusControlV
Below is the human-readable ABI.
This can be directly passed into an ethers.js Contract
or Interface
constructor.
const MULTICALL_ABI = [
// https://github.com/mds1/multicall
'function aggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes[] returnData)',
'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)',
'function aggregate3Value(tuple(address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)',
'function blockAndAggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes32 blockHash, tuple(bool success, bytes returnData)[] returnData)',
'function getBasefee() view returns (uint256 basefee)',
'function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)',
'function getBlockNumber() view returns (uint256 blockNumber)',
'function getChainId() view returns (uint256 chainid)',
'function getCurrentBlockCoinbase() view returns (address coinbase)',
'function getCurrentBlockDifficulty() view returns (uint256 difficulty)',
'function getCurrentBlockGasLimit() view returns (uint256 gaslimit)',
'function getCurrentBlockTimestamp() view returns (uint256 timestamp)',
'function getEthBalance(address addr) view returns (uint256 balance)',
'function getLastBlockHash() view returns (bytes32 blockHash)',
'function tryAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)',
'function tryBlockAndAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes32 blockHash, tuple(bool success, bytes returnData)[] returnData)',
];