├── .gitignore
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
└── src
├── Multicall.sol
├── Multicall.t.sol
└── Multicall2.sol
/.gitignore:
--------------------------------------------------------------------------------
1 | /out
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/ds-test"]
2 | path = lib/ds-test
3 | url = https://github.com/dapphub/ds-test
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Maker Foundation
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 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all :; dapp build
2 | clean :; dapp clean
3 | test :; dapp test
4 | deploy :; dapp create Multicall
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DEPRECATED
2 |
3 | _This repository is no longer maintained. An active fork can be found at https://github.com/mds1/multicall. Note that this is an external repository not maintained by any entity funded or directed by MakerDAO governance._
4 |
5 | # Multicall
6 |
7 | Multicall aggregates results from multiple contract constant function calls.
8 |
9 | This reduces the number of separate JSON RPC requests that need to be sent
10 | (especially useful if using remote nodes like Infura), while also providing the
11 | guarantee that all values returned are from the same block (like an atomic read)
12 | and returning the block number the values are from (giving them important
13 | context so that results from old blocks can be ignored if they're from an
14 | out-of-date node).
15 |
16 | This smart contract is intended to be used with
17 | [Multicall.js](https://github.com/makerdao/multicall.js) in front-end dapps.
18 |
19 | ### Multicall Contract Addresses
20 | | Chain | Address |
21 | | ------- | ------- |
22 | | Mainnet | [0xeefba1e63905ef1d7acba5a8513c70307c1ce441](https://etherscan.io/address/0xeefba1e63905ef1d7acba5a8513c70307c1ce441#contracts) |
23 | | Kovan | [0x2cc8688c5f75e365aaeeb4ea8d6a480405a48d2a](https://kovan.etherscan.io/address/0x2cc8688c5f75e365aaeeb4ea8d6a480405a48d2a#contracts) |
24 | | Rinkeby | [0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821](https://rinkeby.etherscan.io/address/0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821#contracts) |
25 | | Görli | [0x77dca2c955b15e9de4dbbcf1246b4b85b651e50e](https://goerli.etherscan.io/address/0x77dca2c955b15e9de4dbbcf1246b4b85b651e50e#contracts) |
26 | | Ropsten | [0x53c43764255c17bd724f74c4ef150724ac50a3ed](https://ropsten.etherscan.io/address/0x53c43764255c17bd724f74c4ef150724ac50a3ed#code) |
27 | | xDai | [0xb5b692a88bdfc81ca69dcb1d924f59f0413a602a](https://blockscout.com/poa/dai/address/0xb5b692a88bdfc81ca69dcb1d924f59f0413a602a) |
28 | | Polygon | [0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507](https://explorer-mainnet.maticvigil.com/address/0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507/contracts)
29 | | Mumbai | [0x08411ADd0b5AA8ee47563b146743C13b3556c9Cc](https://explorer-mumbai.maticvigil.com/address/0x08411ADd0b5AA8ee47563b146743C13b3556c9Cc/transactions)
30 |
31 | ### Multicall2 Contract Addresses
32 | Multicall2 is the same as Multicall, but provides addition functions that allow calls within the batch to fail. Useful for situations where a call may fail depending on the state of the contract.
33 |
34 | | Chain | Address |
35 | | ------- | ------- |
36 | | Mainnet | [0x5ba1e12693dc8f9c48aad8770482f4739beed696](https://etherscan.io/address/0x5ba1e12693dc8f9c48aad8770482f4739beed696#contracts) |
37 | | Kovan | [0x5ba1e12693dc8f9c48aad8770482f4739beed696](https://kovan.etherscan.io/address/0x5ba1e12693dc8f9c48aad8770482f4739beed696#contracts) |
38 | | Rinkeby | [0x5ba1e12693dc8f9c48aad8770482f4739beed696](https://rinkeby.etherscan.io/address/0x5ba1e12693dc8f9c48aad8770482f4739beed696#contracts) |
39 | | Görli | [0x5ba1e12693dc8f9c48aad8770482f4739beed696](https://goerli.etherscan.io/address/0x5ba1e12693dc8f9c48aad8770482f4739beed696#contracts) |
40 | | Ropsten | [0x5ba1e12693dc8f9c48aad8770482f4739beed696](https://ropsten.etherscan.io/address/0x5ba1e12693dc8f9c48aad8770482f4739beed696#code) |
41 |
42 | ### Third-Party Deployments
43 |
44 | The following addresses have been submitted by external contributors and have not been vetted by Multicall maintainers.
45 |
46 | | Chain | Address |
47 | | ------- | ------- |
48 | | RSK Mainnet | [0x6c62bf5440de2cb157205b15c424bceb5c3368f5](https://explorer.rsk.co/address/0x6c62bf5440de2cb157205b15c424bceb5c3368f5) |
49 | | RSK Testnet | [0x9e469e1fc7fb4c5d17897b68eaf1afc9df39f103](https://explorer.testnet.rsk.co/address/0x9e469e1fc7fb4c5d17897b68eaf1afc9df39f103) |
50 | | BSC Mainnet | [0x41263cba59eb80dc200f3e2544eda4ed6a90e76c](https://bscscan.com/address/0x41263cba59eb80dc200f3e2544eda4ed6a90e76c) |
51 | | BSC Testnet | [0xae11C5B5f29A6a25e955F0CB8ddCc416f522AF5C](https://testnet.bscscan.com/address/0xae11c5b5f29a6a25e955f0cb8ddcc416f522af5c) |
52 |
--------------------------------------------------------------------------------
/src/Multicall.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 |
4 | pragma solidity >=0.5.0;
5 | pragma experimental ABIEncoderV2;
6 |
7 | /// @title Multicall - Aggregate results from multiple read-only function calls
8 | /// @author Michael Elliot
9 | /// @author Joshua Levine
10 | /// @author Nick Johnson
11 |
12 | contract Multicall {
13 | struct Call {
14 | address target;
15 | bytes callData;
16 | }
17 | function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) {
18 | blockNumber = block.number;
19 | returnData = new bytes[](calls.length);
20 | for(uint256 i = 0; i < calls.length; i++) {
21 | (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
22 | require(success);
23 | returnData[i] = ret;
24 | }
25 | }
26 | // Helper functions
27 | function getEthBalance(address addr) public view returns (uint256 balance) {
28 | balance = addr.balance;
29 | }
30 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
31 | blockHash = blockhash(blockNumber);
32 | }
33 | function getLastBlockHash() public view returns (bytes32 blockHash) {
34 | blockHash = blockhash(block.number - 1);
35 | }
36 | function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
37 | timestamp = block.timestamp;
38 | }
39 | function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
40 | difficulty = block.difficulty;
41 | }
42 | function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
43 | gaslimit = block.gaslimit;
44 | }
45 | function getCurrentBlockCoinbase() public view returns (address coinbase) {
46 | coinbase = block.coinbase;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Multicall.t.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 | pragma experimental ABIEncoderV2;
3 |
4 | import "ds-test/test.sol";
5 | import "./Multicall.sol";
6 |
7 | contract Store {
8 | uint256 internal val;
9 | function set(uint256 _val) public { val = _val; }
10 | function get() public view returns (uint256) { return val; }
11 | function getAnd10() public view returns (uint256, uint256) { return (val, 10); }
12 | function getAdd(uint256 _val) public view returns (uint256) { return val + _val; }
13 | }
14 |
15 | // We inherit from Multicall rather than deploy an instance because solidity
16 | // can't return dynamically sized byte arrays from external contracts
17 | contract MulticallTest is DSTest, Multicall {
18 |
19 | Store public storeA;
20 | Store public storeB;
21 |
22 | function setUp() public {
23 | storeA = new Store();
24 | storeB = new Store();
25 | }
26 |
27 | function test_store_basic_sanity() public {
28 | assertEq(storeA.get(), 0);
29 | storeA.set(100);
30 | assertEq(storeA.get(), 100);
31 | storeA.set(0);
32 | assertEq(storeA.get(), 0);
33 | }
34 |
35 | function test_single_call_single_return_no_args() public {
36 | storeA.set(123);
37 |
38 | Call[] memory _calls = new Call[](1);
39 | _calls[0].target = address(storeA);
40 | _calls[0].callData = abi.encodeWithSignature("get()");
41 |
42 | (, bytes[] memory _returnData) = aggregate(_calls);
43 |
44 | bytes memory _word = _returnData[0];
45 | uint256 _returnVal;
46 | assembly { _returnVal := mload(add(0x20, _word)) }
47 |
48 | assertEq(_returnVal, 123);
49 | }
50 |
51 | function test_multi_call_single_return_no_args() public {
52 | storeA.set(123);
53 | storeB.set(321);
54 |
55 | Call[] memory _calls = new Call[](2);
56 | _calls[0].target = address(storeA);
57 | _calls[0].callData = abi.encodeWithSignature("get()");
58 | _calls[1].target = address(storeB);
59 | _calls[1].callData = abi.encodeWithSignature("get()");
60 |
61 | (, bytes[] memory _returnData) = aggregate(_calls);
62 |
63 | bytes memory _wordA = _returnData[0];
64 | bytes memory _wordB = _returnData[1];
65 | uint256 _returnValA;
66 | uint256 _returnValB;
67 | assembly { _returnValA := mload(add(0x20, _wordA)) }
68 | assembly { _returnValB := mload(add(0x20, _wordB)) }
69 |
70 | assertEq(_returnValA, 123);
71 | assertEq(_returnValB, 321);
72 | }
73 |
74 | function test_single_call_single_return_single_arg() public {
75 | storeA.set(123);
76 |
77 | Call[] memory _calls = new Call[](1);
78 | _calls[0].target = address(storeA);
79 | _calls[0].callData = abi.encodeWithSignature("getAdd(uint256)", 1);
80 |
81 | (, bytes[] memory _returnData) = aggregate(_calls);
82 |
83 | bytes memory _word = _returnData[0];
84 | uint256 _returnVal;
85 | assembly { _returnVal := mload(add(0x20, _word)) }
86 |
87 | assertEq(_returnVal, 124);
88 | }
89 |
90 | function test_multi_call_single_return_single_arg() public {
91 | storeA.set(123);
92 | storeB.set(321);
93 |
94 | Call[] memory _calls = new Call[](2);
95 | _calls[0].target = address(storeA);
96 | _calls[0].callData = abi.encodeWithSignature("getAdd(uint256)", 1);
97 | _calls[1].target = address(storeB);
98 | _calls[1].callData = abi.encodeWithSignature("getAdd(uint256)", 1);
99 |
100 | (, bytes[] memory _returnData) = aggregate(_calls);
101 |
102 | bytes memory _wordA = _returnData[0];
103 | bytes memory _wordB = _returnData[1];
104 | uint256 _returnValA;
105 | uint256 _returnValB;
106 | assembly { _returnValA := mload(add(0x20, _wordA)) }
107 | assembly { _returnValB := mload(add(0x20, _wordB)) }
108 |
109 | assertEq(_returnValA, 124);
110 | assertEq(_returnValB, 322);
111 | }
112 |
113 | function test_single_call_multi_return_no_args() public {
114 | storeA.set(123);
115 |
116 | Call[] memory _calls = new Call[](1);
117 | _calls[0].target = address(storeA);
118 | _calls[0].callData = abi.encodeWithSignature("getAnd10()");
119 |
120 | (, bytes[] memory _returnData) = aggregate(_calls);
121 |
122 | bytes memory _words = _returnData[0];
123 | uint256 _returnValA1;
124 | uint256 _returnValA2;
125 | assembly { _returnValA1 := mload(add(0x20, _words)) }
126 | assembly { _returnValA2 := mload(add(0x40, _words)) }
127 |
128 | assertEq(_returnValA1, 123);
129 | assertEq(_returnValA2, 10);
130 |
131 | }
132 |
133 | function test_helpers() public {
134 | bytes32 blockHash = getBlockHash(510);
135 | bytes32 lastBlockHash = getLastBlockHash();
136 | uint256 timestamp = getCurrentBlockTimestamp();
137 | uint256 difficulty = getCurrentBlockDifficulty();
138 | uint256 gaslimit = getCurrentBlockGasLimit();
139 | address coinbase = getCurrentBlockCoinbase();
140 | uint256 balance = getEthBalance(address(this));
141 |
142 | assertEq(blockHash, blockhash(510));
143 | assertEq(lastBlockHash, blockhash(block.number - 1));
144 | assertEq(timestamp, block.timestamp);
145 | assertEq(difficulty, block.difficulty);
146 | assertEq(gaslimit, block.gaslimit);
147 | assertEq(coinbase, block.coinbase);
148 | assertEq(balance, address(this).balance);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/Multicall2.sol:
--------------------------------------------------------------------------------
1 | pragma solidity >=0.5.0;
2 | pragma experimental ABIEncoderV2;
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 |
9 | contract Multicall2 {
10 | struct Call {
11 | address target;
12 | bytes callData;
13 | }
14 | struct Result {
15 | bool success;
16 | bytes returnData;
17 | }
18 |
19 | function aggregate(Call[] memory 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 | function blockAndAggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
29 | (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls);
30 | }
31 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
32 | blockHash = blockhash(blockNumber);
33 | }
34 | function getBlockNumber() public view returns (uint256 blockNumber) {
35 | blockNumber = block.number;
36 | }
37 | function getCurrentBlockCoinbase() public view returns (address coinbase) {
38 | coinbase = block.coinbase;
39 | }
40 | function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
41 | difficulty = block.difficulty;
42 | }
43 | function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
44 | gaslimit = block.gaslimit;
45 | }
46 | function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
47 | timestamp = block.timestamp;
48 | }
49 | function getEthBalance(address addr) public view returns (uint256 balance) {
50 | balance = addr.balance;
51 | }
52 | function getLastBlockHash() public view returns (bytes32 blockHash) {
53 | blockHash = blockhash(block.number - 1);
54 | }
55 | function tryAggregate(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) {
56 | returnData = new Result[](calls.length);
57 | for(uint256 i = 0; i < calls.length; i++) {
58 | (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
59 |
60 | if (requireSuccess) {
61 | require(success, "Multicall2 aggregate: call failed");
62 | }
63 |
64 | returnData[i] = Result(success, ret);
65 | }
66 | }
67 | function tryBlockAndAggregate(bool requireSuccess, Call[] memory calls) public returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) {
68 | blockNumber = block.number;
69 | blockHash = blockhash(block.number);
70 | returnData = tryAggregate(requireSuccess, calls);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------