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

add setSecondaryGateway script #24

Merged
merged 2 commits into from
Feb 6, 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
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]}`);
}
});
Loading