From 73d646abe4f42a9a38859d41b6b55a73d41a7900 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 13 Sep 2024 13:07:37 +0200 Subject: [PATCH 1/4] deployment of multicall3 --- .../deploy-scripts/DeployL2Contracts.sol | 24 ++ .../test/test_config/constant/hardhat.json | 2 +- .../contracts/dev-contracts/Multicall3.sol | 237 ++++++++++++++++++ 3 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 l2-contracts/contracts/dev-contracts/Multicall3.sol diff --git a/l1-contracts/deploy-scripts/DeployL2Contracts.sol b/l1-contracts/deploy-scripts/DeployL2Contracts.sol index dddd26b25..fe37859ef 100644 --- a/l1-contracts/deploy-scripts/DeployL2Contracts.sol +++ b/l1-contracts/deploy-scripts/DeployL2Contracts.sol @@ -28,6 +28,7 @@ contract DeployL2Script is Script { address l2SharedBridgeProxy; address consensusRegistryImplementation; address consensusRegistryProxy; + address multicall3; address forceDeployUpgraderAddress; } @@ -39,6 +40,7 @@ contract DeployL2Script is Script { bytes l2SharedBridgeProxyBytecode; bytes consensusRegistryBytecode; bytes consensusRegistryProxyBytecode; + bytes multicall3Bytecode; bytes forceDeployUpgrader; } @@ -61,6 +63,7 @@ contract DeployL2Script is Script { deployForceDeployer(); deployConsensusRegistry(); deployConsensusRegistryProxy(); + deployMulticall3(); saveOutput(); } @@ -137,6 +140,10 @@ contract DeployL2Script is Script { "/../l2-contracts/artifacts-zk/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json" ); + contracts.multicall3Bytecode = Utils.readHardhatBytecode( + "/../l2-contracts/argificats-zk/contracts/dev-contracts/Multicall3.sol/Multicall3.json" + ); + contracts.forceDeployUpgrader = Utils.readHardhatBytecode( "/../l2-contracts/artifacts-zk/contracts/ForceDeployUpgrader.sol/ForceDeployUpgrader.json" ); @@ -160,6 +167,7 @@ contract DeployL2Script is Script { vm.serializeAddress("root", "l2_shared_bridge_proxy", config.l2SharedBridgeProxy); vm.serializeAddress("root", "consensus_registry_implementation", config.consensusRegistryImplementation); vm.serializeAddress("root", "consensus_registry_proxy", config.consensusRegistryProxy); + vm.serializeAddress("root", "multicall3", config.multicall3); string memory toml = vm.serializeAddress("root", "l2_default_upgrader", config.forceDeployUpgraderAddress); string memory root = vm.projectRoot(); string memory path = string.concat(root, "/script-out/output-deploy-l2-contracts.toml"); @@ -261,6 +269,22 @@ contract DeployL2Script is Script { }); } + function deployMulticall3() internal { + // Multicall3 doesn't have a constructor. + bytes memory constructorData = ""; + + config.multicall3 = Utils.deployThroughL1({ + bytecode: contracts.multicall3, + constructorargs: constructorData, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainId, + bridgehubAddress: config.bridgehubAddress, + l1SharedBridgeProxy: config.l1SharedBridgeProxy + }); + } + // Deploy a transparent upgradable proxy for the already deployed consensus registry // implementation and save its address into the config. function deployConsensusRegistryProxy() internal { diff --git a/l1-contracts/test/test_config/constant/hardhat.json b/l1-contracts/test/test_config/constant/hardhat.json index 0e63431f0..aeb7ec506 100644 --- a/l1-contracts/test/test_config/constant/hardhat.json +++ b/l1-contracts/test/test_config/constant/hardhat.json @@ -95,4 +95,4 @@ "decimals": 18, "address": "0x51E83b811930bb4a3aAb3494894ec237Cb6cEc49" } -] +] \ No newline at end of file diff --git a/l2-contracts/contracts/dev-contracts/Multicall3.sol b/l2-contracts/contracts/dev-contracts/Multicall3.sol new file mode 100644 index 000000000..9a6c69340 --- /dev/null +++ b/l2-contracts/contracts/dev-contracts/Multicall3.sol @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +/// @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 { + // add this to be excluded from coverage report + function test() internal virtual {} + + 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.prevrandao; + } + + /// @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; + } +} From cbe788a34074b35a8f36bf8fd04d5aa7b07fc4e5 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 13 Sep 2024 14:17:50 +0200 Subject: [PATCH 2/4] fix --- l1-contracts/deploy-scripts/DeployL2Contracts.sol | 4 ++-- l1-contracts/script-out/.gitkeep | 0 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 l1-contracts/script-out/.gitkeep diff --git a/l1-contracts/deploy-scripts/DeployL2Contracts.sol b/l1-contracts/deploy-scripts/DeployL2Contracts.sol index fe37859ef..2f0778e7b 100644 --- a/l1-contracts/deploy-scripts/DeployL2Contracts.sol +++ b/l1-contracts/deploy-scripts/DeployL2Contracts.sol @@ -141,7 +141,7 @@ contract DeployL2Script is Script { ); contracts.multicall3Bytecode = Utils.readHardhatBytecode( - "/../l2-contracts/argificats-zk/contracts/dev-contracts/Multicall3.sol/Multicall3.json" + "/../l2-contracts/artifacts-zk/contracts/dev-contracts/Multicall3.sol/Multicall3.json" ); contracts.forceDeployUpgrader = Utils.readHardhatBytecode( @@ -274,7 +274,7 @@ contract DeployL2Script is Script { bytes memory constructorData = ""; config.multicall3 = Utils.deployThroughL1({ - bytecode: contracts.multicall3, + bytecode: contracts.multicall3Bytecode, constructorargs: constructorData, create2salt: "", l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, diff --git a/l1-contracts/script-out/.gitkeep b/l1-contracts/script-out/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 2aeb1f4556fa78f50a4f668f60e78dd861e13f7a Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 13 Sep 2024 14:48:55 +0200 Subject: [PATCH 3/4] fix --- l1-contracts/test/test_config/constant/hardhat.json | 2 +- script-out/.gitkeep | 0 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 script-out/.gitkeep diff --git a/l1-contracts/test/test_config/constant/hardhat.json b/l1-contracts/test/test_config/constant/hardhat.json index aeb7ec506..0e63431f0 100644 --- a/l1-contracts/test/test_config/constant/hardhat.json +++ b/l1-contracts/test/test_config/constant/hardhat.json @@ -95,4 +95,4 @@ "decimals": 18, "address": "0x51E83b811930bb4a3aAb3494894ec237Cb6cEc49" } -] \ No newline at end of file +] diff --git a/script-out/.gitkeep b/script-out/.gitkeep new file mode 100644 index 000000000..e69de29bb From 6079b0e551dfd87caecfcb7c6f310b3d1f44d683 Mon Sep 17 00:00:00 2001 From: Grzegorz Prusak Date: Fri, 13 Sep 2024 14:50:59 +0200 Subject: [PATCH 4/4] gitkeep again --- {script-out => l1-contracts/script-out}/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {script-out => l1-contracts/script-out}/.gitkeep (100%) diff --git a/script-out/.gitkeep b/l1-contracts/script-out/.gitkeep similarity index 100% rename from script-out/.gitkeep rename to l1-contracts/script-out/.gitkeep