From c195fdba459fb8100732eb6e89d0f6196234166d Mon Sep 17 00:00:00 2001 From: Daniel Wang <99078276+dantaik@users.noreply.github.com> Date: Fri, 31 May 2024 02:08:59 +0700 Subject: [PATCH] docs(protocol,docs-site): add and deploy multicall3 (#17430) --- .../network-reference/mainnet-addresses.md | 1 + packages/protocol/.eslintignore | 3 +- packages/protocol/.solhintignore | 3 +- .../thirdparty/multicall/Multicall3.sol | 256 ++++++++++++++++++ .../deployments/mainnet-contract-logs-L2.md | 1 + 5 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 packages/protocol/contracts/thirdparty/multicall/Multicall3.sol diff --git a/packages/docs-site/src/content/docs/network-reference/mainnet-addresses.md b/packages/docs-site/src/content/docs/network-reference/mainnet-addresses.md index eb2379a0d82..4027c745bd4 100644 --- a/packages/docs-site/src/content/docs/network-reference/mainnet-addresses.md +++ b/packages/docs-site/src/content/docs/network-reference/mainnet-addresses.md @@ -52,6 +52,7 @@ description: Network reference page describing various important addresses on Ta | WETH | `0xA51894664A773981C6C112C43ce576f315d5b1B6` | | Taiko Token (Bridged) | `0xa9d23408b9ba935c230493c40c73824df71a0975` | | Safe Singleton Factory | `0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7` | +| Multicall3 | `0x174EB72C7d94a6D7dBAE3e4d20337B57088bB246` | ## Rollup contracts owner diff --git a/packages/protocol/.eslintignore b/packages/protocol/.eslintignore index dcb9ae39eee..c912638f61e 100644 --- a/packages/protocol/.eslintignore +++ b/packages/protocol/.eslintignore @@ -3,4 +3,5 @@ artifacts cache coverage lib -contracts/automata-attestation/ \ No newline at end of file +contracts/automata-attestation/ +contracts/thirdparty/multicall/ \ No newline at end of file diff --git a/packages/protocol/.solhintignore b/packages/protocol/.solhintignore index b98f25593a5..c5b984b6439 100644 --- a/packages/protocol/.solhintignore +++ b/packages/protocol/.solhintignore @@ -4,4 +4,5 @@ contracts/test/TestLibRLPReader.sol **/contracts/thirdparty/**/*.sol /contracts/automata-attestation/ test/GasComparison.t.sol -test/TestLn.sol \ No newline at end of file +test/TestLn.sol +contracts/thirdparty/multicall/*.sol \ No newline at end of file diff --git a/packages/protocol/contracts/thirdparty/multicall/Multicall3.sol b/packages/protocol/contracts/thirdparty/multicall/Multicall3.sol new file mode 100644 index 00000000000..ee0b7772407 --- /dev/null +++ b/packages/protocol/contracts/thirdparty/multicall/Multicall3.sol @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +/// @title Multicall3 +/// @notice Aggregate results from multiple function calls +/// @dev Multicall & Multicall2 backwards-compatible +/// @dev Aggregate methods are marked `payable` to save 24 gas per call +/// @author Michael Elliot +/// @author Joshua Levine +/// @author Nick Johnson +/// @author Andreas Bigger +/// @author Matt Solomon +contract Multicall3 { + struct Call { + address target; + bytes callData; + } + + struct Call3 { + address target; + bool allowFailure; + bytes callData; + } + + struct Call3Value { + address target; + bool allowFailure; + uint256 value; + bytes callData; + } + + struct Result { + bool success; + bytes returnData; + } + + /// @notice Backwards-compatible call aggregation with Multicall + /// @param calls An array of Call structs + /// @return blockNumber The block number where the calls were executed + /// @return returnData An array of bytes containing the responses + function aggregate(Call[] calldata calls) + public + payable + returns (uint256 blockNumber, bytes[] memory returnData) + { + blockNumber = block.number; + uint256 length = calls.length; + returnData = new bytes[](length); + Call calldata call; + for (uint256 i = 0; i < length;) { + bool success; + call = calls[i]; + (success, returnData[i]) = call.target.call(call.callData); + require(success, "Multicall3: call failed"); + unchecked { + ++i; + } + } + } + + /// @notice Backwards-compatible with Multicall2 + /// @notice Aggregate calls without requiring success + /// @param requireSuccess If true, require all calls to succeed + /// @param calls An array of Call structs + /// @return returnData An array of Result structs + function tryAggregate( + bool requireSuccess, + Call[] calldata calls + ) + public + payable + returns (Result[] memory returnData) + { + uint256 length = calls.length; + returnData = new Result[](length); + Call calldata call; + for (uint256 i = 0; i < length;) { + Result memory result = returnData[i]; + call = calls[i]; + (result.success, result.returnData) = call.target.call(call.callData); + if (requireSuccess) require(result.success, "Multicall3: call failed"); + unchecked { + ++i; + } + } + } + + /// @notice Backwards-compatible with Multicall2 + /// @notice Aggregate calls and allow failures using tryAggregate + /// @param calls An array of Call structs + /// @return blockNumber The block number where the calls were executed + /// @return blockHash The hash of the block where the calls were executed + /// @return returnData An array of Result structs + function tryBlockAndAggregate( + bool requireSuccess, + Call[] calldata calls + ) + public + payable + returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) + { + blockNumber = block.number; + blockHash = blockhash(block.number); + returnData = tryAggregate(requireSuccess, calls); + } + + /// @notice Backwards-compatible with Multicall2 + /// @notice Aggregate calls and allow failures using tryAggregate + /// @param calls An array of Call structs + /// @return blockNumber The block number where the calls were executed + /// @return blockHash The hash of the block where the calls were executed + /// @return returnData An array of Result structs + function blockAndAggregate(Call[] calldata calls) + public + payable + returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) + { + (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); + } + + /// @notice Aggregate calls, ensuring each returns success if required + /// @param calls An array of Call3 structs + /// @return returnData An array of Result structs + function aggregate3(Call3[] calldata calls) + public + payable + returns (Result[] memory returnData) + { + uint256 length = calls.length; + returnData = new Result[](length); + Call3 calldata calli; + for (uint256 i = 0; i < length;) { + Result memory result = returnData[i]; + calli = calls[i]; + (result.success, result.returnData) = calli.target.call(calli.callData); + assembly { + // Revert if the call fails and failure is not allowed + // `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)` + if iszero(or(calldataload(add(calli, 0x20)), mload(result))) { + // set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // set data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // set length of revert string + mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017) + // set revert string: bytes32(abi.encodePacked("Multicall3: call failed")) + mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000) + revert(0x00, 0x64) + } + } + unchecked { + ++i; + } + } + } + + /// @notice Aggregate calls with a msg value + /// @notice Reverts if msg.value is less than the sum of the call values + /// @param calls An array of Call3Value structs + /// @return returnData An array of Result structs + function aggregate3Value(Call3Value[] calldata calls) + public + payable + returns (Result[] memory returnData) + { + uint256 valAccumulator; + uint256 length = calls.length; + returnData = new Result[](length); + Call3Value calldata calli; + for (uint256 i = 0; i < length;) { + Result memory result = returnData[i]; + calli = calls[i]; + uint256 val = calli.value; + // Humanity will be a Type V Kardashev Civilization before this overflows - andreas + // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256 + unchecked { + valAccumulator += val; + } + (result.success, result.returnData) = calli.target.call{ value: val }(calli.callData); + assembly { + // Revert if the call fails and failure is not allowed + // `allowFailure := calldataload(add(calli, 0x20))` and `success := mload(result)` + if iszero(or(calldataload(add(calli, 0x20)), mload(result))) { + // set "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // set data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // set length of revert string + mstore(0x24, 0x0000000000000000000000000000000000000000000000000000000000000017) + // set revert string: bytes32(abi.encodePacked("Multicall3: call failed")) + mstore(0x44, 0x4d756c746963616c6c333a2063616c6c206661696c6564000000000000000000) + revert(0x00, 0x84) + } + } + unchecked { + ++i; + } + } + // Finally, make sure the msg.value = SUM(call[0...i].value) + require(msg.value == valAccumulator, "Multicall3: value mismatch"); + } + + /// @notice Returns the block hash for the given block number + /// @param blockNumber The block number + function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { + blockHash = blockhash(blockNumber); + } + + /// @notice Returns the block number + function getBlockNumber() public view returns (uint256 blockNumber) { + blockNumber = block.number; + } + + /// @notice Returns the block coinbase + function getCurrentBlockCoinbase() public view returns (address coinbase) { + coinbase = block.coinbase; + } + + /// @notice Returns the block difficulty + function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { + difficulty = block.difficulty; + } + + /// @notice Returns the block gas limit + function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { + gaslimit = block.gaslimit; + } + + /// @notice Returns the block timestamp + function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { + timestamp = block.timestamp; + } + + /// @notice Returns the (ETH) balance of a given address + function getEthBalance(address addr) public view returns (uint256 balance) { + balance = addr.balance; + } + + /// @notice Returns the block hash of the last block + function getLastBlockHash() public view returns (bytes32 blockHash) { + unchecked { + blockHash = blockhash(block.number - 1); + } + } + + /// @notice Gets the base fee of the given block + /// @notice Can revert if the BASEFEE opcode is not implemented by the given chain + function getBasefee() public view returns (uint256 basefee) { + basefee = block.basefee; + } + + /// @notice Returns the chain id + function getChainId() public view returns (uint256 chainid) { + chainid = block.chainid; + } +} diff --git a/packages/protocol/deployments/mainnet-contract-logs-L2.md b/packages/protocol/deployments/mainnet-contract-logs-L2.md index baf15015b8f..3fb35051a34 100644 --- a/packages/protocol/deployments/mainnet-contract-logs-L2.md +++ b/packages/protocol/deployments/mainnet-contract-logs-L2.md @@ -153,6 +153,7 @@ ## Other EOAs/Contracts - safe-singleton-factory: `0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7` +- multicall3: `0x174EB72C7d94a6D7dBAE3e4d20337B57088bB246` - `davidcai.eth`:`0x56706F118e42AE069F20c5636141B844D1324AE1` - `admin.taiko.eth`: `0x9CBeE534B5D8a6280e01a14844Ee8aF350399C7F` - `labs.taiko.eth`: `0xB73b0FC4C0Cfc73cF6e034Af6f6b42Ebe6c8b49D`