Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues 6 #13

Merged
merged 5 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 238 additions & 0 deletions contracts/dev-contracts/Multicall3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
/**
*Submitted for verification at Etherscan.io on 2022-05-17
*/

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @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 <mike@makerdao.com>
/// @author Joshua Levine <joshua@makerdao.com>
/// @author Nick Johnson <arachnid@notdot.net>
/// @author Andreas Bigger <andreas@nascent.xyz>
/// @author Matt Solomon <matt@mattsolomon.dev>
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;
}
}
5 changes: 3 additions & 2 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'hardhat-abi-exporter';
import * as dotenv from 'dotenv';
import './script/deploy_portal';
import './script/deploy_merge_token';
import './script/deploy_multicall';

dotenv.config();

Expand Down Expand Up @@ -61,8 +62,8 @@ const config: HardhatUserConfig = {
ethNetwork: 'mainnet',
verifyURL: 'https://explorer.zklink.io/contract_verification',
zksync: true,
accounts: [process.env.WALLET_PRIVATE_KEY || '']
}
accounts: [process.env.WALLET_PRIVATE_KEY || ''],
},
},
zksolc: {
version: '1.3.22',
Expand Down
5 changes: 5 additions & 0 deletions script/deploy_log_name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ export const DEPLOY_LOG_MERGETOKEN_NAME = 'mergeToken_NAME';
export const DEPLOY_LOG_MERGETOKEN_SYMBOL = 'mergeToken_SYMBOL';
export const DEPLOY_LOG_MERGETOKEN_DECIMALS = 'mergeToken_DECIMALS';
export const DEPLOY_LOG_MERGETOKEN_VERIFIED = 'mergeTokenVerified';

// consumed in deploy_multicall.ts
export const DEPLOY_MULTICALL_LOG_PREFIX = 'deploy_multicall';
export const DEPLOY_LOG_MULTICALL = 'multicall';
export const DEPLOY_LOG_MULTICALL_VERIFIED = 'multicallVerified';
54 changes: 54 additions & 0 deletions script/deploy_multicall.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as fs from 'fs';
import { verifyContractCode, createOrGetDeployLog, ChainContractDeployer, getDeployTx } from './utils';
import {
DEPLOY_LOG_DEPLOYER,
DEPLOY_LOG_DEPLOY_TX_HASH,
DEPLOY_LOG_DEPLOY_BLOCK_NUMBER,
DEPLOY_MULTICALL_LOG_PREFIX,
DEPLOY_LOG_MULTICALL,
DEPLOY_LOG_MULTICALL_VERIFIED,
} from './deploy_log_name';
import { task, types } from 'hardhat/config';

function getContractName() {
return 'Multicall3';
}

task('deployMulticall3', 'Deploy Multicall3')
.addParam('skipVerify', 'Skip verify', false, types.boolean, true)
.setAction(async (taskArgs, hardhat) => {
let skipVerify = taskArgs.skipVerify;
console.log('skip verify contracts?', skipVerify);

const contractDeployer = new ChainContractDeployer(hardhat);
await contractDeployer.init();
const deployerWallet = contractDeployer.deployerWallet;

const { deployLogPath, deployLog } = createOrGetDeployLog(DEPLOY_MULTICALL_LOG_PREFIX, hardhat.network.name);
const dLog = deployLog as any;
dLog[DEPLOY_LOG_DEPLOYER] = await deployerWallet?.getAddress();
fs.writeFileSync(deployLogPath, JSON.stringify(dLog, null, 2));

// deploy multicall3
let multicall3Addr;
if (!(DEPLOY_LOG_MULTICALL in dLog)) {
console.log('deploy multicall3...');
const contractName = getContractName();
const contract = await contractDeployer.deployContract(contractName, []);
const transaction = await getDeployTx(contract);
multicall3Addr = await contract.getAddress();
dLog[DEPLOY_LOG_MULTICALL] = multicall3Addr;
dLog[DEPLOY_LOG_DEPLOY_TX_HASH] = transaction?.hash;
dLog[DEPLOY_LOG_DEPLOY_BLOCK_NUMBER] = transaction?.blockNumber;
fs.writeFileSync(deployLogPath, JSON.stringify(dLog, null, 2));
} else {
multicall3Addr = dLog[DEPLOY_LOG_MULTICALL];
}
console.log('multicall3 token', multicall3Addr);

if (!skipVerify) {
await verifyContractCode(hardhat, multicall3Addr, []);
dLog[DEPLOY_LOG_MULTICALL_VERIFIED] = true;
fs.writeFileSync(deployLogPath, JSON.stringify(dLog, null, 2));
}
});
Loading