├── .github ├── ISSUE_TEMPLATE │ └── config.yml └── workflows │ └── tests.yml ├── .gitmodules ├── funding.json ├── examples ├── rust │ ├── rustfmt.toml │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── typescript │ ├── package.json │ ├── README.md │ ├── viem.ts │ ├── ethers.ts │ ├── constants.ts │ └── pnpm-lock.yaml ├── python │ ├── README.md │ ├── requirements.txt │ └── main.py ├── solidity │ └── README.md └── README.md ├── .gitignore ├── src ├── test │ ├── mocks │ │ ├── EtherSink.sol │ │ └── MockCallee.sol │ ├── Multicall.t.sol │ ├── Multicall2.t.sol │ └── Multicall3.t.sol ├── interfaces │ ├── IMulticall.sol │ ├── IMulticall2.sol │ └── IMulticall3.sol ├── Multicall.sol ├── Multicall2.sol └── Multicall3.sol ├── foundry.toml ├── LICENSE ├── .gas-snapshot ├── README.md └── deployments.json /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | branch = v1 3 | path = lib/forge-std 4 | url = https://github.com/foundry-rs/forge-std 5 | -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0xf1d9b05abe0f41beab3e5f4b829afd9a7ba3679829d4be72c45e0d340f7bffaa" 4 | } 5 | } -------------------------------------------------------------------------------- /examples/rust/rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 100 2 | imports_granularity = "Crate" 3 | max_width = 100 4 | trailing_semicolon = false 5 | use_field_init_shorthand = true 6 | use_small_heuristics = "Max" 7 | wrap_comments = true 8 | -------------------------------------------------------------------------------- /examples/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "rust" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | ethers = "2.0.4" 8 | serde_json = "1.0.96" 9 | tokio = { version = "1.28.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | examples 2 | out 3 | cache 4 | node_modules 5 | target 6 | output 7 | venv 8 | 9 | .vscode 10 | 11 | # Ignore environment variables incase someone accidentally sets and commits 12 | .env 13 | .env.prod 14 | 15 | old.gas-snapshot 16 | -------------------------------------------------------------------------------- /examples/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multicall3-typescript-examples", 3 | "dependencies": { 4 | "ethers": "^6.7.1", 5 | "viem": "^1.9.2" 6 | }, 7 | "devDependencies": { 8 | "@types/node": "^20.1.3", 9 | "ts-node": "^10.9.1", 10 | "typescript": "^5.0.3" 11 | }, 12 | "volta": { 13 | "node": "18.15.0", 14 | "yarn": "1.22.19" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/mocks/EtherSink.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | /// @title EtherSink 5 | /// @notice Receives Ether, that's about it \( o_o )/ 6 | /// @author andreas@nascent.xyz 7 | contract EtherSink { 8 | 9 | /// >>>>>>>>>>>>>>>>>>>>>> ACCEPT CALLS <<<<<<<<<<<<<<<<<<<<<<< /// 10 | 11 | /// @notice Allows the test to receive eth via low level calls 12 | receive() external payable {} 13 | } -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - 'v*.*.*' 9 | pull_request: 10 | 11 | jobs: 12 | tests: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Install Foundry 19 | uses: foundry-rs/foundry-toolchain@v1 20 | with: 21 | version: nightly 22 | 23 | - name: Run Tests 24 | run: FOUNDRY_PROFILE=ci forge test 25 | -------------------------------------------------------------------------------- /examples/rust/README.md: -------------------------------------------------------------------------------- 1 | # Multicall3 Rust Examples 2 | 3 | The Rust Multicall3 example uses [ethers-rs](https://github.com/gakonst/ethers-rs). 4 | 5 | ethers-rs has native Multicall3 support which we leverage here. 6 | This example batches various calls to the DAI contract with an ETH balance lookup and coinbase address lookup. 7 | It then formats and prints the results. 8 | 9 | Run `cargo run` to run the example. 10 | Documentation and examples for the `ethers-rs` Multicall3 functionality can be found in the `ethers-rs` [docs](https://docs.rs/ethers/2.0.4/ethers/contract/struct.Multicall.html). 11 | 12 | See the code and comments in [src/main.rs](./src/main.rs) for more information. 13 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | # Foundry Configuration File 2 | # Default definitions: https://github.com/gakonst/foundry/blob/b7917fa8491aedda4dd6db53fbb206ea233cd531/config/src/lib.rs#L782 3 | # See more config options at: https://github.com/gakonst/foundry/tree/master/config 4 | 5 | # The Default Profile 6 | [profile.default] 7 | # Sets the concrete solc version to use 8 | # This overrides the `auto_detect_solc` value 9 | solc = "0.8.12" 10 | auto_detect_solc = false 11 | # Increase optimizer_runs since we expect multicalls to be highly queried 12 | optimizer = true 13 | optimizer_runs = 10_000_000 14 | # Fuzz more than the default 256 15 | fuzz = { runs = 1_000 } 16 | 17 | # Extreme Fuzzing CI Profile :P 18 | [profile.ci] 19 | fuzz = { runs = 100_000 } 20 | -------------------------------------------------------------------------------- /examples/python/README.md: -------------------------------------------------------------------------------- 1 | # Multicall3 Python Example 2 | 3 | The Python Multicall3 example uses [Ape](https://github.com/ApeWorX/ape). 4 | 5 | Ape has native Multicall3 support which we leverage here. 6 | This example fetches the balances of 3 tokens for 3 users, along with token symbol and decimals. 7 | It then formats and prints the results. 8 | 9 | To run the example: 10 | 11 | 1. Set an environment variable called `WEB3_INFURA_PROJECT_ID` that contains your Infura project ID. 12 | 2. Create a virtual environment with `python3 -m venv venv` 13 | 3. Activate the virtual environment with `source venv/bin/activate` 14 | 4. Install the dependencies with `pip install -r requirements.txt` 15 | 5. Run `python3 main.py` 16 | 17 | See the code and comments in `main.py` for more information. 18 | -------------------------------------------------------------------------------- /src/test/mocks/MockCallee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | /// @title MockCallee 5 | /// @notice Receives calls from the Multicaller 6 | /// @author andreas@nascent.xyz 7 | contract MockCallee { 8 | 9 | /// @notice Failure 10 | error Unsuccessful(); 11 | 12 | /// @notice Returns the block hash for the given block number 13 | /// @param blockNumber The block number 14 | /// @return blockHash The 32 byte block hash 15 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { 16 | blockHash = blockhash(blockNumber); 17 | } 18 | 19 | /// @notice Reverts o______O 20 | function thisMethodReverts() public pure { 21 | revert Unsuccessful(); 22 | } 23 | 24 | /// @notice Accepts a value 25 | function sendBackValue(address target) public payable { 26 | (bool ok, ) = target.call{value: msg.value}(""); 27 | if (!ok) revert Unsuccessful(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/solidity/README.md: -------------------------------------------------------------------------------- 1 | # Multicall3 Solidity Examples 2 | 3 | Since this examples folder lives in the Multicall3 repo, the Solidity examples will simply link out to other examples to avoid having a forge project within this forge repo. 4 | 5 | For Solidity examples, see: 6 | 7 | - The Multicall3 tests themselves, found in [`Multicall3.t.sol`](../../src/test/Multicall3.t.sol). In particular the `testAggregate3` test shows how to decode responses. 8 | - The [`getTokenBalances`](https://github.com/foundry-rs/forge-std/blob/73d44ec7d124e3831bc5f832267889ffb6f9bc3f/src/StdUtils.sol#L143-L171) helper method in [forge-std](https://github.com/foundry-rs/forge-std) and it's [tests](https://github.com/foundry-rs/forge-std/blob/73d44ec7d124e3831bc5f832267889ffb6f9bc3f/test/StdUtils.t.sol#L231-L297). 9 | 10 | The Solidity examples are more brief and straightforward than other languages' examples, because using Multicall3 in Solidity is very similar to making other arbitrary external calls and encoding/decoding the results. 11 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Multicall3 Examples 2 | 3 | Each folder in this directory contains a self-contained example of how to use Multicall3 for the given language or framework. 4 | All examples assume an environment variable called `MAINNET_RPC_URL` is set to a valid Ethereum mainnet RPC URL. 5 | 6 | The examples are intentionally _not_ identical for each language, to show a wider set of use cases (but there are similarities and overlap). 7 | For example, the TypeScript `ethers.js` examples shows ENS resolution using multicall, whereas the Rust `ethers-rs` example fetches token and ETH balances for a given address. 8 | As a result, you may find it useful to look at the examples for multiple languages even if you only plan to use one of them. 9 | All examples are well commented to try making them understandable even if you are not familiar with the language. 10 | 11 | The examples are also not intended to be production-ready code, but rather to show how to use Multicall3 in a simple way. PRs for additional or improved examples are welcome! 12 | -------------------------------------------------------------------------------- /src/interfaces/IMulticall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IMulticall { 7 | struct Call { 8 | address target; 9 | bytes callData; 10 | } 11 | 12 | function aggregate(Call[] calldata calls) 13 | external 14 | returns (uint256 blockNumber, bytes[] memory returnData); 15 | 16 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 17 | 18 | function getCurrentBlockCoinbase() external view returns (address coinbase); 19 | 20 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 21 | 22 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 23 | 24 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 25 | 26 | function getEthBalance(address addr) external view returns (uint256 balance); 27 | 28 | function getLastBlockHash() external view returns (bytes32 blockHash); 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Matt Solomon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/typescript/README.md: -------------------------------------------------------------------------------- 1 | # Multicall3 Typescript Examples 2 | 3 | Multicall3 examples using [viem](#viem) and [ethers.js](#ethersjs). 4 | 5 | ## viem 6 | 7 | [viem](https://viem.sh/) has native Multicall3 support which we leverage here. 8 | This example shows how to compute Uniswap V3 pool addresses and look up their token balances. 9 | 10 | To run the example: 11 | 12 | - Install dependencies with `pnpm install` 13 | - Run `pnpm ts-node viem.ts` 14 | 15 | You can replace `pnpm` with the node package manager of your choice. 16 | 17 | See the code and comments in `viem.ts` for more information. 18 | 19 | ## ethers.js 20 | 21 | [ethers.js](https://docs.ethers.org/v6/) does not have native Multicall3 support so this example shows how to interact with the contract directly. 22 | This example shows how to reverse resolve ENS names for a list of addresses. 23 | It uses the `aggregate3` method to support reverting calls. 24 | 25 | To run the example: 26 | 27 | - Install dependencies with `pnpm install` 28 | - Run `pnpm ts-node ethers.ts` 29 | 30 | You can replace `pnpm` with the node package manager of your choice. 31 | 32 | See the code and comments in `ethers.ts` for more information. 33 | -------------------------------------------------------------------------------- /src/interfaces/IMulticall2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0 <0.9.0; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | interface IMulticall2 { 7 | struct Call { 8 | address target; 9 | bytes callData; 10 | } 11 | 12 | struct Result { 13 | bool success; 14 | bytes returnData; 15 | } 16 | 17 | function aggregate(Call[] calldata calls) external returns (uint256 blockNumber, bytes[] memory returnData); 18 | 19 | function blockAndAggregate(Call[] calldata calls) 20 | external 21 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 22 | 23 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 24 | 25 | function getBlockNumber() external view returns (uint256 blockNumber); 26 | 27 | function getCurrentBlockCoinbase() external view returns (address coinbase); 28 | 29 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 30 | 31 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 32 | 33 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 34 | 35 | function getEthBalance(address addr) external view returns (uint256 balance); 36 | 37 | function getLastBlockHash() external view returns (bytes32 blockHash); 38 | 39 | function tryAggregate(bool requireSuccess, Call[] calldata calls) external returns (Result[] memory returnData); 40 | 41 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) 42 | external 43 | returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/Multicall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | /// @title Multicall - Aggregate results from multiple read-only function calls 5 | /// @author Michael Elliot 6 | /// @author Joshua Levine 7 | /// @author Nick Johnson 8 | 9 | contract Multicall { 10 | struct Call { 11 | address target; 12 | bytes callData; 13 | } 14 | 15 | function aggregate(Call[] calldata calls) public returns (uint256 blockNumber, bytes[] memory returnData) { 16 | blockNumber = block.number; 17 | returnData = new bytes[](calls.length); 18 | for (uint256 i = 0; i < calls.length; i++) { 19 | (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); 20 | require(success); 21 | returnData[i] = ret; 22 | } 23 | } 24 | 25 | // Helper functions 26 | function getEthBalance(address addr) public view returns (uint256 balance) { 27 | balance = addr.balance; 28 | } 29 | 30 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { 31 | blockHash = blockhash(blockNumber); 32 | } 33 | 34 | function getLastBlockHash() public view returns (bytes32 blockHash) { 35 | blockHash = blockhash(block.number - 1); 36 | } 37 | 38 | function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { 39 | timestamp = block.timestamp; 40 | } 41 | 42 | function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { 43 | difficulty = block.difficulty; 44 | } 45 | 46 | function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { 47 | gaslimit = block.gaslimit; 48 | } 49 | 50 | function getCurrentBlockCoinbase() public view returns (address coinbase) { 51 | coinbase = block.coinbase; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/interfaces/IMulticall3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.12 <0.9.0; 3 | 4 | interface IMulticall3 { 5 | struct Call { 6 | address target; 7 | bytes callData; 8 | } 9 | 10 | struct Call3 { 11 | address target; 12 | bool allowFailure; 13 | bytes callData; 14 | } 15 | 16 | struct Call3Value { 17 | address target; 18 | bool allowFailure; 19 | uint256 value; 20 | bytes callData; 21 | } 22 | 23 | struct Result { 24 | bool success; 25 | bytes returnData; 26 | } 27 | 28 | function aggregate(Call[] calldata calls) external payable returns (uint256 blockNumber, bytes[] memory returnData); 29 | 30 | function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData); 31 | 32 | function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData); 33 | 34 | function blockAndAggregate( 35 | Call[] calldata calls 36 | ) external payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 37 | 38 | function getBasefee() external view returns (uint256 basefee); 39 | 40 | function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash); 41 | 42 | function getBlockNumber() external view returns (uint256 blockNumber); 43 | 44 | function getChainId() external view returns (uint256 chainid); 45 | 46 | function getCurrentBlockCoinbase() external view returns (address coinbase); 47 | 48 | function getCurrentBlockDifficulty() external view returns (uint256 difficulty); 49 | 50 | function getCurrentBlockGasLimit() external view returns (uint256 gaslimit); 51 | 52 | function getCurrentBlockTimestamp() external view returns (uint256 timestamp); 53 | 54 | function getEthBalance(address addr) external view returns (uint256 balance); 55 | 56 | function getLastBlockHash() external view returns (bytes32 blockHash); 57 | 58 | function tryAggregate( 59 | bool requireSuccess, 60 | Call[] calldata calls 61 | ) external payable returns (Result[] memory returnData); 62 | 63 | function tryBlockAndAggregate( 64 | bool requireSuccess, 65 | Call[] calldata calls 66 | ) external payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData); 67 | } 68 | -------------------------------------------------------------------------------- /.gas-snapshot: -------------------------------------------------------------------------------- 1 | Multicall2Test:testAggregation() (gas: 15033) 2 | Multicall2Test:testBlockAndAggregateUnsuccessful() (gas: 19327) 3 | Multicall2Test:testGetBlockHash(uint256) (runs: 1000, μ: 5696, ~: 5696) 4 | Multicall2Test:testGetBlockNumber() (gas: 5442) 5 | Multicall2Test:testGetCurrentBlockCoinbase() (gas: 5579) 6 | Multicall2Test:testGetCurrentBlockDifficulty() (gas: 5463) 7 | Multicall2Test:testGetCurrentBlockGasLimit() (gas: 5485) 8 | Multicall2Test:testGetCurrentBlockTimestamp() (gas: 5432) 9 | Multicall2Test:testGetEthBalance(address) (runs: 1000, μ: 8424, ~: 8444) 10 | Multicall2Test:testGetLastBlockHash() (gas: 5693) 11 | Multicall2Test:testTryAggregate() (gas: 19395) 12 | Multicall2Test:testTryAggregateUnsuccessful() (gas: 19227) 13 | Multicall2Test:testTryBlockAndAggregate() (gas: 19676) 14 | Multicall2Test:testTryBlockAndAggregateUnsuccessful() (gas: 19330) 15 | Multicall2Test:testUnsuccessfulAggregation() (gas: 18858) 16 | Multicall3Test:testAggregate3() (gas: 23338) 17 | Multicall3Test:testAggregate3Unsuccessful() (gas: 18821) 18 | Multicall3Test:testAggregate3Value() (gas: 47754) 19 | Multicall3Test:testAggregate3ValueUnsuccessful() (gas: 79594) 20 | Multicall3Test:testAggregation() (gas: 14785) 21 | Multicall3Test:testBlockAndAggregateUnsuccessful() (gas: 18879) 22 | Multicall3Test:testGetBasefee() (gas: 5463) 23 | Multicall3Test:testGetBlockHash(uint256) (runs: 1000, μ: 5696, ~: 5696) 24 | Multicall3Test:testGetBlockNumber() (gas: 5553) 25 | Multicall3Test:testGetChainId() (gas: 5485) 26 | Multicall3Test:testGetCurrentBlockCoinbase() (gas: 5534) 27 | Multicall3Test:testGetCurrentBlockDifficulty() (gas: 5463) 28 | Multicall3Test:testGetCurrentBlockGasLimit() (gas: 5507) 29 | Multicall3Test:testGetCurrentBlockTimestamp() (gas: 5410) 30 | Multicall3Test:testGetEthBalance(address) (runs: 1000, μ: 8387, ~: 8400) 31 | Multicall3Test:testGetLastBlockHash() (gas: 5678) 32 | Multicall3Test:testTryAggregate() (gas: 18859) 33 | Multicall3Test:testTryAggregateUnsuccessful() (gas: 18824) 34 | Multicall3Test:testTryBlockAndAggregate() (gas: 19163) 35 | Multicall3Test:testTryBlockAndAggregateUnsuccessful() (gas: 18949) 36 | Multicall3Test:testUnsuccessfulAggregation() (gas: 18593) 37 | MulticallTest:testAggregation() (gas: 14824) 38 | MulticallTest:testGetBlockHash(uint256) (runs: 1000, μ: 5674, ~: 5674) 39 | MulticallTest:testGetCurrentBlockCoinbase() (gas: 5534) 40 | MulticallTest:testGetCurrentBlockDifficulty() (gas: 5418) 41 | MulticallTest:testGetCurrentBlockGasLimit() (gas: 5440) 42 | MulticallTest:testGetCurrentBlockTimestamp() (gas: 5387) 43 | MulticallTest:testGetEthBalance(address) (runs: 1000, μ: 8409, ~: 8422) 44 | MulticallTest:testGetLastBlockHash() (gas: 5648) 45 | MulticallTest:testUnsuccessfulAggregation() (gas: 18612) -------------------------------------------------------------------------------- /src/test/Multicall.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {Multicall} from "../Multicall.sol"; 6 | import {MockCallee} from "./mocks/MockCallee.sol"; 7 | 8 | contract MulticallTest is Test { 9 | Multicall multicall; 10 | MockCallee callee; 11 | 12 | /// @notice Setups up the testing suite 13 | function setUp() public { 14 | multicall = new Multicall(); 15 | callee = new MockCallee(); 16 | } 17 | 18 | /// >>>>>>>>>>>>>>>>>>>> AGGREGATION TESTS <<<<<<<<<<<<<<<<<<<< /// 19 | 20 | function testAggregation() public { 21 | // Test successful call 22 | Multicall.Call[] memory calls = new Multicall.Call[](1); 23 | calls[0] = Multicall.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 24 | (uint256 blockNumber, bytes[] memory returnData) = multicall.aggregate(calls); 25 | assertEq(blockNumber, block.number); 26 | assertEq(keccak256(returnData[0]), keccak256(abi.encodePacked(blockhash(block.number)))); 27 | } 28 | 29 | function testUnsuccessfulAggregation() public { 30 | // Test unexpected revert 31 | Multicall.Call[] memory calls = new Multicall.Call[](2); 32 | calls[0] = Multicall.Call( 33 | address(callee), 34 | abi.encodeWithSignature("getBlockHash(uint256)", block.number) 35 | ); 36 | calls[1] = Multicall.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()") 37 | ); 38 | vm.expectRevert(bytes("")); 39 | multicall.aggregate(calls); 40 | } 41 | 42 | /// >>>>>>>>>>>>>>>>>>>>>> HELPER TESTS <<<<<<<<<<<<<<<<<<<<<<< /// 43 | 44 | function testGetEthBalance(address addr) public { 45 | assertEq(addr.balance, multicall.getEthBalance(addr)); 46 | } 47 | 48 | function testGetBlockHash(uint256 blockNumber) public { 49 | assertEq(blockhash(blockNumber), multicall.getBlockHash(blockNumber)); 50 | } 51 | 52 | function testGetLastBlockHash() public { 53 | // Prevent arithmetic underflow on the genesis block 54 | if (block.number == 0) return; 55 | assertEq(blockhash(block.number - 1), multicall.getLastBlockHash()); 56 | } 57 | 58 | function testGetCurrentBlockTimestamp() public { 59 | assertEq(block.timestamp, multicall.getCurrentBlockTimestamp()); 60 | } 61 | 62 | function testGetCurrentBlockDifficulty() public { 63 | assertEq(block.difficulty, multicall.getCurrentBlockDifficulty()); 64 | } 65 | 66 | function testGetCurrentBlockGasLimit() public { 67 | assertEq(block.gaslimit, multicall.getCurrentBlockGasLimit()); 68 | } 69 | 70 | function testGetCurrentBlockCoinbase() public { 71 | assertEq(block.coinbase, multicall.getCurrentBlockCoinbase()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/python/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.8.4 2 | aiosignal==1.3.1 3 | ape-alchemy==0.6.1 4 | ape-ens==0.6.0 5 | ape-etherscan==0.6.4 6 | ape-foundry==0.6.7 7 | ape-hardhat==0.6.4 8 | ape-infura==0.6.1 9 | ape-solidity==0.6.3 10 | ape-template==0.6.0 11 | ape-tokens==0.6.0 12 | ape-vyper==0.6.5 13 | appnope==0.1.3 14 | arrow==1.2.3 15 | asttokens==2.2.1 16 | async-timeout==4.0.2 17 | attrs==23.1.0 18 | backcall==0.2.0 19 | base58==1.0.3 20 | binaryornot==0.4.4 21 | bitarray==2.7.3 22 | cached-property==1.5.2 23 | certifi==2022.12.7 24 | cffi==1.15.1 25 | chardet==5.1.0 26 | charset-normalizer==3.1.0 27 | click==8.1.3 28 | commonmark==0.9.1 29 | cookiecutter==2.1.1 30 | cryptography==40.0.2 31 | cytoolz==0.12.1 32 | dataclassy==0.11.1 33 | decorator==5.1.1 34 | Deprecated==1.2.13 35 | eip712==0.2.1 36 | eth-abi==4.0.0 37 | eth-account==0.8.0 38 | eth-ape==0.6.8 39 | eth-bloom==2.0.0 40 | eth-hash==0.5.1 41 | eth-keyfile==0.6.1 42 | eth-keys==0.4.0 43 | eth-rlp==0.3.0 44 | eth-tester==0.8.0b3 45 | eth-typing==3.3.0 46 | eth-utils==2.1.0 47 | ethpm-types==0.4.5 48 | evm-trace==0.1.0a18 49 | exceptiongroup==1.1.1 50 | executing==1.2.0 51 | frozenlist==1.3.3 52 | hexbytes==0.3.0 53 | idna==3.4 54 | ijson==3.2.0.post0 55 | importlib-metadata==6.6.0 56 | iniconfig==2.0.0 57 | ipython==8.13.2 58 | jedi==0.18.2 59 | Jinja2==3.1.2 60 | jinja2-time==0.2.0 61 | jsonschema==4.17.3 62 | lru-dict==1.1.8 63 | MarkupSafe==2.1.2 64 | matplotlib-inline==0.1.6 65 | morphys==1.0 66 | msgspec==0.14.2 67 | multidict==6.0.4 68 | mypy-extensions==0.4.4 69 | numpy==1.24.3 70 | packaging==23.1 71 | pandas==1.5.3 72 | parsimonious==0.9.0 73 | parso==0.8.3 74 | pexpect==4.8.0 75 | pickleshare==0.7.5 76 | pluggy==1.0.0 77 | prompt-toolkit==3.0.38 78 | protobuf==4.22.4 79 | ptyprocess==0.7.0 80 | pure-eval==0.2.2 81 | py-cid==0.3.0 82 | py-ecc==6.0.0 83 | py-evm==0.6.1a2 84 | py-geth==3.12.0 85 | py-multibase==1.0.3 86 | py-multicodec==0.2.1 87 | py-multihash==0.2.3 88 | py-solc-x==1.1.1 89 | pycparser==2.21 90 | pycryptodome==3.17 91 | pydantic==1.10.7 92 | pyethash==0.1.27 93 | PyGithub==1.58.1 94 | Pygments==2.15.1 95 | PyJWT==2.6.0 96 | PyNaCl==1.5.0 97 | pyrsistent==0.19.3 98 | pytest==7.3.1 99 | python-baseconv==1.2.2 100 | python-dateutil==2.8.2 101 | python-slugify==8.0.1 102 | pytz==2023.3 103 | PyYAML==6.0 104 | regex==2023.5.5 105 | requests==2.30.0 106 | rich==12.6.0 107 | rlp==3.0.0 108 | safe-pysha3==1.0.4 109 | semantic-version==2.10.0 110 | six==1.16.0 111 | sortedcontainers==2.4.0 112 | SQLAlchemy==2.0.12 113 | stack-data==0.6.2 114 | text-unidecode==1.3 115 | tokenlists==0.1.2 116 | tomli==2.0.1 117 | toolz==0.12.0 118 | tqdm==4.65.0 119 | traitlets==5.9.0 120 | trie==2.1.0 121 | typing_extensions==4.5.0 122 | urllib3==1.26.15 123 | varint==1.0.2 124 | vvm==0.1.0 125 | watchdog==2.3.1 126 | wcwidth==0.2.6 127 | web3==6.3.0 128 | websockets==11.0.2 129 | wrapt==1.15.0 130 | yarl==1.9.2 131 | zipp==3.15.0 132 | -------------------------------------------------------------------------------- /examples/python/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Multicall3 example using ape: https://github.com/ApeWorX/ape 3 | 4 | Ape has native Multicall3 support which we leverage here. This example fetches the balances of 3 5 | tokens for 3 users, along with token symbol and decimals. It then formats and prints the results. 6 | 7 | To run the example: 8 | 9 | 1. Set an environment variable called `WEB3_INFURA_PROJECT_ID` that contains your Infura project ID. 10 | 2. Create a virtual environment with `python3 -m venv venv` 11 | 3. Activate the virtual environment with `source venv/bin/activate` 12 | 4. Install the dependencies with `pip install -r requirements.txt` 13 | 5. Run `python3 main.py` 14 | """ 15 | from ape import Contract, convert, networks 16 | from ape_ethereum import multicall 17 | 18 | # Requires an Infura API key to be set in an environment variable named `WEB3_INFURA_PROJECT_ID` 19 | with networks.ethereum.mainnet.use_provider("infura"): 20 | # Define the users and tokens we want to query. 21 | tokens = [ 22 | Contract("0x6B175474E89094C44Da98b954EedeAC495271d0F"), # DAI. 23 | Contract("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"), # USDC. 24 | Contract("0xdAC17F958D2ee523a2206206994597C13D831ec7"), # USDT. 25 | ] 26 | 27 | justin_sun = "0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296" 28 | users = ["vitalik.eth", "cz-🔶-binance.eth", justin_sun] 29 | 30 | # Create a multicall object. 31 | call = multicall.Call() 32 | 33 | # For each token we fetch the symbol and decimals, and then the balance for each user. 34 | for token in tokens: 35 | call.add(token.symbol) 36 | call.add(token.decimals) 37 | for user in users: 38 | call.add(token.balanceOf, user) 39 | 40 | # Execute the multicall, then format and print results. 41 | response = list(call()) 42 | 43 | # Each token has 5 calls: symbol, decimals, and 3 balances. Therefore token symbols are at 44 | # indices 0, 5, 10, and token decimals are at indices 1, 6, 11. 45 | num_calls = 5 46 | for i, token in enumerate(tokens): 47 | # i = 0, 1, 2 therefore: 48 | # - Token symbol indices are `i * num_calls` 49 | # - Token decimals are at `i * num_calls + 1`` 50 | symbol = response[i * num_calls] 51 | print(f"\033[1m{symbol} Balances:\033[0m") # Print token symbol in bold. 52 | decimals = response[i * num_calls + 1] 53 | 54 | for j, user in enumerate(users): 55 | # The remaining 3 calls in a set of 5 are balances for each user, so we know that user 56 | # balances are at indices `i * num_calls + 2 + j`, where 2 is the number of calls prior 57 | # to the first balance call. 58 | num_prior_calls = 2 59 | balance = response[i * num_calls + num_prior_calls + j] 60 | user_name = user if j == 0 else "CZ" if j == 1 else "Justin Sun" 61 | formatted_balance = balance / 10**decimals 62 | print(f" {user_name:<15} {formatted_balance}") 63 | -------------------------------------------------------------------------------- /src/Multicall2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | /// @title Multicall2 - Aggregate results from multiple read-only function calls 5 | /// @author Michael Elliot 6 | /// @author Joshua Levine 7 | /// @author Nick Johnson 8 | contract Multicall2 { 9 | struct Call { 10 | address target; 11 | bytes callData; 12 | } 13 | 14 | struct Result { 15 | bool success; 16 | bytes returnData; 17 | } 18 | 19 | function aggregate(Call[] calldata calls) public returns (uint256 blockNumber, bytes[] memory returnData) { 20 | blockNumber = block.number; 21 | returnData = new bytes[](calls.length); 22 | for (uint256 i = 0; i < calls.length; i++) { 23 | (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); 24 | require(success, "Multicall aggregate: call failed"); 25 | returnData[i] = ret; 26 | } 27 | } 28 | 29 | function tryAggregate(bool requireSuccess, Call[] calldata calls) public returns (Result[] memory returnData) { 30 | returnData = new Result[](calls.length); 31 | for (uint256 i = 0; i < calls.length; i++) { 32 | (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); 33 | 34 | if (requireSuccess) { 35 | require(success, "Multicall2 aggregate: call failed"); 36 | } 37 | 38 | returnData[i] = Result(success, ret); 39 | } 40 | } 41 | 42 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) { 43 | blockNumber = block.number; 44 | blockHash = blockhash(block.number); 45 | returnData = tryAggregate(requireSuccess, calls); 46 | } 47 | 48 | function blockAndAggregate(Call[] calldata calls) public returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) { 49 | (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); 50 | } 51 | 52 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { 53 | blockHash = blockhash(blockNumber); 54 | } 55 | 56 | function getBlockNumber() public view returns (uint256 blockNumber) { 57 | blockNumber = block.number; 58 | } 59 | 60 | function getCurrentBlockCoinbase() public view returns (address coinbase) { 61 | coinbase = block.coinbase; 62 | } 63 | 64 | function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { 65 | difficulty = block.difficulty; 66 | } 67 | 68 | function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { 69 | gaslimit = block.gaslimit; 70 | } 71 | 72 | function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { 73 | timestamp = block.timestamp; 74 | } 75 | 76 | function getEthBalance(address addr) public view returns (uint256 balance) { 77 | balance = addr.balance; 78 | } 79 | 80 | function getLastBlockHash() public view returns (bytes32 blockHash) { 81 | blockHash = blockhash(block.number - 1); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/typescript/viem.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @notice viem (https://viem.sh/) has native Multicall3 support which we leverage here. This 3 | * example shows how to compute Uniswap V3 pool addresses and look up their token balances. To run 4 | * the example: 5 | * - Install dependencies with `pnpm install` 6 | * - Run `pnpm ts-node viem.ts` 7 | * 8 | * You can replace `pnpm` with the node package manager of your choice. 9 | */ 10 | import { createPublicClient, http, parseAbi, formatUnits } from 'viem'; 11 | import { mainnet } from 'viem/chains'; 12 | 13 | // Setup the client (in ethers.js, this is called a provider). 14 | const client = createPublicClient({ 15 | chain: mainnet, 16 | transport: http(process.env.ETH_RPC_URL), 17 | }); 18 | 19 | // Uniswap V3 constants. 20 | const FEE_TIERS = [100, 500, 3000, 10000]; 21 | const UNISWAP_FACTORY = { 22 | address: '0x1F98431c8aD98523631AE4a59f267346ea31F984', 23 | abi: parseAbi([ 24 | 'function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)', 25 | ]), 26 | } as const; 27 | 28 | // Tokens addresses. 29 | const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; 30 | const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; 31 | 32 | async function example1() { 33 | // The first multicall computes the address of the DAI/USDC pool for each fee tier. 34 | const poolAddrCalls = FEE_TIERS.map((fee) => { 35 | return { 36 | ...UNISWAP_FACTORY, 37 | functionName: 'getPool', 38 | args: [DAI_ADDRESS, USDC_ADDRESS, fee], 39 | } as const; 40 | }); 41 | 42 | // Execute the multicall and get the pool addresses. None of these calls can fail so we set 43 | // `allowFailure` to false. This results in each return value's type matching the type of the 44 | // corresponding call, e.g. `0x${string}` for addresses, `bigint` for uint256, etc. If we set 45 | // `allowFailure` to true then the returns types are of the following shape, using the example of 46 | // the address return type: 47 | // { 48 | // error: Error; 49 | // result?: undefined; 50 | // status: "error"; 51 | // } | { 52 | // error?: undefined; 53 | // result: `0x${string}`; 54 | // status: "success"; 55 | // } 56 | const poolAddresses = await client.multicall({ contracts: poolAddrCalls, allowFailure: false }); 57 | console.log('DAI/USDC Pool Addresses'); 58 | const percentages = FEE_TIERS.map((fee) => 59 | (fee / 1e6).toLocaleString(undefined, { 60 | style: 'percent', 61 | minimumIntegerDigits: 1, 62 | minimumFractionDigits: 2, 63 | }) 64 | ); 65 | percentages.map((percent, i) => console.log(` ${percent} pool: ${poolAddresses[i]}`)); 66 | 67 | // For each pool, let's get the DAI and USDC balances. 68 | const balanceOfAbi = parseAbi([ 69 | 'function balanceOf(address who) external view returns (uint256 balance)', 70 | ]); 71 | const DAI = { address: DAI_ADDRESS, abi: balanceOfAbi } as const; 72 | const USDC = { address: USDC_ADDRESS, abi: balanceOfAbi } as const; 73 | 74 | const balanceCalls = poolAddresses 75 | .map((poolAddress) => { 76 | return [ 77 | { ...DAI, functionName: 'balanceOf', args: [poolAddress] } as const, 78 | { ...USDC, functionName: 'balanceOf', args: [poolAddress] } as const, 79 | ]; 80 | }) 81 | .flat(); 82 | 83 | // Execute the multicall and log the results. 84 | const balances = await client.multicall({ contracts: balanceCalls, allowFailure: false }); 85 | 86 | console.log('DAI/USDC Pool Balances'); 87 | balances.map((balance, i) => { 88 | const token = i % 2 === 0 ? 'DAI' : 'USDC'; 89 | const decimals = i % 2 === 0 ? 18 : 6; 90 | const percent = percentages[Math.floor(i / 2)]; 91 | const amount = Number(formatUnits(balance, decimals)).toLocaleString(undefined, {}); 92 | const spacer = ' '.repeat(5 - token.length); 93 | console.log(` ${percent} pool ${token} balance:${spacer}${amount}`); 94 | }); 95 | } 96 | 97 | example1().catch(console.error); 98 | -------------------------------------------------------------------------------- /examples/typescript/ethers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @notice ethers.js (https://docs.ethers.org/v6/) does not have native Multicall3 support so this 3 | * example shows how to interact with the contract directly. This example shows how to reverse 4 | * resolve ENS names for a list of addresses. It uses the `aggregate3` method to support reverting 5 | * calls. To run the example: 6 | * - Install dependencies with `pnpm install` 7 | * - Run `pnpm ts-node ethers.ts` 8 | * 9 | * You can replace `pnpm` with the node package manager of your choice. 10 | */ 11 | import { Contract, Interface, JsonRpcProvider, namehash } from 'ethers'; 12 | import { MULTICALL_ADDRESS, MULTICALL_ABI_ETHERS } from './constants'; 13 | 14 | // Setup the provider (in viem, this is called a client). 15 | const MAINNET_RPC_URL = process.env.MAINNET_RPC_URL; 16 | if (!MAINNET_RPC_URL) throw new Error('Please set the MAINNET_RPC_URL environment variable.'); 17 | const provider = new JsonRpcProvider(MAINNET_RPC_URL); 18 | 19 | // Get Multicall contract instance. 20 | const multicall = new Contract(MULTICALL_ADDRESS, MULTICALL_ABI_ETHERS, provider); 21 | 22 | // Reverse resolve ENS names for a list of addresses using the `aggregate3` method to support 23 | // reverting calls. The process shown here for reverse resolving ENS names is based on: 24 | // https://github.com/ethers-io/ethers.js/blob/0802b70a724321f56d4c170e4c8a46b7804dfb48/src.ts/providers/abstract-provider.ts#L976 25 | async function example1() { 26 | // Define some users. 27 | const users = [ 28 | '0x8700B87C2A053BDE8Cdc84d5078B4AE47c127FeB', 29 | '0x9EAB9D856a3a667dc4CD10001D59c679C64756E7', 30 | '0x78d32460D0a53Ac2678e869Eb6b4f6bA9d2Ef360', 31 | '0x3B60e31CFC48a9074CD5bEbb26C9EAa77650a43F', 32 | '0x99FBa19112f221D0B44c9c22241f5e6b2Db715F6', 33 | '0xE943CA883ef3294E0FC55a1A14591aBeAD1B5927', 34 | '0x26E3a9c84fdB9b7fE33Dfd5E8D273D016e4e4Fb6', 35 | ]; 36 | 37 | // Setup the contract addresses and interface methods that we'll need. 38 | const ensRegistryAddr = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; 39 | const ensRegistryInterface = new Interface(['function resolver(bytes32) view returns (address)']); 40 | const resolverInterface = new Interface(['function name(bytes32) view returns (string)']); 41 | 42 | // CALL 1: Get the reverse resolver address for each account. 43 | // For each address, compute it's reverse resolver namehash. 44 | const nodes = users.map((addr) => namehash(addr.substring(2).toLowerCase() + '.addr.reverse')); 45 | 46 | // Prepare the calls to look up each user's resolver address. 47 | const resolverCalls = nodes.map((node) => ({ 48 | target: ensRegistryAddr, 49 | allowFailure: true, // We allow failure for all calls. 50 | callData: ensRegistryInterface.encodeFunctionData('resolver', [node]), 51 | })); 52 | 53 | // Execute those calls. 54 | type Aggregate3Response = { success: boolean; returnData: string }; 55 | const resolverResults: Aggregate3Response[] = await multicall.aggregate3.staticCall( 56 | resolverCalls 57 | ); 58 | 59 | // Decode the responses. 60 | const resolverAddrs = resolverResults.map(({ success, returnData }, i) => { 61 | if (!success) throw new Error(`Failed to get resolver for ${users[i]}`); 62 | return ensRegistryInterface.decodeFunctionResult('resolver', returnData)[0]; 63 | }); 64 | 65 | // CALL 2: Get the name for each account. 66 | // First we prepare the calls. 67 | const nameCalls = resolverAddrs.map((resolverAddr, i) => ({ 68 | target: resolverAddr, 69 | allowFailure: false, // We allow failure for all calls. 70 | callData: resolverInterface.encodeFunctionData('name', [nodes[i]]), 71 | })); 72 | 73 | // Execute those calls. 74 | const nameResults: Aggregate3Response[] = await multicall.aggregate3.staticCall(nameCalls); 75 | 76 | // Decode the responses. 77 | const names = nameResults.map(({ success, returnData }, i) => { 78 | if (!success) throw new Error(`Failed to get name for ${users[i]}`); 79 | if (returnData === '0x') return users[i]; // If no ENS name, return the address. 80 | return resolverInterface.decodeFunctionResult('name', returnData)[0]; 81 | }); 82 | 83 | // Print the mapping of address to ENS names. 84 | users.forEach((user, i) => console.log(`${user}: ${names[i]}`)); 85 | } 86 | 87 | example1().catch(console.error); 88 | -------------------------------------------------------------------------------- /examples/typescript/constants.ts: -------------------------------------------------------------------------------- 1 | // The viem and ethers.js human-readable ABI's are different. First we have `MULTICALL_ABI` which is 2 | // the viem-compatible version, then `MULTICALL_ABI_ETHERS` which is the ethers.js-compatible 3 | // version. We could have provided the standard JSON ABI instead and used it with both ethers and 4 | // viem, but human-readable ABI's are much more readable. 5 | 6 | export const MULTICALL_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11'; 7 | 8 | // Viem. 9 | import { parseAbi } from 'viem'; 10 | export const MULTICALL_ABI = [ 11 | // https://github.com/mds1/multicall 12 | 'struct Call { address target; bytes callData; }', 13 | 'struct Call3 { address target; bool allowFailure; bytes callData; }', 14 | 'struct Call3Value { address target; bool allowFailure; uint256 value; bytes callData; }', 15 | 'struct Result { bool success; bytes returnData; }', 16 | 'function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData)', 17 | 'function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData)', 18 | 'function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)', 19 | 'function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)', 20 | 'function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData)', 21 | 'function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData)', 22 | 'function getBasefee() view returns (uint256 basefee)', 23 | 'function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)', 24 | 'function getBlockNumber() view returns (uint256 blockNumber)', 25 | 'function getChainId() view returns (uint256 chainid)', 26 | 'function getCurrentBlockCoinbase() view returns (address coinbase)', 27 | 'function getCurrentBlockDifficulty() view returns (uint256 difficulty)', 28 | 'function getCurrentBlockGasLimit() view returns (uint256 gaslimit)', 29 | 'function getCurrentBlockTimestamp() view returns (uint256 timestamp)', 30 | 'function getEthBalance(address addr) view returns (uint256 balance)', 31 | 'function getLastBlockHash() view returns (bytes32 blockHash)', 32 | ] as const; 33 | 34 | export const MULTICALL_CONTRACT = { 35 | address: MULTICALL_ADDRESS, 36 | abi: parseAbi(MULTICALL_ABI), 37 | } as const; 38 | 39 | // Ethers 40 | export const MULTICALL_ABI_ETHERS = [ 41 | // https://github.com/mds1/multicall 42 | 'function aggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes[] returnData)', 43 | 'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)', 44 | 'function aggregate3Value(tuple(address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)', 45 | 'function blockAndAggregate(tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes32 blockHash, tuple(bool success, bytes returnData)[] returnData)', 46 | 'function getBasefee() view returns (uint256 basefee)', 47 | 'function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)', 48 | 'function getBlockNumber() view returns (uint256 blockNumber)', 49 | 'function getChainId() view returns (uint256 chainid)', 50 | 'function getCurrentBlockCoinbase() view returns (address coinbase)', 51 | 'function getCurrentBlockDifficulty() view returns (uint256 difficulty)', 52 | 'function getCurrentBlockGasLimit() view returns (uint256 gaslimit)', 53 | 'function getCurrentBlockTimestamp() view returns (uint256 timestamp)', 54 | 'function getEthBalance(address addr) view returns (uint256 balance)', 55 | 'function getLastBlockHash() view returns (bytes32 blockHash)', 56 | 'function tryAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)', 57 | 'function tryBlockAndAggregate(bool requireSuccess, tuple(address target, bytes callData)[] calls) payable returns (uint256 blockNumber, bytes32 blockHash, tuple(bool success, bytes returnData)[] returnData)', 58 | ]; 59 | 60 | export const ERC20_ABI = [ 61 | 'event Approval(address indexed owner, address indexed spender, uint256 value)', 62 | 'event UnBlacklisted(address indexed _account)', 63 | 'function allowance(address owner, address spender) view returns (uint256)', 64 | 'function approve(address spender, uint256 value) returns (bool)', 65 | 'function balanceOf(address account) view returns (uint256)', 66 | 'function decimals() view returns (uint8)', 67 | 'function name() view returns (string)', 68 | 'function symbol() view returns (string)', 69 | 'function totalSupply() view returns (uint256)', 70 | 'function transfer(address to, uint256 value) returns (bool)', 71 | 'function transferFrom(address from, address to, uint256 value) returns (bool)', 72 | ] as const; -------------------------------------------------------------------------------- /src/test/Multicall2.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {Multicall2} from "../Multicall2.sol"; 6 | import {MockCallee} from "./mocks/MockCallee.sol"; 7 | 8 | contract Multicall2Test is Test { 9 | Multicall2 multicall; 10 | MockCallee callee; 11 | 12 | /// @notice Setups up the testing suite 13 | function setUp() public { 14 | multicall = new Multicall2(); 15 | callee = new MockCallee(); 16 | } 17 | 18 | /// >>>>>>>>>>>>>>>>>>>>> AGGREGATE TESTS <<<<<<<<<<<<<<<<<<<<< /// 19 | 20 | function testAggregation() public { 21 | // Test successful call 22 | Multicall2.Call[] memory calls = new Multicall2.Call[](1); 23 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 24 | (uint256 blockNumber, bytes[] memory returnData) = multicall.aggregate(calls); 25 | assertEq(blockNumber, block.number); 26 | assertEq(keccak256(returnData[0]), keccak256(abi.encodePacked(blockhash(block.number)))); 27 | } 28 | 29 | function testUnsuccessfulAggregation() public { 30 | // Test unexpected revert 31 | Multicall2.Call[] memory calls = new Multicall2.Call[](2); 32 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 33 | calls[1] = Multicall2.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 34 | vm.expectRevert(bytes("Multicall aggregate: call failed")); 35 | multicall.aggregate(calls); 36 | } 37 | 38 | /// >>>>>>>>>>>>>>>>>>> TRY AGGREGATE TESTS <<<<<<<<<<<<<<<<<<< /// 39 | 40 | function testTryAggregate() public { 41 | Multicall2.Call[] memory calls = new Multicall2.Call[](2); 42 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 43 | calls[1] = Multicall2.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 44 | (Multicall2.Result[] memory returnData) = multicall.tryAggregate(false, calls); 45 | assertTrue(returnData[0].success); 46 | assertEq(keccak256(returnData[0].returnData), keccak256(abi.encodePacked(blockhash(block.number)))); 47 | assertTrue(!returnData[1].success); 48 | } 49 | 50 | function testTryAggregateUnsuccessful() public { 51 | Multicall2.Call[] memory calls = new Multicall2.Call[](2); 52 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 53 | calls[1] = Multicall2.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 54 | vm.expectRevert(bytes("Multicall2 aggregate: call failed")); 55 | multicall.tryAggregate(true, calls); 56 | } 57 | 58 | /// >>>>>>>>>>>>>> TRY BLOCK AND AGGREGATE TESTS <<<<<<<<<<<<<< /// 59 | 60 | function testTryBlockAndAggregate() public { 61 | Multicall2.Call[] memory calls = new Multicall2.Call[](2); 62 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 63 | calls[1] = Multicall2.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 64 | (uint256 blockNumber, bytes32 blockHash, Multicall2.Result[] memory returnData) = multicall.tryBlockAndAggregate(false, calls); 65 | assertEq(blockNumber, block.number); 66 | assertEq(blockHash, blockhash(block.number)); 67 | assertTrue(returnData[0].success); 68 | assertEq(keccak256(returnData[0].returnData), keccak256(abi.encodePacked(blockhash(block.number)))); 69 | assertTrue(!returnData[1].success); 70 | } 71 | 72 | function testTryBlockAndAggregateUnsuccessful() public { 73 | Multicall2.Call[] memory calls = new Multicall2.Call[](2); 74 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 75 | calls[1] = Multicall2.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 76 | vm.expectRevert(bytes("Multicall2 aggregate: call failed")); 77 | multicall.tryBlockAndAggregate(true, calls); 78 | } 79 | 80 | function testBlockAndAggregateUnsuccessful() public { 81 | Multicall2.Call[] memory calls = new Multicall2.Call[](2); 82 | calls[0] = Multicall2.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 83 | calls[1] = Multicall2.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 84 | vm.expectRevert(bytes("Multicall2 aggregate: call failed")); 85 | multicall.blockAndAggregate(calls); 86 | } 87 | 88 | /// >>>>>>>>>>>>>>>>>>>>>> HELPER TESTS <<<<<<<<<<<<<<<<<<<<<<< /// 89 | 90 | function testGetBlockHash(uint256 blockNumber) public { 91 | assertEq(blockhash(blockNumber), multicall.getBlockHash(blockNumber)); 92 | } 93 | 94 | function testGetBlockNumber() public { 95 | assertEq(block.number, multicall.getBlockNumber()); 96 | } 97 | 98 | function testGetCurrentBlockCoinbase() public { 99 | assertEq(block.coinbase, multicall.getCurrentBlockCoinbase()); 100 | } 101 | 102 | function testGetCurrentBlockDifficulty() public { 103 | assertEq(block.difficulty, multicall.getCurrentBlockDifficulty()); 104 | } 105 | 106 | function testGetCurrentBlockGasLimit() public { 107 | assertEq(block.gaslimit, multicall.getCurrentBlockGasLimit()); 108 | } 109 | 110 | function testGetCurrentBlockTimestamp() public { 111 | assertEq(block.timestamp, multicall.getCurrentBlockTimestamp()); 112 | } 113 | 114 | function testGetEthBalance(address addr) public { 115 | assertEq(addr.balance, multicall.getEthBalance(addr)); 116 | } 117 | 118 | function testGetLastBlockHash() public { 119 | // Prevent arithmetic underflow on the genesis block 120 | if (block.number == 0) return; 121 | assertEq(blockhash(block.number - 1), multicall.getLastBlockHash()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /examples/rust/src/main.rs: -------------------------------------------------------------------------------- 1 | /// Multicall3 example using ethers-rs: https://github.com/gakonst/ethers-rs 2 | /// 3 | /// ethers-rs has native Multicall3 support which we leverage here. This example batches various 4 | /// calls to the DAI contract with an ETH balance lookup and coinbase address lookup. It then 5 | /// formats and prints the results. 6 | /// 7 | /// Run `cargo run` to run the example. Documentation and examples for the `ethers-rs` Multicall3 8 | /// functionality can be found in the `ethers-rs` docs: https://docs.rs/ethers/2.0.4/ethers/contract/struct.Multicall.html 9 | use ethers::{ 10 | abi::Abi, 11 | contract::{Contract, Multicall}, 12 | providers::{Http, Provider}, 13 | types::{Address, U256}, 14 | utils::format_units, 15 | }; 16 | use serde_json; 17 | use std::{convert::TryFrom, env, error::Error, str::FromStr, sync::Arc}; 18 | 19 | #[tokio::main] 20 | async fn main() -> Result<(), Box> { 21 | // Create provider. 22 | let provider = Arc::new(Provider::::try_from(env::var("MAINNET_RPC_URL")?)?); 23 | 24 | // Define the contracts we want to call. Here we just inline the ABI but you can also load it 25 | // from a file. 26 | let dai_address = Address::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F")?; 27 | let dai_abi: Abi = serde_json::from_str( 28 | r#"[{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"guy","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"usr","type":"address"},{"indexed":true,"internalType":"bytes32","name":"arg1","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"arg2","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"deny","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"move","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bool","name":"allowed","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"pull","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"usr","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"push","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"guy","type":"address"}],"name":"rely","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]"#, 29 | )?; 30 | let dai = Contract::>::new(dai_address, dai_abi, provider.clone()); 31 | 32 | // Create a multicall instance. It defaults to using Multicall3, and we use `none` since our 33 | // network has a known Multicall3 address. 34 | let mut multicall = Multicall::>::new(provider, None).await?; 35 | 36 | // Add the calls we want to query. We pass `false` to `add_call` to indicate the call is not 37 | // allowed to revert. 38 | let vitalik = Address::from_str("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")?; 39 | multicall 40 | .add_call(multicall.contract.method::<_, Address>("getCurrentBlockCoinbase", ())?, false) 41 | .add_call(dai.method::<_, String>("symbol", ())?, false) 42 | .add_call(dai.method::<_, u8>("decimals", ())?, false) 43 | .add_call(dai.method::<_, U256>("balanceOf", vitalik)?, false) 44 | .add_get_eth_balance(vitalik, false); 45 | 46 | // Wait for the response of the multicall and format the results. Because we have a fixed list 47 | // of calls defined above, we know exactly what the response types will be and therefore we 48 | // can explicitly define the types in the `response` tuple as shown below. If the list of calls 49 | // was generated dynamically, e.g. in a for loop, then we would not know these types at compile 50 | // time, so instead we'd have to iterate over the responses and match on the `Token` type of 51 | // each element in the response. 52 | let response: (Address, String, u8, U256, U256) = multicall.call().await?; 53 | let dai_balance = format_units(response.3, response.2 as usize)?; 54 | let eth_balance = format_units(response.4, "ether")?; 55 | 56 | // Print results, aligning the data. 57 | let label1 = "Coinbase:"; 58 | let label2 = format!("Vitalik's {} balance:", response.1); 59 | let label3 = "Vitalik's ETH balance:"; 60 | let width = label1.len().max(label2.len()).max(label3.len()); 61 | println!("{:width$} {:?}", label1, response.0); 62 | println!("{:width$} {}", label2, dai_balance); 63 | println!("{:width$} {}", label3, eth_balance); 64 | 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /src/Multicall3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | /// @title Multicall3 5 | /// @notice Aggregate results from multiple function calls 6 | /// @dev Multicall & Multicall2 backwards-compatible 7 | /// @dev Aggregate methods are marked `payable` to save 24 gas per call 8 | /// @author Michael Elliot 9 | /// @author Joshua Levine 10 | /// @author Nick Johnson 11 | /// @author Andreas Bigger 12 | /// @author Matt Solomon 13 | contract Multicall3 { 14 | struct Call { 15 | address target; 16 | bytes callData; 17 | } 18 | 19 | struct Call3 { 20 | address target; 21 | bool allowFailure; 22 | bytes callData; 23 | } 24 | 25 | struct Call3Value { 26 | address target; 27 | bool allowFailure; 28 | uint256 value; 29 | bytes callData; 30 | } 31 | 32 | struct Result { 33 | bool success; 34 | bytes returnData; 35 | } 36 | 37 | /// @notice Backwards-compatible call aggregation with Multicall 38 | /// @param calls An array of Call structs 39 | /// @return blockNumber The block number where the calls were executed 40 | /// @return returnData An array of bytes containing the responses 41 | function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData) { 42 | blockNumber = block.number; 43 | uint256 length = calls.length; 44 | returnData = new bytes[](length); 45 | Call calldata call; 46 | for (uint256 i = 0; i < length;) { 47 | bool success; 48 | call = calls[i]; 49 | (success, returnData[i]) = call.target.call(call.callData); 50 | require(success, "Multicall3: call failed"); 51 | unchecked { ++i; } 52 | } 53 | } 54 | 55 | /// @notice Backwards-compatible with Multicall2 56 | /// @notice Aggregate calls without requiring success 57 | /// @param requireSuccess If true, require all calls to succeed 58 | /// @param calls An array of Call structs 59 | /// @return returnData An array of Result structs 60 | function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData) { 61 | uint256 length = calls.length; 62 | returnData = new Result[](length); 63 | Call calldata call; 64 | for (uint256 i = 0; i < length;) { 65 | Result memory result = returnData[i]; 66 | call = calls[i]; 67 | (result.success, result.returnData) = call.target.call(call.callData); 68 | if (requireSuccess) require(result.success, "Multicall3: call failed"); 69 | unchecked { ++i; } 70 | } 71 | } 72 | 73 | /// @notice Backwards-compatible with Multicall2 74 | /// @notice Aggregate calls and allow failures using tryAggregate 75 | /// @param calls An array of Call structs 76 | /// @return blockNumber The block number where the calls were executed 77 | /// @return blockHash The hash of the block where the calls were executed 78 | /// @return returnData An array of Result structs 79 | function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) { 80 | blockNumber = block.number; 81 | blockHash = blockhash(block.number); 82 | returnData = tryAggregate(requireSuccess, calls); 83 | } 84 | 85 | /// @notice Backwards-compatible with Multicall2 86 | /// @notice Aggregate calls and allow failures using tryAggregate 87 | /// @param calls An array of Call structs 88 | /// @return blockNumber The block number where the calls were executed 89 | /// @return blockHash The hash of the block where the calls were executed 90 | /// @return returnData An array of Result structs 91 | function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) { 92 | (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); 93 | } 94 | 95 | /// @notice Aggregate calls, ensuring each returns success if required 96 | /// @param calls An array of Call3 structs 97 | /// @return returnData An array of Result structs 98 | function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData) { 99 | uint256 length = calls.length; 100 | returnData = new Result[](length); 101 | Call3 calldata calli; 102 | for (uint256 i = 0; i < length;) { 103 | Result memory result = returnData[i]; 104 | calli = calls[i]; 105 | (result.success, result.returnData) = calli.target.call(calli.callData); 106 | assembly { 107 | // Revert if the call fails and failure is not allowed 108 | // `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)` 109 | if iszero(or(calldataload(add(calli, 0x20)), mload(result))) { 110 | // set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) 111 | mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) 112 | // set data offset 113 | mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) 114 | // set length of revert string 115 | mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017) 116 | // set revert string: bytes32(abi.encodePacked("Multicall3: call failed")) 117 | mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000) 118 | revert(0x00, 0x64) 119 | } 120 | } 121 | unchecked { ++i; } 122 | } 123 | } 124 | 125 | /// @notice Aggregate calls with a msg value 126 | /// @notice Reverts if msg.value is less than the sum of the call values 127 | /// @param calls An array of Call3Value structs 128 | /// @return returnData An array of Result structs 129 | function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData) { 130 | uint256 valAccumulator; 131 | uint256 length = calls.length; 132 | returnData = new Result[](length); 133 | Call3Value calldata calli; 134 | for (uint256 i = 0; i < length;) { 135 | Result memory result = returnData[i]; 136 | calli = calls[i]; 137 | uint256 val = calli.value; 138 | // Humanity will be a Type V Kardashev Civilization before this overflows - andreas 139 | // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256 140 | unchecked { valAccumulator += val; } 141 | (result.success, result.returnData) = calli.target.call{value: val}(calli.callData); 142 | assembly { 143 | // Revert if the call fails and failure is not allowed 144 | // `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)` 145 | if iszero(or(calldataload(add(calli, 0x20)), mload(result))) { 146 | // set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) 147 | mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) 148 | // set data offset 149 | mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) 150 | // set length of revert string 151 | mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017) 152 | // set revert string: bytes32(abi.encodePacked("Multicall3: call failed")) 153 | mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000) 154 | revert(0x00, 0x84) 155 | } 156 | } 157 | unchecked { ++i; } 158 | } 159 | // Finally, make sure the msg.value = SUM(call[0...i].value) 160 | require(msg.value == valAccumulator, "Multicall3: value mismatch"); 161 | } 162 | 163 | /// @notice Returns the block hash for the given block number 164 | /// @param blockNumber The block number 165 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { 166 | blockHash = blockhash(blockNumber); 167 | } 168 | 169 | /// @notice Returns the block number 170 | function getBlockNumber() public view returns (uint256 blockNumber) { 171 | blockNumber = block.number; 172 | } 173 | 174 | /// @notice Returns the block coinbase 175 | function getCurrentBlockCoinbase() public view returns (address coinbase) { 176 | coinbase = block.coinbase; 177 | } 178 | 179 | /// @notice Returns the block difficulty 180 | function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { 181 | difficulty = block.difficulty; 182 | } 183 | 184 | /// @notice Returns the block gas limit 185 | function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { 186 | gaslimit = block.gaslimit; 187 | } 188 | 189 | /// @notice Returns the block timestamp 190 | function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { 191 | timestamp = block.timestamp; 192 | } 193 | 194 | /// @notice Returns the (ETH) balance of a given address 195 | function getEthBalance(address addr) public view returns (uint256 balance) { 196 | balance = addr.balance; 197 | } 198 | 199 | /// @notice Returns the block hash of the last block 200 | function getLastBlockHash() public view returns (bytes32 blockHash) { 201 | unchecked { 202 | blockHash = blockhash(block.number - 1); 203 | } 204 | } 205 | 206 | /// @notice Gets the base fee of the given block 207 | /// @notice Can revert if the BASEFEE opcode is not implemented by the given chain 208 | function getBasefee() public view returns (uint256 basefee) { 209 | basefee = block.basefee; 210 | } 211 | 212 | /// @notice Returns the chain id 213 | function getChainId() public view returns (uint256 chainid) { 214 | chainid = block.chainid; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/test/Multicall3.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.12; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {Multicall3} from "../Multicall3.sol"; 6 | import {MockCallee} from "./mocks/MockCallee.sol"; 7 | import {EtherSink} from "./mocks/EtherSink.sol"; 8 | 9 | contract Multicall3Test is Test { 10 | Multicall3 multicall; 11 | MockCallee callee; 12 | EtherSink etherSink; 13 | 14 | /// @notice Setups up the testing suite 15 | function setUp() public { 16 | multicall = new Multicall3(); 17 | callee = new MockCallee(); 18 | etherSink = new EtherSink(); 19 | } 20 | 21 | /// >>>>>>>>>>>>>>>>>>>>> AGGREGATE TESTS <<<<<<<<<<<<<<<<<<<<< /// 22 | 23 | function testAggregation() public { 24 | // Test successful call 25 | Multicall3.Call[] memory calls = new Multicall3.Call[](1); 26 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 27 | (uint256 blockNumber, bytes[] memory returnData) = multicall.aggregate(calls); 28 | assertEq(blockNumber, block.number); 29 | assertEq(keccak256(returnData[0]), keccak256(abi.encodePacked(blockhash(block.number)))); 30 | } 31 | 32 | function testUnsuccessfulAggregation() public { 33 | // Test unexpected revert 34 | Multicall3.Call[] memory calls = new Multicall3.Call[](2); 35 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 36 | calls[1] = Multicall3.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 37 | vm.expectRevert(bytes("Multicall3: call failed")); 38 | multicall.aggregate(calls); 39 | } 40 | 41 | /// >>>>>>>>>>>>>>>>>>> TRY AGGREGATE TESTS <<<<<<<<<<<<<<<<<<< /// 42 | 43 | function testTryAggregate() public { 44 | Multicall3.Call[] memory calls = new Multicall3.Call[](2); 45 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 46 | calls[1] = Multicall3.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 47 | (Multicall3.Result[] memory returnData) = multicall.tryAggregate(false, calls); 48 | assertTrue(returnData[0].success); 49 | assertEq(keccak256(returnData[0].returnData), keccak256(abi.encodePacked(blockhash(block.number)))); 50 | assertTrue(!returnData[1].success); 51 | } 52 | 53 | function testTryAggregateUnsuccessful() public { 54 | Multicall3.Call[] memory calls = new Multicall3.Call[](2); 55 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 56 | calls[1] = Multicall3.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 57 | vm.expectRevert(bytes("Multicall3: call failed")); 58 | multicall.tryAggregate(true, calls); 59 | } 60 | 61 | /// >>>>>>>>>>>>>> TRY BLOCK AND AGGREGATE TESTS <<<<<<<<<<<<<< /// 62 | 63 | function testTryBlockAndAggregate() public { 64 | Multicall3.Call[] memory calls = new Multicall3.Call[](2); 65 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 66 | calls[1] = Multicall3.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 67 | (uint256 blockNumber, bytes32 blockHash, Multicall3.Result[] memory returnData) = multicall.tryBlockAndAggregate(false, calls); 68 | assertEq(blockNumber, block.number); 69 | assertEq(blockHash, blockhash(block.number)); 70 | assertTrue(returnData[0].success); 71 | assertEq(keccak256(returnData[0].returnData), keccak256(abi.encodePacked(blockhash(block.number)))); 72 | assertTrue(!returnData[1].success); 73 | } 74 | 75 | function testTryBlockAndAggregateUnsuccessful() public { 76 | Multicall3.Call[] memory calls = new Multicall3.Call[](2); 77 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 78 | calls[1] = Multicall3.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 79 | vm.expectRevert(bytes("Multicall3: call failed")); 80 | multicall.tryBlockAndAggregate(true, calls); 81 | } 82 | 83 | function testBlockAndAggregateUnsuccessful() public { 84 | Multicall3.Call[] memory calls = new Multicall3.Call[](2); 85 | calls[0] = Multicall3.Call(address(callee), abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 86 | calls[1] = Multicall3.Call(address(callee), abi.encodeWithSignature("thisMethodReverts()")); 87 | vm.expectRevert(bytes("Multicall3: call failed")); 88 | multicall.blockAndAggregate(calls); 89 | } 90 | 91 | /// >>>>>>>>>>>>>>>>>>> AGGREGATE3 TESTS <<<<<<<<<<<<<<<<<<<<<< /// 92 | 93 | function testAggregate3() public { 94 | Multicall3.Call3[] memory calls = new Multicall3.Call3[](3); 95 | calls[0] = Multicall3.Call3(address(callee), false, abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 96 | calls[1] = Multicall3.Call3(address(callee), true, abi.encodeWithSignature("thisMethodReverts()")); 97 | calls[2] = Multicall3.Call3(address(multicall), true, abi.encodeWithSignature("getCurrentBlockTimestamp()")); 98 | (Multicall3.Result[] memory returnData) = multicall.aggregate3(calls); 99 | 100 | // Call 1. 101 | assertTrue(returnData[0].success); 102 | assertEq(blockhash(block.number), abi.decode(returnData[0].returnData, (bytes32))); 103 | assertEq(keccak256(returnData[0].returnData), keccak256(abi.encodePacked(blockhash(block.number)))); 104 | 105 | // Call 2. 106 | assertTrue(!returnData[1].success); 107 | assertEq(returnData[1].returnData.length, 4); 108 | assertEq(bytes4(returnData[1].returnData), bytes4(keccak256("Unsuccessful()"))); 109 | 110 | // Call 3. 111 | assertTrue(returnData[2].success); 112 | assertEq(abi.decode(returnData[2].returnData, (uint256)), block.timestamp); 113 | } 114 | 115 | function testAggregate3Unsuccessful() public { 116 | Multicall3.Call3[] memory calls = new Multicall3.Call3[](2); 117 | calls[0] = Multicall3.Call3(address(callee), false, abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 118 | calls[1] = Multicall3.Call3(address(callee), false, abi.encodeWithSignature("thisMethodReverts()")); 119 | vm.expectRevert(bytes("Multicall3: call failed")); 120 | multicall.aggregate3(calls); 121 | } 122 | 123 | /// >>>>>>>>>>>>>>>>> AGGREGATE3VALUE TESTS <<<<<<<<<<<<<<<<<<< /// 124 | 125 | function testAggregate3Value() public { 126 | Multicall3.Call3Value[] memory calls = new Multicall3.Call3Value[](3); 127 | calls[0] = Multicall3.Call3Value(address(callee), false, 0, abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 128 | calls[1] = Multicall3.Call3Value(address(callee), true, 0, abi.encodeWithSignature("thisMethodReverts()")); 129 | calls[2] = Multicall3.Call3Value(address(callee), true, 1, abi.encodeWithSignature("sendBackValue(address)", address(etherSink))); 130 | (Multicall3.Result[] memory returnData) = multicall.aggregate3Value{value: 1}(calls); 131 | assertTrue(returnData[0].success); 132 | assertEq(keccak256(returnData[0].returnData), keccak256(abi.encodePacked(blockhash(block.number)))); 133 | assertTrue(!returnData[1].success); 134 | assertTrue(returnData[2].success); 135 | } 136 | 137 | function testAggregate3ValueUnsuccessful() public { 138 | Multicall3.Call3Value[] memory calls = new Multicall3.Call3Value[](3); 139 | calls[0] = Multicall3.Call3Value(address(callee), false, 0, abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 140 | calls[1] = Multicall3.Call3Value(address(callee), false, 0, abi.encodeWithSignature("thisMethodReverts()")); 141 | calls[2] = Multicall3.Call3Value(address(callee), false, 1, abi.encodeWithSignature("sendBackValue(address)", address(etherSink))); 142 | vm.expectRevert(bytes("Multicall3: call failed")); 143 | multicall.aggregate3Value(calls); 144 | 145 | // Should fail if we don't provide enough value 146 | Multicall3.Call3Value[] memory calls2 = new Multicall3.Call3Value[](3); 147 | calls2[0] = Multicall3.Call3Value(address(callee), true, 0, abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 148 | calls2[1] = Multicall3.Call3Value(address(callee), true, 0, abi.encodeWithSignature("thisMethodReverts()")); 149 | calls2[2] = Multicall3.Call3Value(address(callee), true, 1, abi.encodeWithSignature("sendBackValue(address)", address(etherSink))); 150 | vm.expectRevert(bytes("Multicall3: value mismatch")); 151 | multicall.aggregate3Value(calls2); 152 | 153 | // Works if we provide enough value 154 | Multicall3.Call3Value[] memory calls3 = new Multicall3.Call3Value[](3); 155 | calls3[0] = Multicall3.Call3Value(address(callee), false, 0, abi.encodeWithSignature("getBlockHash(uint256)", block.number)); 156 | calls3[1] = Multicall3.Call3Value(address(callee), true, 0, abi.encodeWithSignature("thisMethodReverts()")); 157 | calls3[2] = Multicall3.Call3Value(address(callee), false, 1, abi.encodeWithSignature("sendBackValue(address)", address(etherSink))); 158 | multicall.aggregate3Value{value: 1}(calls3); 159 | } 160 | 161 | /// >>>>>>>>>>>>>>>>>>>>>> HELPER TESTS <<<<<<<<<<<<<<<<<<<<<<< /// 162 | 163 | function testGetBlockHash(uint256 blockNumber) public { 164 | assertEq(blockhash(blockNumber), multicall.getBlockHash(blockNumber)); 165 | } 166 | 167 | function testGetBlockNumber() public { 168 | assertEq(block.number, multicall.getBlockNumber()); 169 | } 170 | 171 | function testGetCurrentBlockCoinbase() public { 172 | assertEq(block.coinbase, multicall.getCurrentBlockCoinbase()); 173 | } 174 | 175 | function testGetCurrentBlockDifficulty() public { 176 | assertEq(block.difficulty, multicall.getCurrentBlockDifficulty()); 177 | } 178 | 179 | function testGetCurrentBlockGasLimit() public { 180 | assertEq(block.gaslimit, multicall.getCurrentBlockGasLimit()); 181 | } 182 | 183 | function testGetCurrentBlockTimestamp() public { 184 | assertEq(block.timestamp, multicall.getCurrentBlockTimestamp()); 185 | } 186 | 187 | function testGetEthBalance(address addr) public { 188 | assertEq(addr.balance, multicall.getEthBalance(addr)); 189 | } 190 | 191 | function testGetLastBlockHash() public { 192 | // Prevent arithmetic underflow on the genesis block 193 | if (block.number == 0) return; 194 | assertEq(blockhash(block.number - 1), multicall.getLastBlockHash()); 195 | } 196 | 197 | function testGetBasefee() public { 198 | assertEq(block.basefee, multicall.getBasefee()); 199 | } 200 | 201 | function testGetChainId() public { 202 | assertEq(block.chainid, multicall.getChainId()); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /examples/typescript/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | ethers: 9 | specifier: ^6.7.1 10 | version: 6.7.1 11 | viem: 12 | specifier: ^1.9.2 13 | version: 1.9.2(typescript@5.0.3) 14 | 15 | devDependencies: 16 | '@types/node': 17 | specifier: ^20.1.3 18 | version: 20.1.3 19 | ts-node: 20 | specifier: ^10.9.1 21 | version: 10.9.1(@types/node@20.1.3)(typescript@5.0.3) 22 | typescript: 23 | specifier: ^5.0.3 24 | version: 5.0.3 25 | 26 | packages: 27 | 28 | /@adraffy/ens-normalize@1.9.0: 29 | resolution: {integrity: sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==} 30 | dev: false 31 | 32 | /@adraffy/ens-normalize@1.9.2: 33 | resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==} 34 | dev: false 35 | 36 | /@cspotcode/source-map-support@0.8.1: 37 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 38 | engines: {node: '>=12'} 39 | dependencies: 40 | '@jridgewell/trace-mapping': 0.3.9 41 | dev: true 42 | 43 | /@jridgewell/resolve-uri@3.1.1: 44 | resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} 45 | engines: {node: '>=6.0.0'} 46 | dev: true 47 | 48 | /@jridgewell/sourcemap-codec@1.4.15: 49 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 50 | dev: true 51 | 52 | /@jridgewell/trace-mapping@0.3.9: 53 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 54 | dependencies: 55 | '@jridgewell/resolve-uri': 3.1.1 56 | '@jridgewell/sourcemap-codec': 1.4.15 57 | dev: true 58 | 59 | /@noble/curves@1.0.0: 60 | resolution: {integrity: sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==} 61 | dependencies: 62 | '@noble/hashes': 1.3.0 63 | dev: false 64 | 65 | /@noble/curves@1.1.0: 66 | resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} 67 | dependencies: 68 | '@noble/hashes': 1.3.1 69 | dev: false 70 | 71 | /@noble/hashes@1.1.2: 72 | resolution: {integrity: sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==} 73 | dev: false 74 | 75 | /@noble/hashes@1.3.0: 76 | resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} 77 | dev: false 78 | 79 | /@noble/hashes@1.3.1: 80 | resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} 81 | engines: {node: '>= 16'} 82 | dev: false 83 | 84 | /@noble/secp256k1@1.7.1: 85 | resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} 86 | dev: false 87 | 88 | /@scure/base@1.1.2: 89 | resolution: {integrity: sha512-sSCrnIdaUZQHhBxZThMuk7Wm1TWzMD3uJNdGgx3JS23xSqevu0tAOsg8k66nL3R2NwQe65AI9GgqpPOgZys/eA==} 90 | dev: false 91 | 92 | /@scure/bip32@1.3.0: 93 | resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==} 94 | dependencies: 95 | '@noble/curves': 1.0.0 96 | '@noble/hashes': 1.3.1 97 | '@scure/base': 1.1.2 98 | dev: false 99 | 100 | /@scure/bip39@1.2.0: 101 | resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} 102 | dependencies: 103 | '@noble/hashes': 1.3.1 104 | '@scure/base': 1.1.2 105 | dev: false 106 | 107 | /@tsconfig/node10@1.0.9: 108 | resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} 109 | dev: true 110 | 111 | /@tsconfig/node12@1.0.11: 112 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 113 | dev: true 114 | 115 | /@tsconfig/node14@1.0.3: 116 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 117 | dev: true 118 | 119 | /@tsconfig/node16@1.0.3: 120 | resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} 121 | dev: true 122 | 123 | /@types/node@18.15.13: 124 | resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} 125 | dev: false 126 | 127 | /@types/node@20.1.3: 128 | resolution: {integrity: sha512-NP2yfZpgmf2eDRPmgGq+fjGjSwFgYbihA8/gK+ey23qT9RkxsgNTZvGOEpXgzIGqesTYkElELLgtKoMQTys5vA==} 129 | 130 | /@types/ws@8.5.5: 131 | resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} 132 | dependencies: 133 | '@types/node': 20.1.3 134 | dev: false 135 | 136 | /abitype@0.9.3(typescript@5.0.3): 137 | resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==} 138 | peerDependencies: 139 | typescript: '>=5.0.4' 140 | zod: ^3 >=3.19.1 141 | peerDependenciesMeta: 142 | typescript: 143 | optional: true 144 | zod: 145 | optional: true 146 | dependencies: 147 | typescript: 5.0.3 148 | dev: false 149 | 150 | /acorn-walk@8.2.0: 151 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 152 | engines: {node: '>=0.4.0'} 153 | dev: true 154 | 155 | /acorn@8.8.2: 156 | resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} 157 | engines: {node: '>=0.4.0'} 158 | hasBin: true 159 | dev: true 160 | 161 | /aes-js@4.0.0-beta.5: 162 | resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} 163 | dev: false 164 | 165 | /arg@4.1.3: 166 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 167 | dev: true 168 | 169 | /create-require@1.1.1: 170 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 171 | dev: true 172 | 173 | /diff@4.0.2: 174 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 175 | engines: {node: '>=0.3.1'} 176 | dev: true 177 | 178 | /ethers@6.7.1: 179 | resolution: {integrity: sha512-qX5kxIFMfg1i+epfgb0xF4WM7IqapIIu50pOJ17aebkxxa4BacW5jFrQRmCJpDEg2ZK2oNtR5QjrQ1WDBF29dA==} 180 | engines: {node: '>=14.0.0'} 181 | dependencies: 182 | '@adraffy/ens-normalize': 1.9.2 183 | '@noble/hashes': 1.1.2 184 | '@noble/secp256k1': 1.7.1 185 | '@types/node': 18.15.13 186 | aes-js: 4.0.0-beta.5 187 | tslib: 2.4.0 188 | ws: 8.5.0 189 | transitivePeerDependencies: 190 | - bufferutil 191 | - utf-8-validate 192 | dev: false 193 | 194 | /isomorphic-ws@5.0.0(ws@8.12.0): 195 | resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} 196 | peerDependencies: 197 | ws: '*' 198 | dependencies: 199 | ws: 8.12.0 200 | dev: false 201 | 202 | /make-error@1.3.6: 203 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 204 | dev: true 205 | 206 | /ts-node@10.9.1(@types/node@20.1.3)(typescript@5.0.3): 207 | resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} 208 | hasBin: true 209 | peerDependencies: 210 | '@swc/core': '>=1.2.50' 211 | '@swc/wasm': '>=1.2.50' 212 | '@types/node': '*' 213 | typescript: '>=2.7' 214 | peerDependenciesMeta: 215 | '@swc/core': 216 | optional: true 217 | '@swc/wasm': 218 | optional: true 219 | dependencies: 220 | '@cspotcode/source-map-support': 0.8.1 221 | '@tsconfig/node10': 1.0.9 222 | '@tsconfig/node12': 1.0.11 223 | '@tsconfig/node14': 1.0.3 224 | '@tsconfig/node16': 1.0.3 225 | '@types/node': 20.1.3 226 | acorn: 8.8.2 227 | acorn-walk: 8.2.0 228 | arg: 4.1.3 229 | create-require: 1.1.1 230 | diff: 4.0.2 231 | make-error: 1.3.6 232 | typescript: 5.0.3 233 | v8-compile-cache-lib: 3.0.1 234 | yn: 3.1.1 235 | dev: true 236 | 237 | /tslib@2.4.0: 238 | resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} 239 | dev: false 240 | 241 | /typescript@5.0.3: 242 | resolution: {integrity: sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==} 243 | engines: {node: '>=12.20'} 244 | hasBin: true 245 | 246 | /v8-compile-cache-lib@3.0.1: 247 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 248 | dev: true 249 | 250 | /viem@1.9.2(typescript@5.0.3): 251 | resolution: {integrity: sha512-a4l502Sl4ytYOT6a77ezDCEEgztR+wF8ZZRLAEVcrQawFFIiAWXGPrXVPT8MQmDF00idPMKPON8Ayz34Ft3NLw==} 252 | peerDependencies: 253 | typescript: '>=5.0.4' 254 | peerDependenciesMeta: 255 | typescript: 256 | optional: true 257 | dependencies: 258 | '@adraffy/ens-normalize': 1.9.0 259 | '@noble/curves': 1.1.0 260 | '@noble/hashes': 1.3.0 261 | '@scure/bip32': 1.3.0 262 | '@scure/bip39': 1.2.0 263 | '@types/ws': 8.5.5 264 | abitype: 0.9.3(typescript@5.0.3) 265 | isomorphic-ws: 5.0.0(ws@8.12.0) 266 | typescript: 5.0.3 267 | ws: 8.12.0 268 | transitivePeerDependencies: 269 | - bufferutil 270 | - utf-8-validate 271 | - zod 272 | dev: false 273 | 274 | /ws@8.12.0: 275 | resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} 276 | engines: {node: '>=10.0.0'} 277 | peerDependencies: 278 | bufferutil: ^4.0.1 279 | utf-8-validate: '>=5.0.2' 280 | peerDependenciesMeta: 281 | bufferutil: 282 | optional: true 283 | utf-8-validate: 284 | optional: true 285 | dev: false 286 | 287 | /ws@8.5.0: 288 | resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} 289 | engines: {node: '>=10.0.0'} 290 | peerDependencies: 291 | bufferutil: ^4.0.1 292 | utf-8-validate: ^5.0.2 293 | peerDependenciesMeta: 294 | bufferutil: 295 | optional: true 296 | utf-8-validate: 297 | optional: true 298 | dev: false 299 | 300 | /yn@3.1.1: 301 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 302 | engines: {node: '>=6'} 303 | dev: true 304 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | > [!CAUTION] 12 | > The Multicall3 deployer private key was mined with Profanity and has been compromised. 13 | > The attacker(s) with the key typically steal funds from the deployer account, and there is one 14 | > instance of a custom contract being deployed to the Multicall3 address on Ancient8 15 | > ([1](https://scan.ancient8.gg/address/0xcA11bde05977b3631167028862bE2a173976CA11), 16 | > [2](https://github.com/mds1/multicall/issues/336)). 17 | > 18 | > As a result, no new manual deployments will be made by the deployer. 19 | > New deployments can still occur, see [New Deployments](#new-deployments) for more information. 20 | > 21 | > All existing deployments of Multicall3 are safe, and new deployments using the approaches in the 22 | > [New Deployments](#new-deployments) section will continue to be safe. Only the Ancient8 deployment 23 | > is known to be incorrect. 24 | 25 | [`Multicall3`](./src/Multicall3.sol) is deployed on over 250 chains at `0xcA11bde05977b3631167028862bE2a173976CA11`. 26 | The full list of deployed chains along with the Multicall3 ABI can be found at https://multicall3.com. 27 | The ABI is provided in various formats, and can be copied to your clipboard or downloaded to a file. 28 | 29 | Multicall3 is the primary contract in this repository, and **is recommended for all use cases**[^1]. 30 | 31 | - [Usage](#usage) 32 | - [Batch Contract Reads](#batch-contract-reads) 33 | - [Batch Contract Writes](#batch-contract-writes) 34 | - [Deployments and ABI](#deployments-and-abi) 35 | - [Existing Deployments](#existing-deployments) 36 | - [New Deployments](#new-deployments) 37 | - [Contract Verification](#contract-verification) 38 | - [Security](#security) 39 | - [Development](#development) 40 | - [Gas Golfing Techniques](#gas-golfing-tricks-and-optimizations) 41 | 42 | ## Usage 43 | 44 | Multicall3 has two main use cases: 45 | 46 | - Aggregate results from multiple contract reads into a single JSON-RPC request. 47 | - Execute multiple state-changing calls in a single transaction. 48 | 49 | Because it can be used for both use cases, no methods in this contract are `view`, and all can mutate state and are `payable`. 50 | 51 | ### Batch Contract Reads 52 | 53 | This is the most common use case for executing a multicall. 54 | This allows a single `eth_call` JSON RPC request to return the results of multiple contract function calls. 55 | This has many benefits, because it: 56 | 57 | - Reduces the number of separate JSON RPC requests that need to be sent, which is especially useful if using remote nodes like Infura. This (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. 58 | - Guarantees that all values returned are from the same block. 59 | - Enables block number or timestamp to be returned with the read data, to help detect stale data. 60 | 61 | Many libraries and tools such as [ethers-rs](https://github.com/gakonst/ethers-rs), [viem](https://viem.sh/), and [ape](https://apeworx.io/) have native Multicall3 integration. 62 | To learn how to use Multicall3 with these tools, check out this repo's [examples](./examples) and read the documentation for the tool you're using to learn more. 63 | 64 | When directly interacting with the contract to batch calls, the `aggregate3` method is likely what you'll want to use. 65 | It takes an array of `Call3` structs, and returns an array of `Result` structs: 66 | 67 | ```solidity 68 | struct Call3 { 69 | // Target contract to call. 70 | address target; 71 | // If false, the entire call will revert if the call fails. 72 | bool allowFailure; 73 | // Data to call on the target contract. 74 | bytes callData; 75 | } 76 | 77 | struct Result { 78 | // True if the call succeeded, false otherwise. 79 | bool success; 80 | // Return data if the call succeeded, or revert data if the call reverted. 81 | bytes returnData; 82 | } 83 | 84 | /// @notice Aggregate calls, ensuring each returns success if required 85 | /// @param calls An array of Call3 structs 86 | /// @return returnData An array of Result structs 87 | function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData); 88 | ``` 89 | 90 | To obtain the block number or timestamp of the block the calls were executed in with your return data, simply add a call where the `target` is the `Multicall3` contract itself, and the `callData` is the [`getBlockNumber`](./src/Multicall3.sol#L170) or [`getCurrentBlockTimestamp`](./src/Multicall3.sol#L190) method. 91 | 92 | There are a number of other methods to return block properties, including: 93 | 94 | - [`getBlockHash`](./src/Multicall3.sol#L165): Returns the block hash for the given block number. 95 | - [`getBlockNumber`](./src/Multicall3.sol#L170): Returns the current block's number. 96 | - [`getCurrentBlockCoinbase`](./src/Multicall3.sol#L175): Returns the current block's coinbase. 97 | - [`getCurrentBlockDifficulty`](./src/Multicall3.sol#L180): Returns the current block's difficulty for Proof-of-Work chains or the latest RANDAO value for Proof-of-Stake chains. See [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399) to learn more about this. 98 | - [`getCurrentBlockGasLimit`](./src/Multicall3.sol#L185): Returns the current block's gas limit. 99 | - [`getCurrentBlockTimestamp`](./src/Multicall3.sol#L190): Returns the current block's timestamp. 100 | - [`getEthBalance`](./src/Multicall3.sol#L195): Returns the ETH (or native token) balance of the given address. 101 | - [`getLastBlockHash`](./src/Multicall3.sol#L200): Returns the block hash of the previous block. 102 | - [`getBasefee`](./src/Multicall3.sol#L208): Returns the base fee of the given block. This will revert if the BASEFEE opcode is not supported on the given chain. See [EIP-1599](https://eips.ethereum.org/EIPS/eip-1559) to learn more about this. 103 | - [`getChainId`](./src/Multicall3.sol#L213): Returns the chain ID. 104 | 105 | If you need to send less calldata as part of your multicall and can tolerate less granularity of specifying which calls fail, you can check out the other aggregation methods: 106 | 107 | - [`aggregate3Value`](./src/Multicall3.sol#L129): Similar to `aggregate3`, but also lets you send values with calls. 108 | - [`aggregate`](./src/Multicall3.sol#L41): Returns a tuple of `(uint256 blockNumber, bytes[] returnData)` and reverts if any call fails. 109 | - [`blockAndAggregate`](./src/Multicall3.sol#L91): Similar to `aggregate`, but also returns the block number and block hash. 110 | - [`tryAggregate`](./src/Multicall3.sol#L60): Takes a `bool` value indicating whether success is required for all calls, and returns a tuple of `(bool success, bytes[] returnData)[]`. 111 | - [`tryBlockAndAggregate`](./src/Multicall3.sol#L79): Similar to `tryAggregate`, but also returns the block number and block hash. 112 | 113 | _Note that the above tuples are represented as structs in the code, but are shown above as tuples for brevity._ 114 | 115 | ### Batch Contract Writes 116 | 117 | _If using Multicall3 for this purpose, be aware it is unaudited, so use at your own risk._ 118 | _However, because it is a stateless contract, it should be safe when used correctly—**it should never hold your funds after a transaction ends, and you should never approve Multicall3 to spend your tokens**_. 119 | 120 | Multicall3 can also be used to batch on-chain transactions using the methods described in the [Batch Contract Reads](#batch-contract-reads) section. 121 | 122 | When using Multicall3 for this purpose, there are **two important details you MUST understand**. 123 | 124 | 1. How `msg.sender` behaves when calling vs. delegatecalling to a contract. 125 | 2. The risks of using `msg.value` in a multicall. 126 | 127 | Before explaining both of these, let's first cover some background on how the Ethereum Virtual Machine (EVM) works. 128 | 129 | There are two types of accounts in Ethereum: Externally Owned Accounts (EOAs) and Contract Accounts. 130 | EOAs are controlled by private keys, and Contract Accounts are controlled by code. 131 | 132 | When an EOA calls a contract, the `msg.sender` value during execution of the call provides the address of that EOA. This is also true if the call was executed by a contract. 133 | The word "call" here specifically refers to the [`CALL`](https://www.evm.codes/#f1?fork=shanghai) opcode. 134 | Whenever a CALL is executed, the _context_ changes. 135 | New context means storage operations will be performed on the called contract, there is a new value (i.e. `msg.value`), and a new caller (i.e. `msg.sender`). 136 | 137 | The EVM also supports the [`DELEGATECALL`](https://www.evm.codes/#f4) opcode, which is similar to `CALL`, but different in a very important way: it _does not_ change the context of the call. 138 | This means the contract being delegatecalled will see the same `msg.sender`, the same `msg.value`, and operate on the same storage as the calling contract. This is very powerful, but can also be dangerous. 139 | 140 | It's important to note that you cannot delegatecall from an EOA—an EOA can only call a contract, not delegatecall it. 141 | 142 | Now that we understand the difference between `CALL` and `DELEGATECALL`, let's see how this applies to `msg.sender` and `msg.value` concerns. 143 | We know that we can either `CALL` or `DELEGATECALL` to a contract, and that `msg.sender` will be different depending on which opcode we use. 144 | 145 | Because you cannot delegatecall from an EOA, this significantly reduces the benefit of calling Multicall3 from an EOA—any calls the Multicall3 executes will have the MultiCall3 address as the `msg.sender`. 146 | **This means you should only call Multicall3 from an EOA if the `msg.sender` does not matter.** 147 | 148 | If you are using a contract wallet or executing a call to Multicall3 from another contract, you can either CALL or DELEGATECALL. 149 | Calls will behave the same as described above for the EOA case, and delegatecalls will preserve the context. 150 | This means if you delegatecall to Multicall3 from a contract, the `msg.sender` of the calls executed by Multicall3 will be that contract. 151 | This can be very useful, and is how the Gnosis Safe [Transaction Builder](https://help.safe.global/en/articles/40841-transaction-builder) works to batch calls from a Safe. 152 | 153 | Similarly, because `msg.value` does not change with a delegatecall, you must be careful relying on `msg.value` within a multicall. 154 | 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/). 155 | 156 | ## Deployments and ABI 157 | 158 | ### Existing Deployments 159 | 160 | Multicall3 is deployed on over 100 chains at `0xcA11bde05977b3631167028862bE2a173976CA11`[^2]. 161 | A sortable, searchable list of all chains it's deployed on can be found at https://multicall3.com/deployments. 162 | 163 | The ABI can be found on https://multicall3.com/abi, where it can be downloaded or copied to the clipboard in various formats, including: 164 | 165 | - Solidity interface. 166 | - JSON ABI, prettified. 167 | - JSON ABI, minified. 168 | - ethers.js human readable ABI. 169 | - viem human readable ABI. 170 | 171 | Alternatively, you can: 172 | 173 | - Download the ABI from the [releases](https://github.com/mds1/multicall/releases) page. 174 | - Copy the ABI from [Etherscan](https://etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code). 175 | - Install [Foundry](https://github.com/gakonst/foundry/) and run `cast interface 0xcA11bde05977b3631167028862bE2a173976CA11`. 176 | 177 | ### New Deployments 178 | 179 | There are two ways to get Multicall3 deployed to a chain: 180 | 181 | 1. Deploy it yourself using a pre-signed transaction. Details on how to do this are in the below paragraph. 182 | 2. Place Multicall3 as a predeploy on your chain. If you are a fork of the OP Stack, Multicall3 is a [preinstall](https://specs.optimism.io/protocol/preinstalls.html) and will be present on all new chains, so deployment is not necessary. 183 | 184 | > [!WARNING] 185 | > Before using the signed transaction, you **MUST** make sure the chain's gas metering is equivalent to the EVM's. 186 | > 187 | > The pre-signed transaction has a gas limit of 1,000,000 gas, so if the chain will require more than 1M gas to deploy the transaction will revert and we will be unable to deploy Multicall3 at that address. If that happens, the only way to get Multicall3 at the expected address is for the chain to place the contract there as a predeploy. 188 | > 189 | > If you are unsure how to verify this, you can either use the `eth_estimateGas` RPC method or simply deploy the Multicall3 contract from another account and see how much gas deployment used. EVM chains should require exactly 872,776 gas to deploy Multicall3. 190 | > 191 | > Arbitrum chains are well-known chains that cannot be deployed using the pre-signed transaction. See [this](https://arbiscan.io/tx/0x211f6689adbb0f3fba7392e899d23bde029cef532cbd0ae900920cc09f7d1f32) deployment on Arbitrum One that required a gas limit of 14,345,935 gas—well above the 1,000,000 gas limit of the signed transaction. 192 | 193 | It's recommended to test sending the transaction on a local network——such as an anvil instance forked from the chain—to verify it works as expected before deploying to a production network. 194 | You can see an example of a successful deployment using the signed transaction on Base [here](https://basescan.org/tx/0x07471adfe8f4ec553c1199f495be97fc8be8e0626ae307281c22534460184ed1). 195 | 196 | After deploying, **please open a PR to update the `deployments.json` file with the new deployment**, this way other users can easily know that it's deployed. 197 | 198 | Below is the signed transaction. 199 | It has a gas limit of 1,000,000 gas and a gas price of 100 gwei, so before deploying you'll need to send at least 0.1 ETH to the deployer deployer address (`0x05f32b3cc3888453ff71b01135b34ff8e41263f2`). 200 | It is recommended to send ETH to this account as late as possible to reduce the risk of accidentally burning the nonce. 201 | 202 | ```text 203 | 0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086 204 | ``` 205 | 206 | To deploy with [cast](https://book.getfoundry.sh/cast/): 207 | 208 | ```sh 209 | # `TX` is the signed transaction above. 210 | # `RPC_URL` is the RPC URL of the chain you want to deploy to. 211 | cast publish $TX --rpc-url $RPC_URL 212 | ``` 213 | 214 | ### Contract Verification 215 | 216 | To verify the code on a block explorer, use the following parameters: 217 | 218 | - Paste in the code from [Multicall3.sol](./src/Multicall3.sol) 219 | - Select solidity 0.8.12 220 | - Optimizer enabled with 10000000 runs 221 | - No constructor arguments 222 | 223 | ## Security 224 | 225 | **This contract is unaudited.** 226 | 227 | For on-chain transactions using Multicall3, or for contracts inheriting from Multicall3: 228 | 229 | - **Ensure it NEVER holds funds after a transaction ends**. Any ETH, tokens, or other funds held by this contract can be stolen. There are bots that monitor for this and they will immediately steal any funds they find. 230 | - Never approve Multicall3 to spend your tokens. If you do, anyone can steal your tokens. There are likely bots that monitor for this as well. 231 | - It is not recommended to inherit from this contract if your contract will hold funds. But if you must, be sure you know what you're doing and protect all state changing methods with an `onlyOwner` modifier or similar so funds cannot be stolen. 232 | - Be sure you understand CALL vs. DELEGATECALL behavior depending on your use case. See the [Batch Contract Writes](#batch-contract-writes) section for more details. 233 | 234 | For off-chain reads the worst case scenario is you get back incorrect data, but this should not occur for properly formatted calls. 235 | 236 | ## Development 237 | 238 | This repo uses [Foundry](https://github.com/gakonst/foundry) for development and testing 239 | and git submodules for dependency management. 240 | 241 | Clone the repo and run `forge test` to run tests. 242 | Forge will automatically install any missing dependencies. 243 | 244 | The repo for https://multicall3.com can be found [here](https://github.com/mds1/multicall3-frontend). 245 | 246 | ## Gas Golfing Tricks and Optimizations 247 | 248 | Below is a list of some of the optimizations used by Multicall3's `aggregate3` and `aggregate3Value` methods[^3]: 249 | 250 | - In `for` loops, array length is cached to avoid reading the length on each loop iteration. 251 | - In `for` loops, the counter is incremented within an `unchecked` block. 252 | - In `for` loops, the counter is incremented with the prefix increment (`++i`) instead of a postfix increment (`i++`). 253 | - All revert strings fit within a single 32 byte slot. 254 | - Function parameters use `calldata` instead of `memory`. 255 | - Instead of requiring `call.allowFailure || result.success`, we use assembly's `or()` instruction to [avoid](https://twitter.com/transmissions11/status/1501645922266091524) a `JUMPI` and `iszero()` since it's cheaper to evaluate both conditions. 256 | - Methods are given a `payable` modifier which removes a check that `msg.value == 0` when calling a method. 257 | - Calldata and memory pointers are used to cache values so they are not read multiple times within a loop. 258 | - No block data (e.g. block number, hash, or timestamp) is returned by default, and is instead left up to the caller. 259 | - The value accumulator in `aggregate3Value` is within an `unchecked` block. 260 | 261 | Read more about Solidity gas optimization tips: 262 | 263 | - [Generic writeup about common gas optimizations, etc.](https://gist.github.com/hrkrshnn/ee8fabd532058307229d65dcd5836ddc) by [Harikrishnan Mulackal](https://twitter.com/_hrkrshnn) 264 | - [Yul (and Some Solidity) Optimizations and Tricks](https://hackmd.io/@gn56kcRBQc6mOi7LCgbv1g/rJez8O8st) by [ControlCplusControlV](https://twitter.com/controlcthenv) 265 | 266 | [^1]: [`Multicall`](./src/Multicall.sol) is the original contract, and [`Multicall2`](./src/Multicall2.sol) added support for handling failed calls in a multicall. [`Multicall3`](./src/Multicall3.sol) is recommended over these because it's backwards-compatible with both, cheaper to use, adds new methods, and is deployed on more chains. You can read more about the original contracts and their deployments in the [makerdao/multicall](https://github.com/makerdao/multicall) repo. 267 | [^2]: There are a few unofficial deployments at other addresses for chains that compute addresses differently, which can also be found at 268 | [^3]: Some of these tricks are outdated with newer Solidity versions and via-ir. Be sure to benchmark your code before assuming the changes are guaranteed to reduce gas usage. 269 | -------------------------------------------------------------------------------- /deployments.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Mainnet", 4 | "chainId": 1, 5 | "url": "https://etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 6 | }, 7 | { 8 | "name": "Kovan", 9 | "chainId": 42, 10 | "url": "https://kovan.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 11 | }, 12 | { 13 | "name": "Rinkeby", 14 | "chainId": 4, 15 | "url": "https://rinkeby.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 16 | }, 17 | { 18 | "name": "Görli", 19 | "chainId": 5, 20 | "url": "https://goerli.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 21 | }, 22 | { 23 | "name": "Ropsten", 24 | "chainId": 3, 25 | "url": "https://ropsten.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 26 | }, 27 | { 28 | "name": "Sepolia", 29 | "chainId": 11155111, 30 | "url": "https://sepolia.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 31 | }, 32 | { 33 | "name": "Holesky", 34 | "chainId": 17000, 35 | "url": "https://holesky.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 36 | }, 37 | { 38 | "name": "Story", 39 | "chainId": 1514, 40 | "url": "https://www.storyscan.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 41 | }, 42 | { 43 | "name": "Story Aeneid Testnet", 44 | "chainId": 1315, 45 | "url": "https://aeneid.storyscan.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 46 | }, 47 | { 48 | "name": "Xterio Chain", 49 | "chainId": 112358, 50 | "url": "https://xterscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 51 | }, 52 | { 53 | "name": "Xterio Testnet", 54 | "chainId": 1637450, 55 | "url": "https://testnet.xterscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 56 | }, 57 | { 58 | "name": "Optimism", 59 | "chainId": 10, 60 | "url": "https://optimistic.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 61 | }, 62 | { 63 | "name": "Optimism Kovan", 64 | "chainId": 69, 65 | "url": "https://kovan-optimistic.etherscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 66 | }, 67 | { 68 | "name": "Optimism Görli", 69 | "chainId": 420, 70 | "url": "https://blockscout.com/optimism/goerli/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 71 | }, 72 | { 73 | "name": "Optimism Sepolia", 74 | "chainId": 11155420, 75 | "url": "https://optimism-sepolia.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 76 | }, 77 | { 78 | "name": "Arbitrum", 79 | "chainId": 42161, 80 | "url": "https://arbiscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 81 | }, 82 | { 83 | "name": "Arbitrum Nova", 84 | "chainId": 42170, 85 | "url": "https://nova.arbiscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 86 | }, 87 | { 88 | "name": "Arbitrum Görli", 89 | "chainId": 421613, 90 | "url": "https://goerli-rollup-explorer.arbitrum.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 91 | }, 92 | { 93 | "name": "Arbitrum Sepolia", 94 | "chainId": 421614, 95 | "url": "https://sepolia-explorer.arbitrum.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 96 | }, 97 | { 98 | "name": "Arbitrum Rinkeby", 99 | "chainId": 421611, 100 | "url": "https://testnet.arbiscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 101 | }, 102 | { 103 | "name": "Stylus Testnet", 104 | "chainId": 23011913, 105 | "url": "https://stylus-testnet-explorer.arbitrum.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 106 | }, 107 | { 108 | "name": "Polygon", 109 | "chainId": 137, 110 | "url": "https://polygonscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 111 | }, 112 | { 113 | "name": "Mumbai", 114 | "chainId": 80001, 115 | "url": "https://mumbai.polygonscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 116 | }, 117 | { 118 | "name": "Amoy", 119 | "chainId": 80002, 120 | "url": "https://amoy.polygonscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 121 | }, 122 | { 123 | "name": "Polygon zkEVM", 124 | "chainId": 1101, 125 | "url": "https://zkevm.polygonscan.com/address/0xca11bde05977b3631167028862be2a173976ca11#code" 126 | }, 127 | { 128 | "name": "Polygon zkEVM Testnet", 129 | "chainId": 1442, 130 | "url": "https://testnet-zkevm.polygonscan.com/address/0xca11bde05977b3631167028862be2a173976ca11#code" 131 | }, 132 | { 133 | "name": "Cardona zkEVM Testnet", 134 | "chainId": 2442, 135 | "url": "https://cardona-zkevm.polygonscan.com/address/0xca11bde05977b3631167028862be2a173976ca11#code" 136 | }, 137 | { 138 | "name": "Gnosis Chain (xDai)", 139 | "chainId": 100, 140 | "url": "https://blockscout.com/xdai/mainnet/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 141 | }, 142 | { 143 | "name": "Chiado (Gnosis Chain Testnet)", 144 | "chainId": 10200, 145 | "url": "https://blockscout.chiadochain.net/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 146 | }, 147 | { 148 | "name": "Avalanche", 149 | "chainId": 43114, 150 | "url": "https://snowtrace.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 151 | }, 152 | { 153 | "name": "Avalanche Fuji", 154 | "chainId": 43113, 155 | "url": "https://testnet.snowtrace.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 156 | }, 157 | { 158 | "name": "Fantom Testnet", 159 | "chainId": 4002, 160 | "url": "https://testnet.ftmscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 161 | }, 162 | { 163 | "name": "Fantom Opera", 164 | "chainId": 250, 165 | "url": "https://ftmscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 166 | }, 167 | { 168 | "name": "Fantom Sonic", 169 | "chainId": 64240, 170 | "url": "https://public-sonic.fantom.network/address/0xca11bde05977b3631167028862be2a173976ca11" 171 | }, 172 | { 173 | "name": "Sonic Network", 174 | "chainId": 146, 175 | "url": "https://sonicscan.org/address/0xca11bde05977b3631167028862be2a173976ca11" 176 | }, 177 | { 178 | "name": "BNB Smart Chain", 179 | "chainId": 56, 180 | "url": "https://bscscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 181 | }, 182 | { 183 | "name": "BNB Smart Chain Testnet", 184 | "chainId": 97, 185 | "url": "https://testnet.bscscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 186 | }, 187 | { 188 | "name": "opBNB Testnet", 189 | "chainId": 5611, 190 | "url": "https://opbnbscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?p=1&tab=Contract" 191 | }, 192 | { 193 | "name": "opBNB Mainnet", 194 | "chainId": 204, 195 | "url": "https://mainnet.opbnbscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?p=1&tab=Contract" 196 | }, 197 | { 198 | "name": "Moonbeam", 199 | "chainId": 1284, 200 | "url": "https://moonscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 201 | }, 202 | { 203 | "name": "Moonriver", 204 | "chainId": 1285, 205 | "url": "https://moonriver.moonscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 206 | }, 207 | { 208 | "name": "Moonbase Alpha Testnet", 209 | "chainId": 1287, 210 | "url": "https://moonbase.moonscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 211 | }, 212 | { 213 | "name": "Palm", 214 | "chainId": 11297108109, 215 | "url": "https://palm.chainlens.com/contracts/0xca11bde05977b3631167028862be2a173976ca11/sources" 216 | }, 217 | { 218 | "name": "Palm Testnet", 219 | "chainId": 11297108099, 220 | "url": "https://testnet.palm.chainlens.com/contracts/0xca11bde05977b3631167028862be2a173976ca11/sources" 221 | }, 222 | { 223 | "name": "Harmony", 224 | "chainId": 1666600000, 225 | "url": "https://explorer.harmony.one/address/0xcA11bde05977b3631167028862bE2a173976CA11?activeTab=7" 226 | }, 227 | { 228 | "name": "Cronos", 229 | "chainId": 25, 230 | "url": "https://cronoscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 231 | }, 232 | { 233 | "name": "Cronos Testnet", 234 | "chainId": 338, 235 | "url": "https://cronos.org/explorer/testnet3/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 236 | }, 237 | { 238 | "name": "Reya Cronos", 239 | "chainId": 89346162, 240 | "url": "https://reya-cronos.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 241 | }, 242 | { 243 | "name": "Reya Network", 244 | "chainId": 1729, 245 | "url": "https://explorer.reya.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 246 | }, 247 | { 248 | "name": "Fuse", 249 | "chainId": 122, 250 | "url": "https://explorer.fuse.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 251 | }, 252 | { 253 | "name": "Flare Mainnet", 254 | "chainId": 14, 255 | "url": "https://flare-explorer.flare.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 256 | }, 257 | { 258 | "name": "Songbird Canary Network", 259 | "chainId": 19, 260 | "url": "https://songbird-explorer.flare.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 261 | }, 262 | { 263 | "name": "Coston Testnet", 264 | "chainId": 16, 265 | "url": "https://coston-explorer.flare.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 266 | }, 267 | { 268 | "name": "Coston2 Testnet", 269 | "chainId": 114, 270 | "url": "https://coston2-explorer.flare.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 271 | }, 272 | { 273 | "name": "Boba", 274 | "chainId": 288, 275 | "url": "https://blockexplorer.boba.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 276 | }, 277 | { 278 | "name": "Aurora", 279 | "chainId": 1313161554, 280 | "url": "https://explorer.mainnet.aurora.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11" 281 | }, 282 | { 283 | "name": "Astar", 284 | "chainId": 592, 285 | "url": "https://blockscout.com/astar/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 286 | }, 287 | { 288 | "name": "Astar zKyoto Testnet", 289 | "chainId": 6038361, 290 | "url": "https://zkyoto.explorer.startale.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 291 | }, 292 | { 293 | "name": "Astar zkEVM", 294 | "chainId": 3776, 295 | "url": "https://astar-zkevm.explorer.startale.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 296 | }, 297 | { 298 | "name": "OKC", 299 | "chainId": 66, 300 | "url": "https://www.oklink.com/en/okc/address/0xcA11bde05977b3631167028862bE2a173976CA11" 301 | }, 302 | { 303 | "name": "Heco Chain", 304 | "chainId": 128, 305 | "url": "https://hecoinfo.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 306 | }, 307 | { 308 | "name": "Metis Andromeda", 309 | "chainId": 1088, 310 | "url": "https://andromeda-explorer.metis.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 311 | }, 312 | { 313 | "name": "Metis Goerli", 314 | "chainId": 599, 315 | "url": "https://goerli.explorer.metisdevops.link/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 316 | }, 317 | { 318 | "name": "Metis Sepolia", 319 | "chainId": 59902, 320 | "url": "https://sepolia-explorer.metisdevops.link/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 321 | }, 322 | { 323 | "name": "Over Protocol", 324 | "chainId": 54176, 325 | "url": "https://scan.over.network/address/0x03657CDcDA1523C073b5e09c37dd199E6fBD1b99" 326 | }, 327 | { 328 | "name": "Over Protocol Dolphin Testnet", 329 | "chainId": 541764, 330 | "url": "https://dolphin-scan.over.network/address/0x03657CDcDA1523C073b5e09c37dd199E6fBD1b99" 331 | }, 332 | { 333 | "name": "RSK", 334 | "chainId": 30, 335 | "url": "https://explorer.rsk.co/address/0xcA11bde05977b3631167028862bE2a173976CA11" 336 | }, 337 | { 338 | "name": "RSK Testnet", 339 | "chainId": 31, 340 | "url": "https://explorer.testnet.rsk.co/address/0xcA11bde05977b3631167028862bE2a173976CA11" 341 | }, 342 | { 343 | "name": "Evmos", 344 | "chainId": 9001, 345 | "url": "https://evm.evmos.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 346 | }, 347 | { 348 | "name": "Evmos Testnet", 349 | "chainId": 9000, 350 | "url": "https://evm.evmos.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 351 | }, 352 | { 353 | "name": "Thundercore", 354 | "chainId": 108, 355 | "url": "https://viewblock.io/thundercore/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=code" 356 | }, 357 | { 358 | "name": "Thundercore Testnet", 359 | "chainId": 18, 360 | "url": "https://explorer-testnet.thundercore.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 361 | }, 362 | { 363 | "name": "Oasis", 364 | "chainId": 42262, 365 | "url": "https://explorer.emerald.oasis.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 366 | }, 367 | { 368 | "name": "Oasis Sapphire", 369 | "chainId": 23294, 370 | "url": "https://explorer.sapphire.oasis.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 371 | }, 372 | { 373 | "name": "Celo", 374 | "chainId": 42220, 375 | "url": "https://explorer.celo.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 376 | }, 377 | { 378 | "name": "Celo Alfajores Testnet", 379 | "chainId": 44787, 380 | "url": "https://explorer.celo.org/alfajores/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 381 | }, 382 | { 383 | "name": "Godwoken", 384 | "chainId": 71402, 385 | "url": "https://v1.gwscan.com/account/0xcA11bde05977b3631167028862bE2a173976CA11" 386 | }, 387 | { 388 | "name": "Godwoken Testnet", 389 | "chainId": 71401, 390 | "url": "https://gw-explorer.nervosdao.community/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 391 | }, 392 | { 393 | "name": "Klaytn", 394 | "chainId": 8217, 395 | "url": "https://scope.klaytn.com/account/0xcA11bde05977b3631167028862bE2a173976CA11" 396 | }, 397 | { 398 | "name": "Klaytn Testnet (Baobab)", 399 | "chainId": 1001, 400 | "url": "https://baobab.klaytnscope.com/account/0xca11bde05977b3631167028862be2a173976ca11?tabId=contractCode" 401 | }, 402 | { 403 | "name": "Milkomeda", 404 | "chainId": 2001, 405 | "url": "https://explorer-mainnet-cardano-evm.c1.milkomeda.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 406 | }, 407 | { 408 | "name": "KCC", 409 | "chainId": 321, 410 | "url": "https://explorer.kcc.io/en/address/0xcA11bde05977b3631167028862bE2a173976CA11" 411 | }, 412 | { 413 | "name": "Velas", 414 | "chainId": 106, 415 | "url": "https://evmexplorer.velas.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 416 | }, 417 | { 418 | "name": "Telos", 419 | "chainId": 40, 420 | "url": "https://www.teloscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#contract" 421 | }, 422 | { 423 | "name": "Step Network", 424 | "chainId": 1234, 425 | "url": "https://stepscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 426 | }, 427 | { 428 | "name": "Canto", 429 | "chainId": 7700, 430 | "url": "https://tuber.build/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 431 | }, 432 | { 433 | "name": "Canto Testnet", 434 | "chainId": 7701, 435 | "url": "https://testnet.tuber.build/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 436 | }, 437 | { 438 | "name": "Iotex", 439 | "chainId": 4689, 440 | "url": "https://iotexscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#transactions" 441 | }, 442 | { 443 | "name": "Bitgert", 444 | "chainId": 32520, 445 | "url": "https://brisescan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 446 | }, 447 | { 448 | "name": "Kava", 449 | "chainId": 2222, 450 | "url": "https://explorer.kava.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 451 | }, 452 | { 453 | "name": "Mantle Sepolia Testnet", 454 | "chainId": 5003, 455 | "url": "https://explorer.sepolia.mantle.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 456 | }, 457 | { 458 | "name": "Mantle Testnet", 459 | "chainId": 5001, 460 | "url": "https://explorer.testnet.mantle.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 461 | }, 462 | { 463 | "name": "Mantle", 464 | "chainId": 5000, 465 | "url": "https://explorer.mantle.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 466 | }, 467 | { 468 | "name": "Shardeum Sphinx", 469 | "chainId": 8082, 470 | "url": "https://explorer.testnet.mantle.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 471 | }, 472 | { 473 | "name": "Base Testnet (Goerli)", 474 | "chainId": 84531, 475 | "url": "https://goerli.basescan.org/address/0xca11bde05977b3631167028862be2a173976ca11#code" 476 | }, 477 | { 478 | "name": "Base Testnet (Sepolia)", 479 | "chainId": 84532, 480 | "url": "https://base-sepolia.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 481 | }, 482 | { 483 | "name": "Base", 484 | "chainId": 8453, 485 | "url": "https://basescan.org/address/0xca11bde05977b3631167028862be2a173976ca11#code" 486 | }, 487 | { 488 | "name": "Kroma Testnet (Sepolia)", 489 | "chainId": 2358, 490 | "url": "https://sepolia.kromascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 491 | }, 492 | { 493 | "name": "Kroma", 494 | "chainId": 255, 495 | "url": "https://kromascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 496 | }, 497 | { 498 | "name": "DeFiChain EVM Mainnet", 499 | "chainId": 1130, 500 | "url": "https://meta.defiscan.live/address/0xcA11bde05977b3631167028862bE2a173976CA11" 501 | }, 502 | { 503 | "name": "DeFiChain EVM Testnet", 504 | "chainId": 1131, 505 | "url": "https://meta.defiscan.live/address/0xcA11bde05977b3631167028862bE2a173976CA11?network=TestNet" 506 | }, 507 | { 508 | "name": "Defi Oracle Meta Mainnet", 509 | "chainId": 138, 510 | "url": "https://blockscout.defi-oracle.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 511 | }, 512 | { 513 | "name": "DFK Chain Test", 514 | "chainId": 335, 515 | "url": "https://subnets-test.avax.network/defi-kingdoms/address/0xcA11bde05977b3631167028862bE2a173976CA11" 516 | }, 517 | { 518 | "name": "DFK Chain", 519 | "chainId": 53935, 520 | "url": "https://subnets.avax.network/defi-kingdoms/address/0xcA11bde05977b3631167028862bE2a173976CA11" 521 | }, 522 | { 523 | "name": "Neon EVM DevNet", 524 | "chainId": 245022926, 525 | "url": "https://devnet.neonscan.org/address/0xcA11bde05977b3631167028862bE2a173976CA11#contract" 526 | }, 527 | { 528 | "name": "Linea Sepolia Testnet", 529 | "chainId": 59141, 530 | "url": "https://sepolia.lineascan.build/address/0xca11bde05977b3631167028862be2a173976ca11#code" 531 | }, 532 | { 533 | "name": "Linea Goerli Testnet", 534 | "chainId": 59140, 535 | "url": "https://explorer.goerli.linea.build/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 536 | }, 537 | { 538 | "name": "Linea Mainnet", 539 | "chainId": 59144, 540 | "url": "https://lineascan.build/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 541 | }, 542 | { 543 | "name": "Hashbit", 544 | "chainId": 11119, 545 | "url": "https://explorer.hashbit.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 546 | }, 547 | { 548 | "name": "Syscoin", 549 | "chainId": 57, 550 | "url": "https://explorer.syscoin.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 551 | }, 552 | { 553 | "name": "Syscoin Rollux Mainnet", 554 | "chainId": 570, 555 | "url": "https://explorer.rollux.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 556 | }, 557 | { 558 | "name": "Syscoin Tannebaum Testnet", 559 | "chainId": 5700, 560 | "url": "https://tanenbaum.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 561 | }, 562 | { 563 | "name": "Syscoin Tannebaum Rollux", 564 | "chainId": 57000, 565 | "url": "https://rollux.tanenbaum.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 566 | }, 567 | { 568 | "name": "Pulsechain v4 Testnet", 569 | "chainId": 943, 570 | "url": "https://scan.v4.testnet.pulsechain.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 571 | }, 572 | { 573 | "name": "Pulsechain Mainnet", 574 | "chainId": 369, 575 | "url": "https://scan.pulsechain.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 576 | }, 577 | { 578 | "name": "Zora Goerli Testnet", 579 | "chainId": 999, 580 | "url": "https://testnet.explorer.zora.co/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 581 | }, 582 | { 583 | "name": "Zora", 584 | "chainId": 7777777, 585 | "url": "https://explorer.zora.co/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 586 | }, 587 | { 588 | "name": "Zora Sepolia Testnet", 589 | "chainId": 999999999, 590 | "url": "https://sepolia.explorer.zora.energy/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 591 | }, 592 | { 593 | "name": "Darwinia Crab Network", 594 | "chainId": 44, 595 | "url": "https://crab.subscan.io/account/0xca11bde05977b3631167028862be2a173976ca11" 596 | }, 597 | { 598 | "name": "Darwinia Network", 599 | "chainId": 46, 600 | "url": "https://darwinia.subscan.io/account/0xca11bde05977b3631167028862be2a173976ca11" 601 | }, 602 | { 603 | "name": "Chain Verse Mainnet", 604 | "chainId": 5555, 605 | "url": "https://explorer.chainverse.info/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 606 | }, 607 | { 608 | "name": "Scroll Alpha Testnet", 609 | "chainId": 534353, 610 | "url": "https://blockscout.scroll.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 611 | }, 612 | { 613 | "name": "Scroll Sepolia Testnet", 614 | "chainId": 534351, 615 | "url": "https://sepolia.scrollscan.dev/address/0xca11bde05977b3631167028862be2a173976ca11#code" 616 | }, 617 | { 618 | "name": "Scroll", 619 | "chainId": 534352, 620 | "url": "https://scrollscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 621 | }, 622 | { 623 | "name": "Xodex", 624 | "chainId": 2415, 625 | "url": "https://explorer.xo-dex.com/contracts/0xcA11bde05977b3631167028862bE2a173976CA11" 626 | }, 627 | { 628 | "name": "EOS EVM Testnet", 629 | "chainId": 15557, 630 | "url": "https://explorer.testnet.evm.eosnetwork.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 631 | }, 632 | { 633 | "name": "EOS EVM", 634 | "chainId": 17777, 635 | "url": "https://explorer.evm.eosnetwork.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 636 | }, 637 | { 638 | "name": "Crossbell", 639 | "chainId": 3737, 640 | "url": "https://scan.crossbell.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 641 | }, 642 | { 643 | "name": "Dogechain", 644 | "chainId": 2000, 645 | "url": "https://explorer.dogechain.dog/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 646 | }, 647 | { 648 | "name": "MEVerse Chain Testnet", 649 | "chainId": 4759, 650 | "url": "https://testnet.meversescan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 651 | }, 652 | { 653 | "name": "MEVerse Chain Mainnet", 654 | "chainId": 7518, 655 | "url": "https://meversescan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 656 | }, 657 | { 658 | "name": "SKALE Calypso Testnet", 659 | "chainId": 974399131, 660 | "url": "https://giant-half-dual-testnet.explorer.testnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 661 | }, 662 | { 663 | "name": "SKALE Europa Testnet", 664 | "chainId": 1444673419, 665 | "url": "https://juicy-low-small-testnet.explorer.testnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 666 | }, 667 | { 668 | "name": "SKALE Nebula Testnet", 669 | "chainId": 37084624, 670 | "url": "https://lanky-ill-funny-testnet.explorer.testnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 671 | }, 672 | { 673 | "name": "SKALE Titan Testnet", 674 | "chainId": 1020352220, 675 | "url": "https://aware-fake-trim-testnet.explorer.testnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 676 | }, 677 | { 678 | "name": "SKALE Calypso Hub", 679 | "chainId": 1564830818, 680 | "url": "https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 681 | }, 682 | { 683 | "name": "SKALE Europa Liquidity Hub", 684 | "chainId": 2046399126, 685 | "url": "https://elated-tan-skat.explorer.mainnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 686 | }, 687 | { 688 | "name": "SKALE Nebula Gaming Hub", 689 | "chainId": 1482601649, 690 | "url": "https://green-giddy-denebola.explorer.mainnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 691 | }, 692 | { 693 | "name": "SKALE Titan AI Hub", 694 | "chainId": 1350216234, 695 | "url": "https://parallel-stormy-spica.explorer.mainnet.skalenodes.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 696 | }, 697 | { 698 | "name": "Ronin Saigon Testnet", 699 | "chainId": 2021, 700 | "url": "https://saigon-app.roninchain.com/address/ronin:ca11bde05977b3631167028862be2a173976ca11?t=contract" 701 | }, 702 | { 703 | "name": "Ronin Mainnet", 704 | "chainId": 2020, 705 | "url": "https://app.roninchain.com/address/ronin:ca11bde05977b3631167028862be2a173976ca11" 706 | }, 707 | { 708 | "name": "Qitmeer Testnet", 709 | "chainId": 8131, 710 | "url": "https://testnet-qng.qitmeer.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 711 | }, 712 | { 713 | "name": "Qitmeer QNG Mainnet", 714 | "chainId": 813, 715 | "url": "https://qng.meerscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 716 | }, 717 | { 718 | "name": "Q Testnet", 719 | "chainId": 35443, 720 | "url": "https://explorer.qtestnet.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 721 | }, 722 | { 723 | "name": "Q Devnet", 724 | "chainId": 35442, 725 | "url": "https://explorer.qdevnet.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 726 | }, 727 | { 728 | "name": "Q Mainnet", 729 | "chainId": 35441, 730 | "url": "https://explorer.q.org/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 731 | }, 732 | { 733 | "name": "Neon Mainnet", 734 | "chainId": 245022934, 735 | "url": "https://neonscan.org/address/0xca11bde05977b3631167028862be2a173976ca11#contract" 736 | }, 737 | { 738 | "name": "LUKSO Testnet", 739 | "chainId": 4201, 740 | "url": "https://explorer.execution.testnet.lukso.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 741 | }, 742 | { 743 | "name": "LUKSO Mainnet", 744 | "chainId": 42, 745 | "url": "https://explorer.execution.mainnet.lukso.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 746 | }, 747 | { 748 | "name": "Edgeware EdgeEVM", 749 | "chainId": 2021, 750 | "url": "https://edgscan.live/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts" 751 | }, 752 | { 753 | "name": "Meter Testnet", 754 | "chainId": 83, 755 | "url": "https://scan-warringstakes.meter.io/address/0xca11bde05977b3631167028862be2a173976ca11?tab=0&p=1" 756 | }, 757 | { 758 | "name": "Meter", 759 | "chainId": 82, 760 | "url": "https://scan.meter.io/address/0xca11bde05977b3631167028862be2a173976ca11?tab=0&p=1" 761 | }, 762 | { 763 | "name": "Sepolia PGN (Public Goods Network) Testnet", 764 | "chainId": 58008, 765 | "url": "https://explorer.sepolia.publicgoods.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 766 | }, 767 | { 768 | "name": "PGN (Public Goods Network)", 769 | "chainId": 424, 770 | "url": "https://explorer.publicgoods.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 771 | }, 772 | { 773 | "name": "ShimmerEVM", 774 | "chainId": 148, 775 | "url": "https://explorer.evm.shimmer.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 776 | }, 777 | { 778 | "name": "Highbury EVM", 779 | "chainId": 710, 780 | "url": "https://explorer.furya.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 781 | }, 782 | { 783 | "name": "Arthera Testnet", 784 | "chainId": 10243, 785 | "url": "https://explorer-test.arthera.net/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 786 | }, 787 | { 788 | "name": "Arthera Mainnet", 789 | "chainId": 10242, 790 | "url": "https://explorer.arthera.net/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 791 | }, 792 | { 793 | "name": "Manta Pacific Mainnet", 794 | "chainId": 169, 795 | "url": "https://pacific-explorer.manta.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 796 | }, 797 | { 798 | "name": "Jolnir (Taiko Testnet)", 799 | "chainId": 167007, 800 | "url": "https://explorer.jolnir.taiko.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 801 | }, 802 | { 803 | "name": "Katla (Taiko A6 Testnet)", 804 | "chainId": 167008, 805 | "url": "https://explorer.katla.taiko.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 806 | }, 807 | { 808 | "name": "Filecoin Mainnet", 809 | "chainId": 314, 810 | "url": "https://filfox.info/en/address/0xcA11bde05977b3631167028862bE2a173976CA11" 811 | }, 812 | { 813 | "name": "Filecoin Calibration Testnet", 814 | "chainId": 314159, 815 | "url": "https://calibration.filscan.io/en/tx/0xdbfa261cd7d17bb40479a0493ad6c0fee435859e37aae73aa7e803f3122cc465/" 816 | }, 817 | { 818 | "name": "Fusion", 819 | "chainId": 32659, 820 | "url": "https://fsnscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#contract" 821 | }, 822 | { 823 | "name": "Fusion Testnet", 824 | "chainId": 46688, 825 | "url": "https://testnet.fsnscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#contract" 826 | }, 827 | { 828 | "name": "Xai Testnet", 829 | "chainId": 47279324479, 830 | "url": "https://testnet-explorer.xai-chain.net/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 831 | }, 832 | { 833 | "name": "JFIN Chain", 834 | "chainId": 3501, 835 | "url": "https://jfinscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 836 | }, 837 | { 838 | "name": "JFIN Chain Testnet", 839 | "chainId": 3502, 840 | "url": "https://testnet.jfinscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 841 | }, 842 | { 843 | "name": "Chiliz Chain", 844 | "chainId": 88888, 845 | "url": "https://scan.chiliz.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 846 | }, 847 | { 848 | "name": "Chiliz Spicy Testnet", 849 | "chainId": 88882, 850 | "url": "https://testnet.chiliscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11/contract/88882/code" 851 | }, 852 | { 853 | "name": "CORE", 854 | "chainId": 1116, 855 | "url": "https://scan.coredao.org/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 856 | }, 857 | { 858 | "name": "Core Testnet", 859 | "chainId": 1115, 860 | "url": "https://scan.test.btcs.network/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 861 | }, 862 | { 863 | "name": "Core Testnet2", 864 | "chainId": 1114, 865 | "url": "https://scan.test2.btcs.network/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 866 | }, 867 | { 868 | "name": "Ethereum Classic", 869 | "chainId": 61, 870 | "url": "https://etc.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 871 | }, 872 | { 873 | "name": "Frame Testnet", 874 | "chainId": 68840142, 875 | "url": "https://explorer.testnet.frame.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11" 876 | }, 877 | { 878 | "name": "Etherlink Mainnet", 879 | "chainId": 42793, 880 | "url": "https://explorer.etherlink.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 881 | }, 882 | { 883 | "name": "Etherlink Testnet", 884 | "chainId": 128123, 885 | "url": "https://testnet-explorer.etherlink.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 886 | }, 887 | { 888 | "name": "ZetaChain Athens 3 Testnet", 889 | "chainId": 7001, 890 | "url": "https://explorer.zetachain.com/address/0xca11bde05977b3631167028862be2a173976ca11" 891 | }, 892 | { 893 | "name": "ZetaChain ", 894 | "chainId": 7000, 895 | "url": "https://explorer.zetachain.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 896 | }, 897 | { 898 | "name": "X1 Testnet", 899 | "chainId": 195, 900 | "url": "https://www.oklink.com/x1-test/address/0xca11bde05977b3631167028862be2a173976ca11/contract" 901 | }, 902 | { 903 | "name": "Lumiterra Layer3", 904 | "chainId": 94168, 905 | "url": "https://scan.layerlumi.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 906 | }, 907 | { 908 | "name": "BitTorrent Chain Mainnet", 909 | "chainId": 199, 910 | "url": "https://bttcscan.com/address/0xca11bde05977b3631167028862be2a173976ca11#code" 911 | }, 912 | { 913 | "name": "BTT Chain Testnet", 914 | "chainId": 1029, 915 | "url": "https://testnet.bttcscan.com/address/0xca11bde05977b3631167028862be2a173976ca11" 916 | }, 917 | { 918 | "name": "Callisto Mainnet", 919 | "chainId": 820, 920 | "url": "https://explorer.callisto.network/address/0xcA11bde05977b3631167028862bE2a173976CA11/transactions" 921 | }, 922 | { 923 | "name": "Areon Network Testnet", 924 | "chainId": 462, 925 | "url": "https://areonscan.com/contracts/0xca11bde05977b3631167028862be2a173976ca11?page=0" 926 | }, 927 | { 928 | "name": "Areon Network Mainnet", 929 | "chainId": 463, 930 | "url": "https://areonscan.com/contracts/0xca11bde05977b3631167028862be2a173976ca11" 931 | }, 932 | { 933 | "name": "zkFair Mainnet", 934 | "chainId": 42766, 935 | "url": "https://scan.zkfair.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 936 | }, 937 | { 938 | "name": "Mode Mainnet", 939 | "chainId": 34443, 940 | "url": "https://explorer.mode.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 941 | }, 942 | { 943 | "name": "Blast Sepolia", 944 | "chainId": 168587773, 945 | "url": "https://testnet.blastscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contract/168587773/code" 946 | }, 947 | { 948 | "name": "Blast", 949 | "chainId": 81457, 950 | "url": "https://blastscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 951 | }, 952 | { 953 | "name": "Xai", 954 | "chainId": 660279, 955 | "url": "https://explorer.xai-chain.net/address/0xcA11bde05977b3631167028862bE2a173976CA11/contracts#address-tabs" 956 | }, 957 | { 958 | "name": "DOS Chain", 959 | "chainId": 7979, 960 | "url": "https://doscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 961 | }, 962 | { 963 | "name": "DOS Chain Testnet", 964 | "chainId": 3939, 965 | "url": "https://test.doscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 966 | }, 967 | { 968 | "name": "Tron", 969 | "chainId": 728126428, 970 | "url": "https://tronscan.org/#/contract/TEazPvZwDjDtFeJupyo7QunvnrnUjPH8ED/code", 971 | "address": "TEazPvZwDjDtFeJupyo7QunvnrnUjPH8ED" 972 | }, 973 | { 974 | "name": "zkSync Era", 975 | "chainId": 324, 976 | "url": "https://explorer.zksync.io/address/0xF9cda624FBC7e059355ce98a31693d299FACd963#contract", 977 | "address": "0xF9cda624FBC7e059355ce98a31693d299FACd963" 978 | }, 979 | { 980 | "name": "zkSync Era Goerli Testnet", 981 | "chainId": 280, 982 | "url": "https://goerli.explorer.zksync.io/address/0xF9cda624FBC7e059355ce98a31693d299FACd963#contract", 983 | "address": "0xF9cda624FBC7e059355ce98a31693d299FACd963" 984 | }, 985 | { 986 | "name": "zkSync Era Sepolia Testnet", 987 | "chainId": 300, 988 | "url": "https://sepolia.explorer.zksync.io/address/0xF9cda624FBC7e059355ce98a31693d299FACd963#contract", 989 | "address": "0xF9cda624FBC7e059355ce98a31693d299FACd963" 990 | }, 991 | { 992 | "name": "PlayFi Albireo Testnet", 993 | "chainId": 1612127, 994 | "url": "https://albireo-explorer.playfi.ai/address/0xF9cda624FBC7e059355ce98a31693d299FACd963#contract", 995 | "address": "0xF9cda624FBC7e059355ce98a31693d299FACd963" 996 | }, 997 | { 998 | "name": "Abstract Testnet", 999 | "chainId": 11124, 1000 | "url": "https://explorer.testnet.abs.xyz/address/0xF9cda624FBC7e059355ce98a31693d299FACd963#contract", 1001 | "address": "0xF9cda624FBC7e059355ce98a31693d299FACd963" 1002 | }, 1003 | { 1004 | "name": "Abstract Mainnet", 1005 | "chainId": 2741, 1006 | "url": "https://abscan.org/address/0xF9cda624FBC7e059355ce98a31693d299FACd963#code", 1007 | "address": "0xF9cda624FBC7e059355ce98a31693d299FACd963" 1008 | }, 1009 | { 1010 | "name": "Fraxtal Mainnet", 1011 | "chainId": 252, 1012 | "url": "https://fraxscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 1013 | }, 1014 | { 1015 | "name": "Fraxtal Holesky Testnet", 1016 | "chainId": 2522, 1017 | "url": "https://holesky.fraxscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 1018 | }, 1019 | { 1020 | "name": "Omax Mainnet", 1021 | "chainId": 311, 1022 | "url": "https://omaxray.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1023 | }, 1024 | { 1025 | "name": "Syndicate Frame Chain", 1026 | "chainId": 5101, 1027 | "url": "https://explorer-frame.syndicate.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1028 | }, 1029 | { 1030 | "name": "Dela Sepolia", 1031 | "chainId": 9393, 1032 | "url": "https://sepolia-delascan.deperp.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1033 | }, 1034 | { 1035 | "name": "NeoX Testnet", 1036 | "chainId": 12227330, 1037 | "url": "https://xt2scan.ngd.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1038 | }, 1039 | { 1040 | "name": "Sanko Mainnet", 1041 | "chainId": 1996, 1042 | "url": "https://explorer.sanko.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1043 | }, 1044 | { 1045 | "name": "Sanko Testnet", 1046 | "chainId": 1992, 1047 | "url": "https://testnet.sankoscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1048 | }, 1049 | { 1050 | "name": "Berachain Mainnet", 1051 | "chainId": 80094, 1052 | "url": "https://berascan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1053 | }, 1054 | { 1055 | "name": "Berachain Bepolia Testnet", 1056 | "chainId": 80069, 1057 | "url": "https://bepolia.beratrail.io/address/0xca11bde05977b3631167028862be2a173976ca11" 1058 | }, 1059 | { 1060 | "name": "Shibarium", 1061 | "chainId": 109, 1062 | "url": "https://www.shibariumscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1063 | }, 1064 | { 1065 | "name": "Immutable zkEVM Mainnet", 1066 | "chainId": 13371, 1067 | "url": "https://explorer.immutable.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1068 | }, 1069 | { 1070 | "name": "Immutable zkEVM Testnet", 1071 | "chainId": 13473, 1072 | "url": "https://explorer.testnet.immutable.com/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1073 | }, 1074 | { 1075 | "name": "RSS3 VSL Mainnet", 1076 | "chainId": 12553, 1077 | "url": "https://scan.rss3.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1078 | }, 1079 | { 1080 | "name": "RSS3 VSL Sepolia Testnet", 1081 | "chainId": 2331, 1082 | "url": "https://scan.testnet.rss3.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1083 | }, 1084 | { 1085 | "name": "Morph Sepolia Testnet", 1086 | "chainId": 2710, 1087 | "url": "https://explorer-testnet.morphl2.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1088 | }, 1089 | { 1090 | "name": "Morph Holesky Testnet", 1091 | "chainId": 2810, 1092 | "url": "https://explorer-holesky.morphl2.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1093 | }, 1094 | { 1095 | "name": "Morph", 1096 | "chainId": 2818, 1097 | "url": "https://explorer.morphl2.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1098 | }, 1099 | { 1100 | "name": "JIBCHAIN L1", 1101 | "chainId": 8899, 1102 | "url": "https://exp-l1.jibchain.net/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1103 | }, 1104 | { 1105 | "name": "Haqq Mainnet", 1106 | "chainId": 11235, 1107 | "url": "https://explorer.haqq.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1108 | }, 1109 | { 1110 | "name": "Zircuit Sepolia Testnet", 1111 | "chainId": 48899, 1112 | "url": "https://explorer.zircuit.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?activeTab=3" 1113 | }, 1114 | { 1115 | "name": "re.al", 1116 | "chainId": 111188, 1117 | "url": "https://explorer.re.al/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1118 | }, 1119 | { 1120 | "name": "Merlin Testnet", 1121 | "chainId": 686868, 1122 | "url": "https://testnet-scan.merlinchain.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1123 | }, 1124 | { 1125 | "name": "IOTA EVM", 1126 | "chainId": 8822, 1127 | "url": "https://explorer.evm.iota.org/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1128 | }, 1129 | { 1130 | "name": "Planq", 1131 | "chainId": 7070, 1132 | "url": "https://evm.planq.network/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1133 | }, 1134 | { 1135 | "name": "Cyber Testnet", 1136 | "chainId": 111557560, 1137 | "url": "https://testnet.cyberscan.co/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1138 | }, 1139 | { 1140 | "name": "Unit Zero Mainnet", 1141 | "chainId": 88811, 1142 | "url": "https://explorer.unit0.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1143 | }, 1144 | { 1145 | "name": "Unit Zero Stagenet", 1146 | "chainId": 88819, 1147 | "url": "https://explorer-stagenet.unit0.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1148 | }, 1149 | { 1150 | "name": "Unit Zero Testnet", 1151 | "chainId": 88817, 1152 | "url": "https://explorer-testnet.unit0.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1153 | }, 1154 | { 1155 | "name": "Sei EVM Devnet", 1156 | "chainId": 713715, 1157 | "url": "https://seitrace.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1158 | }, 1159 | { 1160 | "name": "Sei EVM Mainnet", 1161 | "chainId": 1329, 1162 | "url": "https://seitrace.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?chain=pacific-1&tab=contract" 1163 | }, 1164 | { 1165 | "name": "Hekla (Taiko A7 Testnet)", 1166 | "chainId": 167009, 1167 | "url": "https://explorer.hekla.taiko.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1168 | }, 1169 | { 1170 | "name": "Taiko Mainnet", 1171 | "chainId": 167000, 1172 | "url": "https://taikoscan.io/address/0xca11bde05977b3631167028862be2a173976ca11#code" 1173 | }, 1174 | { 1175 | "name": "Cyber Mainnet", 1176 | "chainId": 7560, 1177 | "url": "https://cyberscan.co/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1178 | }, 1179 | { 1180 | "name": "DreyerX Mainnet", 1181 | "chainId": 23451, 1182 | "url": "https://scan.dreyerx.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1183 | }, 1184 | { 1185 | "name": "Sahara Testnet", 1186 | "chainId": 313313, 1187 | "url": "https://explorer.saharaa.info/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1188 | }, 1189 | { 1190 | "name": "BOX Chain", 1191 | "chainId": 42299, 1192 | "url": "https://explorerl2new-boxchain-t4zoh9y5dr.t.conduit.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1193 | }, 1194 | { 1195 | "name": "OX Chain", 1196 | "chainId": 6699, 1197 | "url": "https://explorer-ox-chain-2s86s7wp21.t.conduit.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1198 | }, 1199 | { 1200 | "name": "Conflux Espace", 1201 | "chainId": 1030, 1202 | "url": "https://evm.confluxscan.net/address/0xca11bde05977b3631167028862be2a173976ca11?tab=contract-viewer" 1203 | }, 1204 | { 1205 | "name": "BEVM Testnet", 1206 | "chainId": 11503, 1207 | "url": "https://scan-testnet.bevm.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1208 | }, 1209 | { 1210 | "name": "Aura Mainnet", 1211 | "chainId": 6322, 1212 | "url": "https://aurascan.io/evm-contracts/0xca11bde05977b3631167028862be2a173976ca11" 1213 | }, 1214 | { 1215 | "name": "Superposition Testnet", 1216 | "chainId": 98985, 1217 | "url": "https://testnet-explorer.superposition.so/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1218 | }, 1219 | { 1220 | "name": "X Layer Mainnet", 1221 | "chainId": 196, 1222 | "url": "https://www.okx.com/web3/explorer/xlayer/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1223 | }, 1224 | { 1225 | "name": "Nahmii 3 Mainnet", 1226 | "chainId": 4061, 1227 | "url": "https://explorer.nahmii.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1228 | }, 1229 | { 1230 | "name": "Nahmii 3 Testnet", 1231 | "chainId": 4062, 1232 | "url": "https://explorer.testnet.nahmii.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1233 | }, 1234 | { 1235 | "name": "Plume Testnet", 1236 | "chainId": 98867, 1237 | "url": "https://testnet-explorer.plumenetwork.xyz/address/0xca11bde05977b3631167028862be2a173976ca11?tab=contract" 1238 | }, 1239 | { 1240 | "name": "Plume Mainnet", 1241 | "chainId": 98866, 1242 | "url": "https://phoenix-explorer.plumenetwork.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1243 | }, 1244 | { 1245 | "name": "Algen L1", 1246 | "chainId": 8911, 1247 | "url": "https://scan.algen.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1248 | }, 1249 | { 1250 | "name": "Bitlayer Mainnet", 1251 | "chainId": 200901, 1252 | "url": "https://www.btrscan.com/address/0xca11bde05977b3631167028862be2a173976ca11?tab=Contract" 1253 | }, 1254 | { 1255 | "name": "Lisk Mainnet", 1256 | "chainId": 1135, 1257 | "url": "https://blockscout.lisk.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1258 | }, 1259 | { 1260 | "name": "Gravity Alpha Mainnet", 1261 | "chainId": 1625, 1262 | "url": "https://explorer.gravity.xyz/address/0xca11bde05977b3631167028862be2a173976ca11?tab=contract" 1263 | }, 1264 | { 1265 | "name": "Yominet", 1266 | "chainId": 5264468217, 1267 | "url": "https://yominet.explorer.caldera.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1268 | }, 1269 | { 1270 | "name": "Bob", 1271 | "chainId": 60808, 1272 | "url": "https://explorer.gobob.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1273 | }, 1274 | { 1275 | "name": "Superseed", 1276 | "chainId": 53302, 1277 | "url": "https://sepolia-explorer.superseed.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1278 | }, 1279 | { 1280 | "name": "Rupaya", 1281 | "chainId": 499, 1282 | "url": "https://scan.rupaya.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1283 | }, 1284 | { 1285 | "name": "Fluence Testnet", 1286 | "chainId": 52164803, 1287 | "url": "https://blockscout.testnet.fluence.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1288 | }, 1289 | { 1290 | "name": "Fluence Stage", 1291 | "chainId": 123420000220, 1292 | "url": "https://blockscout.stage.fluence.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1293 | }, 1294 | { 1295 | "name": "Fluence", 1296 | "chainId": 9999999, 1297 | "url": "https://blockscout.mainnet.fluence.dev/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1298 | }, 1299 | { 1300 | "name": "Camp Testnet V2", 1301 | "chainId": 325000, 1302 | "url": "https://camp-network-testnet.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1303 | }, 1304 | { 1305 | "name": "Ontology Testnet", 1306 | "chainId": 5851, 1307 | "url": "https://explorer.ont.io/testnet/contract/other/0xca11bde05977b3631167028862be2a173976ca11" 1308 | }, 1309 | { 1310 | "name": "Ontology Mainnet", 1311 | "chainId": 58, 1312 | "url": "https://explorer.ont.io/contract/all/0xca11bde05977b3631167028862be2a173976ca11" 1313 | }, 1314 | { 1315 | "name": "Viction Testnet", 1316 | "chainId": 89, 1317 | "url": "https://testnet.vicscan.xyz/address/0xca11bde05977b3631167028862be2a173976ca11#code" 1318 | }, 1319 | { 1320 | "name": "Viction Mainnet", 1321 | "chainId": 88, 1322 | "url": "https://www.vicscan.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 1323 | }, 1324 | { 1325 | "name": "World Chain", 1326 | "chainId": 480, 1327 | "url": "https://worldchain-mainnet.explorer.alchemy.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1328 | }, 1329 | { 1330 | "name": "Flow Mainnet", 1331 | "chainId": 747, 1332 | "url": "https://evm.flowscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1333 | }, 1334 | { 1335 | "name": "Flow Testnet", 1336 | "chainId": 545, 1337 | "url": "https://evm-testnet.flowscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1338 | }, 1339 | { 1340 | "name": "Conflux Core Space Mainnet", 1341 | "chainId": 1029, 1342 | "url": "https://confluxscan.io/address/cfx:acevn2d3dr6vh4jca28c6cmvkktsg7r8n25vp9hnmw?tab=contract-viewer", 1343 | "address": "cfx:acevn2d3dr6vh4jca28c6cmvkktsg7r8n25vp9hnmw" 1344 | }, 1345 | { 1346 | "name": "Superposition", 1347 | "chainId": 55244, 1348 | "url": "https://explorer.superposition.so/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1349 | }, 1350 | { 1351 | "name": "Starchain Testnet", 1352 | "chainId": 1570, 1353 | "url": "https://devnet.starchainscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1354 | }, 1355 | { 1356 | "name": "Starchain Mainnet", 1357 | "chainId": 1578, 1358 | "url": "https://starchainscan.io/address/0xca11bde05977b3631167028862be2a173976ca11" 1359 | }, 1360 | { 1361 | "name": "ApeChain Mainnet", 1362 | "chainId": 33139, 1363 | "url": "https://apescan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11#code" 1364 | }, 1365 | { 1366 | "name": "WEMIX 3.0 Mainnet", 1367 | "chainId": 1111, 1368 | "url": "https://wemixscan.com/address/0xca11bde05977b3631167028862be2a173976ca11" 1369 | }, 1370 | { 1371 | "name": "Aleph Zero EVM Mainnet", 1372 | "chainId": 41455, 1373 | "url": "https://evm-explorer.alephzero.org/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1374 | }, 1375 | { 1376 | "name": "EDUChain Testnet", 1377 | "chainId": 656476, 1378 | "url": "https://edu-chain-testnet.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1379 | }, 1380 | { 1381 | "name": "Form Testnet", 1382 | "chainId": 132902, 1383 | "url": "https://explorer.form.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1384 | }, 1385 | { 1386 | "name": "peaq", 1387 | "chainId": 3338, 1388 | "url": "https://peaq.subscan.io/account/0xca11bde05977b3631167028862be2a173976ca11?tab=contract&evm_contract_tab=code" 1389 | }, 1390 | { 1391 | "name": "HyperEVM", 1392 | "chainId": 999, 1393 | "url": "https://hyperliquid.cloud.blockscout.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1394 | }, 1395 | { 1396 | "name": "Monad Testnet", 1397 | "chainId": 10143, 1398 | "url": "https://testnet.monadexplorer.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=Contract" 1399 | }, 1400 | { 1401 | "name": "Powerloom Mainnet", 1402 | "chainId": 7869, 1403 | "url": "https://explorer-v2.powerloom.network/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1404 | }, 1405 | { 1406 | "name": "Hoodi", 1407 | "chainId": 560048, 1408 | "url": "https://hoodi.etherscan.io/address/0xca11bde05977b3631167028862be2a173976ca11#code" 1409 | }, 1410 | { 1411 | "name": "MegaETH Testnet", 1412 | "chainId": 6342, 1413 | "url": "https://www.megaexplorer.xyz/address/0xcA11bde05977b3631167028862bE2a173976CA11" 1414 | }, 1415 | { 1416 | "name": "Ink Sepolia", 1417 | "chainId": 763373, 1418 | "url": "https://explorer-sepolia.inkonchain.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1419 | }, 1420 | { 1421 | "name": "Ink", 1422 | "chainId": 57073, 1423 | "url": "https://explorer.inkonchain.com/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1424 | }, 1425 | { 1426 | "name": "Bittensor", 1427 | "chainId": 964, 1428 | "url": "https://evm.taostats.io/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1429 | }, 1430 | { 1431 | "name": "Whitechain", 1432 | "chainId": 1875, 1433 | "url": "https://explorer.whitechain.io/address/0xcA11bde05977b3631167028862bE2a173976CA11/contract" 1434 | }, 1435 | { 1436 | "name": "Tangle Testnet", 1437 | "chainId": 3799, 1438 | "url": "https://testnet-explorer.tangle.tools/address/0xca11bde05977b3631167028862be2a173976ca11?tab=contract" 1439 | }, 1440 | { 1441 | "name": "Tangle Mainnet", 1442 | "chainId": 8545, 1443 | "url": "https://explorer.tangle.tools/address/0xcA11bde05977b3631167028862bE2a173976CA11?tab=contract" 1444 | } 1445 | ] 1446 | --------------------------------------------------------------------------------