├── .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 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 | --------------------------------------------------------------------------------