diff --git a/packages/contracts/contracts/Multicall2.sol b/packages/contracts/contracts/Multicall2.sol new file mode 100644 index 0000000..6bf095e --- /dev/null +++ b/packages/contracts/contracts/Multicall2.sol @@ -0,0 +1,99 @@ +/** + * Submitted for verification at Optimistic.Etherscan.io on 2022-05-14 + */ + +// SPDX-License-Identifier: MIT + +pragma solidity >=0.5.0; +pragma experimental ABIEncoderV2; + +/// @title Multicall2 - Aggregate results from multiple read-only function calls +/// @author Michael Elliot +/// @author Joshua Levine +/// @author Nick Johnson + +contract Multicall2 { + struct Call { + address target; + bytes callData; + } + + struct Result { + bool success; + bytes returnData; + } + + function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { + blockNumber = block.number; + returnData = new bytes[](calls.length); + for (uint256 i = 0; i < calls.length; i++) { + (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); + require(success, "Multicall aggregate: call failed"); + returnData[i] = ret; + } + } + + function blockAndAggregate(Call[] memory calls) + public + returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) + { + (blockNumber, blockHash, returnData) = tryBlockAndAggregate(true, calls); + } + + function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { + blockHash = blockhash(blockNumber); + } + + function getBlockNumber() public view returns (uint256 blockNumber) { + blockNumber = block.number; + } + + function getCurrentBlockCoinbase() public view returns (address coinbase) { + coinbase = block.coinbase; + } + + function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { + difficulty = block.difficulty; + } + + function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { + gaslimit = block.gaslimit; + } + + function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { + timestamp = block.timestamp; + } + + function getEthBalance(address addr) public view returns (uint256 balance) { + balance = addr.balance; + } + + function getLastBlockHash() public view returns (bytes32 blockHash) { + blockHash = blockhash(block.number - 1); + } + + function tryAggregate(bool requireSuccess, Call[] memory calls) public returns (Result[] memory returnData) { + returnData = new Result[](calls.length); + for (uint256 i = 0; i < calls.length; i++) { + (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); + + if (requireSuccess) { + require(success, "Multicall2 aggregate: call failed"); + } + + returnData[i] = Result(success, ret); + } + } + + function tryBlockAndAggregate( + bool requireSuccess, + Call[] memory calls + ) + public + returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData) + { + blockNumber = block.number; + blockHash = blockhash(block.number); + returnData = tryAggregate(requireSuccess, calls); + } +} diff --git a/packages/contracts/script/DeployContracts.s.sol b/packages/contracts/script/DeployContracts.s.sol index ea04871..c73761d 100644 --- a/packages/contracts/script/DeployContracts.s.sol +++ b/packages/contracts/script/DeployContracts.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; -import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/Test.sol"; import { MiniMeToken } from "@vacp2p/minime/contracts/MiniMeToken.sol"; import { DeploymentConfig } from "./DeploymentConfig.s.sol"; import { BaseScript } from "./Base.s.sol"; @@ -56,6 +56,7 @@ contract DeployContracts is BaseScript { featuredVotingContract.setDirectory(directoryContract); vm.stopBroadcast(); + console.log("contract Multicall2", deploymentConfig.getMulticallAddress()); return (minimeToken, directoryContract, votingContract, featuredVotingContract); } } diff --git a/packages/contracts/script/DeploymentConfig.s.sol b/packages/contracts/script/DeploymentConfig.s.sol index 6c8491e..f677bfc 100644 --- a/packages/contracts/script/DeploymentConfig.s.sol +++ b/packages/contracts/script/DeploymentConfig.s.sol @@ -5,8 +5,12 @@ pragma solidity ^0.8.19; import { Script } from "forge-std/Script.sol"; import { MiniMeTokenFactory } from "@vacp2p/minime/contracts/MiniMeTokenFactory.sol"; import { MiniMeToken } from "@vacp2p/minime/contracts/MiniMeToken.sol"; +import { Multicall2 } from "../contracts/Multicall2.sol"; contract DeploymentConfig is Script { + + error DeploymentConfig__InvalidMulticallAddress(); + NetworkConfig public activeNetworkConfig; struct NetworkConfig { @@ -31,11 +35,18 @@ contract DeploymentConfig is Script { address public deployer; + address private multicallAddress; + // solhint-disable-next-line var-name-mixedcase address internal SNT_ADDRESS_GOERLI = 0x3D6AFAA395C31FCd391fE3D562E75fe9E8ec7E6a; // solhint-disable-next-line var-name-mixedcase address internal SNT_ADDRESS_MAINNET = 0x744d70FDBE2Ba4CF95131626614a1763DF805B9E; + // solhint-disable-next-line var-name-mixedcase + address internal MULTICALL_ADDRESS_GOERLI = 0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e; + // solhint-disable-next-line var-name-mixedcase + address internal MULTICALL_ADDRESS_OPTIMISM = 0xeAa6877139d436Dc6d1f75F3aF15B74662617B2C; + constructor(address _broadcaster) { deployer = _broadcaster; if (block.chainid == 1) { @@ -62,7 +73,12 @@ contract DeploymentConfig is Script { }); } - function getGoerliEthConfig() public view returns (NetworkConfig memory) { + function getGoerliEthConfig() public returns (NetworkConfig memory) { + // Actually, it'd be nicer to have `multicallAddress` be part of `NetworkConfig`, + // however, adding another field to the struct causes us to run into the + // "stack too deep" error during compilation, hence, we're using an additional + // property on the contract to access the value later from there. + multicallAddress = MULTICALL_ADDRESS_GOERLI; return NetworkConfig({ votingLengthInSeconds: FOUR_MINS_IN_SECONDS, votingVerificationLengthInSeconds: TWO_MINS_IN_SECONDS, @@ -88,8 +104,16 @@ contract DeploymentConfig is Script { true ); minimeToken.generateTokens(deployer, 100_000_000 ether); + + Multicall2 multicall = new Multicall2(); vm.stopBroadcast(); + // Actually, it'd be nicer to have `multicallAddress` be part of `NetworkConfig`, + // however, adding another field to the struct causes us to run into the + // "stack too deep" error during compilation, hence, we're using an additional + // property on the contract to access the value later from there. + multicallAddress = address(multicall); + return NetworkConfig({ votingLengthInSeconds: FOUR_MINS_IN_SECONDS, votingVerificationLengthInSeconds: TWO_MINS_IN_SECONDS, @@ -101,4 +125,11 @@ contract DeploymentConfig is Script { voteToken: address(minimeToken) }); } + + function getMulticallAddress() public view returns (address) { + if (multicallAddress == address(0)) { + revert DeploymentConfig__InvalidMulticallAddress(); + } + return multicallAddress; + } }