Skip to content

Commit 6232d5a

Browse files
authored
Merge pull request #2291 from xBalbinus/multicall3
feat: document multicall 3
2 parents 5477cf9 + 0703a76 commit 6232d5a

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
description: >-
3+
Multicall allows you to aggregate multiple contract reads into a single JSON-RPC request,
4+
and execute multiple state-changing calls in a single transaction on the FVM.
5+
---
6+
7+
# Multicall3
8+
9+
[Multicall3](https://www.multicall3.com/) is a powerful tool that offers batch contract calls to smart contracts on the Filecoin Virtual Machine (FVM).
10+
11+
Multicall3 is deployed on over 100 chains at `0xcA11bde05977b3631167028862bE2a173976CA11`.
12+
A sortable, searchable list of all chains it's deployed on can be found [here](https://multicall3.com/deployments).
13+
14+
The [multicall3 ABI](https://multicall3.com/abi) can be downloaded or copied to the clipboard in various formats, including:
15+
16+
- Solidity interface.
17+
- JSON ABI, prettified.
18+
- JSON ABI, minified.
19+
- [ethers.js](https://docs.ethers.org/v5/) human readable ABI.
20+
- [viem](https://viem.sh/) human readable ABI.
21+
22+
Alternatively, you can:
23+
24+
- Download the ABI from the [releases](https://github.com/mds1/multicall/releases) page.
25+
- Copy the ABI from [Etherscan](https://etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code).
26+
- Install [Foundry](https://github.com/gakonst/foundry/) and run `cast interface 0xcA11bde05977b3631167028862bE2a173976CA11`.
27+
28+
## Contract address
29+
30+
Multicall has the same, precomputed address for all of the networks it is deployed on.
31+
| Name | Address | Mainnet | Calibration |
32+
| ---------------- | -------------------------------------------- | ------- | ----------- |
33+
| [Multicall - Mainnet](https://filfox.info/en/address/0xcA11bde05977b3631167028862bE2a173976CA11?t=3) | `0xcA11bde05977b3631167028862bE2a173976CA11` | ✔️ ||
34+
| [Multicall - Calibration](https://calibration.filscan.io/en/address/0xcA11bde05977b3631167028862bE2a173976CA11/) | `0xcA11bde05977b3631167028862bE2a173976CA11` || ✔️ |
35+
36+
## Usage
37+
To use Multicall3 to send batch contract read/write to your smart contract, you will need to:
38+
39+
1. Obtain the Multicall3 contract address for the network you're using (Filecoin mainnet or Calibration testnet).
40+
2. Get the Multicall3 ABI, which can be downloaded or copied from various sources mentioned above.
41+
3. Create an instance of the Multicall3 contract using a web3 library like ethers.js or viem.
42+
4. Prepare your batch calls, including the target contract addresses, function selectors, and input data.
43+
5. Use the appropriate Multicall3 method (e.g., `aggregate3` for multiple calls) to execute your batch operations.
44+
6. Process the returned data from the Multicall3 contract.
45+
46+
The steps above differ slightly for integrations using smart contracts, where steps 2 and 3 are replaced with:
47+
48+
2. Import the Multicall3 interface in your smart contract.
49+
3. Create a function that interacts with the Multicall3 contract using the imported interface.
50+
51+
Many libraries and tools such as [ethers-rs](https://docs.rs/ethers/latest/ethers/), [viem](https://viem.sh/), and [ape](https://apeworx.io/) have native Multicall3 integration which can be used in your projects directly. To learn how to use Multicall3 with these tools, check out [Multicall3 examples folder](https://github.com/mds1/multicall/blob/main/examples)
52+
53+
### Batching Contract Reads
54+
Batching contract reads, one of the most common use cases, allows a single `eth_call` JSON RPC request to return the results of multiple contract function calls. It has many benefits:
55+
56+
1. **Reduced JSON RPC Requests**: Multicall reduces the number of separate JSON RPC requests that need to be sent. This is particularly useful when using remote nodes like Infura. By aggregating multiple contract reads into a single JSON-RPC request, Multicall (1) reduces RPC usage and therefore costs, and (2) reduces the number of round trips between the client and the node, which can significantly improve performance.
57+
58+
2. **Consistent Data from the Same Block**: Multicall guarantees that all values returned are from the same block. This ensures data consistency and reliability, as all the read operations are performed on the same state of the blockchain.
59+
60+
3. **Detection of Stale Data**: Multicall enables the block number or timestamp to be returned with the read data. This feature helps in detecting stale data, as developers can compare the block number or timestamp with the current state of the blockchain to ensure the data is up-to-date.
61+
62+
63+
When directly interacting with the Multicall3 contract to batch calls, you'll typically use the `aggregate3` method. This method allows you to execute multiple contract calls in a single transaction. Here's an explanation of how it works, along with examples:
64+
65+
1. Solidity Implementation:
66+
The `aggregate3` method is implemented in the Multicall3 contract like this:
67+
68+
```solidity
69+
function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData) {
70+
uint256 length = calls.length;
71+
returnData = new Result[](length);
72+
for (uint256 i = 0; i < length;) {
73+
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
74+
if (calls[i].allowFailure) {
75+
returnData[i] = Result(success, ret);
76+
} else {
77+
require(success, "Multicall3: call failed");
78+
returnData[i] = Result(true, ret);
79+
}
80+
unchecked { ++i; }
81+
}
82+
}
83+
```
84+
85+
2. Example of sending multicalls to this smart contract:
86+
Here's an example using ethers.js to interact with the Multicall3 contract:
87+
88+
```javascript
89+
const { ethers } = require("ethers");
90+
91+
const provider = new ethers.providers.JsonRpcProvider("https://api.node.glif.io/rpc/v1");
92+
const multicallAddress = "0xcA11bde05977b3631167028862bE2a173976CA11";
93+
const multicallAbi = [/* Multicall3 ABI */];
94+
const multicall = new ethers.Contract(multicallAddress, multicallAbi, provider);
95+
96+
// Example: Batch balance checks for multiple addresses
97+
async function batchBalanceChecks(addresses) {
98+
const calls = addresses.map(address => ({
99+
target: "0x...", // ERC20 token address
100+
allowFailure: false,
101+
callData: ethers.utils.id("balanceOf(address)").slice(0, 10) +
102+
ethers.utils.defaultAbiCoder.encode(["address"], [address]).slice(2)
103+
}));
104+
105+
const results = await multicall.aggregate3(calls);
106+
return results.map(result => ethers.utils.defaultAbiCoder.decode(["uint256"], result.returnData)[0]);
107+
}
108+
109+
batchBalanceChecks(["0x123...", "0x456...", "0x789..."]).then(console.log);
110+
```
111+
112+
This example demonstrates how to use Multicall3 to batch multiple `balanceOf` calls for an ERC20 token in a single transaction, significantly reducing the number of separate RPC calls needed.
113+
114+
### Batch Contract Writes
115+
116+
117+
> :warning: Multicall3, while unaudited, can be safely used for batching on-chain writes when used correctly. As a stateless contract, it should never hold funds after a transaction ends, and users should never approve it to spend tokens.
118+
119+
120+
When using Multicall3, it's crucial to understand two key aspects: the behavior of `msg.sender` in calls versus delegatecalls, and the risks associated with `msg.value` in multicalls.
121+
122+
In FVM, there are two types of accounts: Externally Owned Accounts (EOAs) controlled by private keys, and Contract Accounts controlled by code. The `msg.sender` value during contract execution depends on whether a CALL or DELEGATECALL opcode is used. CALL changes the execution context, while DELEGATECALL preserves it.
123+
124+
For EOAs, which can only use CALL, Multicall3's address becomes the `msg.sender` for subsequent calls. This limits its usefulness from EOAs to scenarios where **`msg.sender` is irrelevant**. However, contract wallets or other contracts can use either CALL or DELEGATECALL, with the latter preserving the original `msg.sender`.
125+
126+
The handling of `msg.value` in multicalls requires caution. Since `msg.value` doesn't change with delegatecalls, relying on it within a multicall can lead to security vulnerabilities. To learn more about this, see [here](https://github.com/runtimeverification/verified-smart-contracts/wiki/List-of-Security-Vulnerabilities#payable-multicall) and [here](https://samczsun.com/two-rights-might-make-a-wrong/).

0 commit comments

Comments
 (0)