Skip to content

Commit

Permalink
add setSecondaryGateway script (#24)
Browse files Browse the repository at this point in the history
* add setSecondaryGateway script

* fix solidity version
  • Loading branch information
zkbenny committed Feb 6, 2024
1 parent c775913 commit 71563e3
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 0 deletions.
5 changes: 5 additions & 0 deletions contracts/zksync/l1-contracts/zksync/interfaces/IAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
pragma solidity ^0.8.0;

import {FeeParams} from "../Storage.sol";
import {IL2Gateway} from "../../../../interfaces/IL2Gateway.sol";

/// @title The interface of the Admin Contract that controls access rights for contract management.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IAdmin {
/// @notice Init gateway
/// @param _gateway The gateway on local chain
function setGateway(IL2Gateway _gateway) external;

/// @notice Change validator status (active or not active)
/// @param _validator Validator address
/// @param _active Active flag
Expand Down
29 changes: 29 additions & 0 deletions contracts/zksync/l1-contracts/zksync/interfaces/IGetters.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {IL2Gateway} from "../../../../interfaces/IL2Gateway.sol";

/// @title The interface of the Getters Contract that implements functions for getting contract state from outside the blockchain.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IGetters {
/*//////////////////////////////////////////////////////////////
CUSTOM GETTERS
//////////////////////////////////////////////////////////////*/

/// @return The gateway on local chain
function getGateway() external view returns (IL2Gateway);

/// @return The total number of batches that were committed & verified & executed
function getTotalBatchesExecuted() external view returns (uint256);

/// @return The total number of priority operations that were added to the priority queue, including all processed ones
function getTotalPriorityTxs() external view returns (uint256);

/// @return Whether the address has a validator access
function isValidator(address _address) external view returns (bool);

/// @return merkleRoot Merkle root of the tree with L2 logs for the selected batch
function l2LogsRootHash(uint256 _batchNumber) external view returns (bytes32 merkleRoot);
}
1 change: 1 addition & 0 deletions examples/arbitrum/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require('@nomiclabs/hardhat-ethers');
require('./scripts/syncL2Requests');
require('./scripts/syncBatchRoot');
require('./scripts/setSecondaryGateway');

const BaseConfig = require('../../hardhat.base.config');

Expand Down
156 changes: 156 additions & 0 deletions examples/arbitrum/scripts/setSecondaryGateway.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
const { providers, Wallet, utils } = require('ethers');
const { readDeployContract, getLogName, readDeployLogField } = require('../../../script/utils');
const logName = require('../../../script/deploy_log_name');
const { L1TransactionReceipt, L1ToL2MessageStatus } = require('@arbitrum/sdk');
const { L1ToL2MessageGasEstimator } = require('@arbitrum/sdk/dist/lib/message/L1ToL2MessageGasEstimator');
const { getBaseFee } = require('@arbitrum/sdk/dist/lib/utils/lib');
const { task, types } = require('hardhat/config');

require('dotenv').config();

task('setSecondaryGateway', 'Send secondary gateway')
.addOptionalParam(
'arbitrator',
'The arbitrator address (default get from arbitrator deploy log)',
undefined,
types.string,
)
.addParam('targetNetwork', 'L2 network name', undefined, types.string, false)
.addOptionalParam('active', 'Enable the gateway?', true, types.boolean)
.setAction(async (taskArgs, hre) => {
const arbitrumName = process.env.ARBITRUM;
const ethereumName = process.env.ETHEREUM;
console.log(`Arbitrum net name: ${arbitrumName}`);
console.log(`Ethereum net name: ${ethereumName}`);

let arbitratorAddr = taskArgs.arbitrator;
let targetNetwork = taskArgs.targetNetwork;
const active = taskArgs.active;
if (arbitratorAddr === undefined) {
arbitratorAddr = readDeployLogField(
logName.DEPLOY_ARBITRATOR_LOG_PREFIX,
logName.DEPLOY_LOG_ARBITRATOR,
ethereumName,
);
}
if (targetNetwork === arbitrumName) {
console.log('Can not set for primary chain');
return;
}
let l1GatewayAddr;
if (targetNetwork === ethereumName) {
l1GatewayAddr = readDeployContract(logName.DEPLOY_ETH_GATEWAY_LOG_PREFIX, logName.DEPLOY_GATEWAY, ethereumName);
} else {
const l1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, targetNetwork);
l1GatewayAddr = readDeployContract(l1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName);
}
if (l1GatewayAddr === undefined) {
console.log('L1 gateway address not found');
return;
}
console.log(`The arbitrator address: ${arbitratorAddr}`);
console.log(`The secondary chain l1 gateway address: ${l1GatewayAddr}`);
console.log(`Enable the gateway? ${active}`);

const arbitrumL1GatewayLogName = getLogName(logName.DEPLOY_L1_GATEWAY_LOG_PREFIX, arbitrumName);
const arbitrumL1GatewayAddr = readDeployContract(arbitrumL1GatewayLogName, logName.DEPLOY_GATEWAY, ethereumName);
if (arbitrumL1GatewayAddr === undefined) {
console.log('Arbitrum l1 gateway address not exist');
return;
}
console.log(`The arbitrum l1 gateway address: ${arbitrumL1GatewayAddr}`);

const arbitrumL2GatewayAddr = readDeployContract(
logName.DEPLOY_L2_GATEWAY_LOG_PREFIX,
logName.DEPLOY_GATEWAY,
arbitrumName,
);
if (arbitrumL2GatewayAddr === undefined) {
console.log('Arbitrum l2 gateway address not exist');
return;
}
console.log(`The arbitrum l2 gateway address: ${arbitrumL2GatewayAddr}`);

const walletPrivateKey = process.env.DEVNET_PRIVKEY;
const l1Provider = new providers.JsonRpcProvider(process.env.L1RPC);
const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC);
const l1Wallet = new Wallet(walletPrivateKey, l1Provider);
const l2Wallet = new Wallet(walletPrivateKey, l2Provider);

/**
* Now we can query the required gas params using the estimateAll method in Arbitrum SDK
*/
const l1ToL2MessageGasEstimate = new L1ToL2MessageGasEstimator(l2Provider);

const l1WalletAddress = await l1Wallet.getAddress();
const l1WalletBalance = utils.formatEther(await l1Wallet.getBalance());
console.log(`${l1WalletAddress} balance on l1: ${l1WalletBalance} ether`);

const arbitrator = await hre.ethers.getContractAt('Arbitrator', arbitratorAddr, l1Wallet);
const zkLinkFactory = await hre.ethers.getContractAt('IZkSync', hre.ethers.constants.AddressZero);
const zkLinkCallValue = 0;
const zkLinkCallData = zkLinkFactory.interface.encodeFunctionData('setSecondaryChainGateway', [
l1GatewayAddr,
active,
]);
const l2GatewayFactory = await hre.ethers.getContractFactory('ArbitrumL2Gateway');
const l2GatewayCallData = l2GatewayFactory.interface.encodeFunctionData('claimMessageCallback', [
zkLinkCallValue,
zkLinkCallData,
]);

/**
* The estimateAll method gives us the following values for sending an L1->L2 message
* (1) maxSubmissionCost: The maximum cost to be paid for submitting the transaction
* (2) gasLimit: The L2 gas limit
* (3) deposit: The total amount to deposit on L1 to cover L2 gas and L2 call value
*/
const l1BaseFee = await getBaseFee(l1Provider);
console.log(`Current base fee on L1 is: ${l1BaseFee}`);
const L1ToL2MessageGasParams = await l1ToL2MessageGasEstimate.estimateAll(
{
from: arbitrumL1GatewayAddr,
to: arbitrumL2GatewayAddr,
l2CallValue: zkLinkCallValue,
excessFeeRefundAddress: l1WalletAddress,
callValueRefundAddress: arbitrumL2GatewayAddr,
data: l2GatewayCallData,
},
l1BaseFee,
l1Provider,
);
console.log(`Current retryable base submission price is: ${L1ToL2MessageGasParams.maxSubmissionCost.toString()}`);
console.log(`Estimate gasLimit on L2 is: ${L1ToL2MessageGasParams.gasLimit.toString()}`);
console.log(`Estimate maxFeePerGas on L2 is: ${L1ToL2MessageGasParams.maxFeePerGas.toString()}`);
console.log(`Estimate fee to pay on L1 is: ${L1ToL2MessageGasParams.deposit.toString()}`);

const adapterParams = utils.defaultAbiCoder.encode(
['uint256', 'uint256', 'uint256'],
[L1ToL2MessageGasParams.maxSubmissionCost, L1ToL2MessageGasParams.gasLimit, L1ToL2MessageGasParams.maxFeePerGas],
);
console.log(`Send a l1 message to l2...`);
const l1Tx = await arbitrator.setSecondaryChainGateway(l1GatewayAddr, active, adapterParams, {
value: L1ToL2MessageGasParams.deposit,
});
const l1TxHash = l1Tx.hash;
console.log(`The l1 tx hash: ${l1TxHash}`);
const arbitratorReceipt = await l1Tx.wait();

const l1TxReceipt = new L1TransactionReceipt(arbitratorReceipt);

/**
* In principle, a single L1 txn can trigger any number of L1-to-L2 messages (each with its own sequencer number).
* In this case, we know our txn triggered only one
* Here, We check if our L1 to L2 message is redeemed on L2
*/
const messages = await l1TxReceipt.getL1ToL2Messages(l2Wallet);
const message = messages[0];
console.log('Waiting for the L2 execution of the transaction. This may take up to 10-15 minutes ⏰');
const messageResult = await message.waitForStatus();
const status = messageResult.status;
if (status === L1ToL2MessageStatus.REDEEMED) {
console.log(`L2 retryable ticket is executed 🥳 ${messageResult.l2TxReceipt.transactionHash}`);
} else {
console.log(`L2 retryable ticket is failed with status ${L1ToL2MessageStatus[status]}`);
}
});

0 comments on commit 71563e3

Please sign in to comment.