Skip to content

Commit

Permalink
Merge branch 'release-v23' into aon-improve-foundry-l1-deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
Deniallugo authored Apr 29, 2024
2 parents d223781 + b5314f6 commit 493cfc2
Show file tree
Hide file tree
Showing 30 changed files with 4,077 additions and 367 deletions.
28 changes: 24 additions & 4 deletions l1-contracts/contracts/bridge/L1SharedBridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Ownable2StepUpgrade
IBridgehub public immutable override BRIDGE_HUB;

/// @dev Era's chainID
uint256 immutable ERA_CHAIN_ID;
uint256 public immutable ERA_CHAIN_ID;

/// @dev The address of zkSync Era diamond proxy contract.
address immutable ERA_DIAMOND_PROXY;
address public immutable ERA_DIAMOND_PROXY;

/// @dev Stores the first batch number on the zkSync Era Diamond Proxy that was settled after Diamond proxy upgrade.
/// This variable is used to differentiate between pre-upgrade and post-upgrade Eth withdrawals. Withdrawals from batches older
Expand Down Expand Up @@ -110,6 +110,12 @@ contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Ownable2StepUpgrade
_;
}

/// @notice Checks that the message sender is the shared bridge itself.
modifier onlySelf() {
require(msg.sender == address(this), "ShB not shared bridge");
_;
}

/// @dev Contract is expected to be used as proxy implementation.
/// @dev Initialize the implementation to prevent Parity hack.
constructor(
Expand Down Expand Up @@ -161,7 +167,7 @@ contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Ownable2StepUpgrade
}

/// @dev transfer tokens from legacy erc20 bridge or mailbox and set chainBalance as part of migration process
function transferFundsFromLegacy(address _token, address _target, uint256 _targetChainId) external onlyOwner {
function transferFundsFromLegacy(address _token, address _target, uint256 _targetChainId) external onlySelf {
if (_token == ETH_TOKEN_ADDRESS) {
uint256 balanceBefore = address(this).balance;
IMailbox(_target).transferEthToSharedBridge();
Expand All @@ -177,11 +183,25 @@ contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Ownable2StepUpgrade
require(legacyBridgeBalance > 0, "ShB: 0 amount to transfer");
IL1ERC20Bridge(_target).transferTokenToSharedBridge(_token);
uint256 balanceAfter = IERC20(_token).balanceOf(address(this));
require(balanceAfter - balanceBefore == legacyBridgeBalance, "ShB: wrong amount transferred");
require(balanceAfter - balanceBefore >= legacyBridgeBalance, "ShB: wrong amount transferred");
chainBalance[_targetChainId][_token] = chainBalance[_targetChainId][_token] + legacyBridgeBalance;
}
}

/// @dev transfer tokens from legacy erc20 bridge or mailbox and set chainBalance as part of migration process.
/// @dev Unlike `transferFundsFromLegacy` is provides a concrete limit on the gas used for the transfer and even if it will fail, it will not revert the whole transaction.
function safeTransferFundsFromLegacy(
address _token,
address _target,
uint256 _targetChainId,
uint256 _gasPerToken
) external onlyOwner {
try this.transferFundsFromLegacy{gas: _gasPerToken}(_token, _target, _targetChainId) {} catch {
// A reasonable amount of gas will be provided to transfer the token.
// If the transfer fails, we don't want to revert the whole transaction.
}
}

function receiveEth(uint256 _chainId) external payable {
require(BRIDGE_HUB.getHyperchain(_chainId) == msg.sender, "receiveEth not state transition");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own
uint256[] memory keys = hyperchainMap.keys();
chainAddresses = new address[](keys.length);
for (uint256 i = 0; i < keys.length; i++) {
chainAddresses[i] = hyperchainMap.get(i);
chainAddresses[i] = hyperchainMap.get(keys[i]);
}
}

Expand Down
8 changes: 4 additions & 4 deletions l1-contracts/contracts/state-transition/Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ contract Verifier is IVerifier {
function _loadVerificationKey() internal pure virtual {
assembly {
// gate setup commitments
mstore(VK_GATE_SETUP_0_X_SLOT, 0x03efa4fe0a5d7aa3d98e8becb5058987dddca95ecbe29b624ad274553bf9dd8e)
mstore(VK_GATE_SETUP_0_Y_SLOT, 0x24fb07eb5f0e62013938eb1dcae0aba367a7150a40a9ab2e1ae15e06bbe43853)
mstore(VK_GATE_SETUP_0_X_SLOT, 0x2b344993e706c18427e96e2eefd8e89aaf4cd464814ed978b98793bf129a786c)
mstore(VK_GATE_SETUP_0_Y_SLOT, 0x123af72bf1c5e693516b037d16ba48711c3486823aec3772318b6737634575a4)
mstore(VK_GATE_SETUP_1_X_SLOT, 0x04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2)
mstore(VK_GATE_SETUP_1_Y_SLOT, 0x0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410)
mstore(VK_GATE_SETUP_2_X_SLOT, 0x0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291)
Expand All @@ -295,8 +295,8 @@ contract Verifier is IVerifier {
mstore(VK_GATE_SETUP_4_Y_SLOT, 0x22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e)
mstore(VK_GATE_SETUP_5_X_SLOT, 0x0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9)
mstore(VK_GATE_SETUP_5_Y_SLOT, 0x15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746)
mstore(VK_GATE_SETUP_6_X_SLOT, 0x1c541b6423211b65e42c1a52d0c8a07bf631fbf24606a4135e9e486f2bb9bb06)
mstore(VK_GATE_SETUP_6_Y_SLOT, 0x057be8e4f2db0b66a9134809a33bae06d380c386fdce322e837d11a22ce0f9a9)
mstore(VK_GATE_SETUP_6_X_SLOT, 0x18e466aac9b830ea5417e1ba2e920acf041e3976d36fe891be80beac1d1696c7)
mstore(VK_GATE_SETUP_6_Y_SLOT, 0x025f89444627d09c7b1bf665dc6f58055bf729c683a4b3e815215758a7075602)
mstore(VK_GATE_SETUP_7_X_SLOT, 0x283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc)
mstore(VK_GATE_SETUP_7_Y_SLOT, 0x223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c269521)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor {
bytes32[] memory blobCommitments = new bytes32[](MAX_NUMBER_OF_BLOBS);
if (pricingMode == PubdataPricingMode.Validium) {
// skipping data validation for validium, we just check that the data is empty
require(logOutput.pubdataHash == 0x00, "v0h");
require(_newBatch.pubdataCommitments.length == 1, "EF: v0l");
} else if (pubdataSource == uint8(PubdataSource.Blob)) {
// In this scenario, pubdataCommitments is a list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 144 bytes
Expand Down
14 changes: 12 additions & 2 deletions l1-contracts/contracts/upgrades/UpgradeHyperchains.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ contract UpgradeHyperchains is BaseZkSyncUpgrade {
/// @notice The main function that will be called by the upgrade proxy.
/// @param _proposedUpgrade The upgrade to be executed.
function upgrade(ProposedUpgrade calldata _proposedUpgrade) public override returns (bytes32) {
(uint256 chainId, address bridgehubAddress, address stateTransitionManager, address sharedBridgeAddress) = abi
.decode(_proposedUpgrade.postUpgradeCalldata, (uint256, address, address, address));
(
uint256 chainId,
address bridgehubAddress,
address stateTransitionManager,
address sharedBridgeAddress,
address chainAdmin,
address validatorTimelock
) = abi.decode(_proposedUpgrade.postUpgradeCalldata, (uint256, address, address, address, address, address));
require(chainId != 0, "UpgradeHyperchain: 1");
require(bridgehubAddress != address(0), "UpgradeHyperchain: 2");
require(stateTransitionManager != address(0), "UpgradeHyperchain: 3");
require(sharedBridgeAddress != address(0), "UpgradeHyperchain: 4");
require(chainAdmin != address(0), "UpgradeHyperchains: 5");
require(validatorTimelock != address(0), "UpgradeHyperchains: 6");

s.chainId = chainId;
s.bridgehub = bridgehubAddress;
Expand All @@ -27,6 +35,8 @@ contract UpgradeHyperchains is BaseZkSyncUpgrade {
s.baseToken = ETH_TOKEN_ADDRESS;
s.baseTokenGasPriceMultiplierNominator = 1;
s.baseTokenGasPriceMultiplierDenominator = 1;
s.admin = chainAdmin;
s.validators[validatorTimelock] = true;

super.upgrade(_proposedUpgrade);
return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE;
Expand Down
5 changes: 4 additions & 1 deletion l1-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@
"hyperchain-upgrade-1": "ts-node scripts/hyperchain-upgrade-1.ts",
"hyperchain-upgrade-2": "ts-node scripts/hyperchain-upgrade-2.ts",
"hyperchain-upgrade-3": "ts-node scripts/hyperchain-upgrade-3.ts",
"upgrade-shared-bridge-era": "ts-node scripts/upgrade-shared-bridge-era.ts"
"token-migration": "ts-node scripts/token-migration.ts",
"setup-legacy-bridge-era": "ts-node scripts/setup-legacy-bridge-era.ts",
"upgrade-consistency-checker": "ts-node scripts/upgrade-consistency-checker.ts",
"governance-accept-ownership": "ts-node scripts/governance-accept-ownership.ts"
},
"dependencies": {
"dotenv": "^16.0.3",
Expand Down
134 changes: 134 additions & 0 deletions l1-contracts/scripts/governance-accept-ownership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/// This script is needed to migrate the ownership for key contracts to the governance multisig

// hardhat import should be the first import in the file
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import * as hardhat from "hardhat";
import { Command } from "commander";
import { Wallet, ethers } from "ethers";
import { formatUnits, parseUnits, Interface } from "ethers/lib/utils";
import { web3Provider, GAS_MULTIPLIER } from "./utils";
import { ethTestConfig } from "../src.ts/utils";

const ownable2StepInterface = new Interface(hardhat.artifacts.readArtifactSync("ValidatorTimelock").abi);
const governanceInterface = new Interface(hardhat.artifacts.readArtifactSync("Governance").abi);

const provider = web3Provider();

async function main() {
const program = new Command();

program.version("0.1.0").name("upgrade-shared-bridge-era").description("upgrade shared bridge for era diamond proxy");

program
.command("transfer-ownership")
.option("--private-key <private-key>")
.option("--gas-price <gas-price>")
.option("--nonce <nonce>")
.option("--owner-address <owner-address>")
.option("--validator-timelock-addr <validatorTimelockAddr>")
.option("--stm-addr <stateTransitionManagerAddr>")
.option("--l1-shared-bridge-addr <l1SharedBridgeAddr>")
.option("--bridgehub-addr <bridgehubAddr>")
.option("--proxy-admin-addr <proxyAdminAddr>")
.option("--only-verifier")
.action(async (cmd) => {
const deployWallet = cmd.privateKey
? new Wallet(cmd.privateKey, provider)
: Wallet.fromMnemonic(
process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic,
"m/44'/60'/0'/0/1"
).connect(provider);
console.log(`Using deployer wallet: ${deployWallet.address}`);

const ownerAddress = ethers.utils.getAddress(cmd.ownerAddress);
console.log(`Using owner address: ${ownerAddress}`);

const gasPrice = cmd.gasPrice
? parseUnits(cmd.gasPrice, "gwei")
: (await provider.getGasPrice()).mul(GAS_MULTIPLIER);
console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`);

const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount();
console.log(`Using nonce: ${nonce}`);

// Moving ownership for ValidatorTimelock
const validatorTimelockAddr = ethers.utils.getAddress(cmd.validatorTimelockAddr);
console.log(`Using ValidatorTimelock address: ${validatorTimelockAddr}`);
const stmAddr = ethers.utils.getAddress(cmd.stmAddr);
console.log("Using STM address: ", stmAddr);
const l1SharedBridgeAddr = ethers.utils.getAddress(cmd.l1SharedBridgeAddr);
console.log("Using L1 Shared Bridge address: ", l1SharedBridgeAddr);
const bridgehubAddr = ethers.utils.getAddress(cmd.bridgehubAddr);
console.log("Using Bridgehub address: ", bridgehubAddr);
const proxyAdminAddr = ethers.utils.getAddress(cmd.proxyAdminAddr);
console.log("Using Proxy Admin address: ", proxyAdminAddr);

await transferOwnership1StepTo(deployWallet, validatorTimelockAddr, ownerAddress);
await transferOwnership1StepTo(deployWallet, stmAddr, ownerAddress);
await transferOwnership1StepTo(deployWallet, l1SharedBridgeAddr, ownerAddress);
await transferOwnership1StepTo(deployWallet, bridgehubAddr, ownerAddress);
await transferOwnership1StepTo(deployWallet, proxyAdminAddr, ownerAddress);
});

program
.command("accept-ownership")
.option("--validator-timelock-addr <validatorTimelockAddr>")
.option("--stm-addr <stateTransitionManagerAddr>")
.option("--l1-shared-bridge-addr <l1SharedBridgeAddr>")
.option("--bridgehub-addr <bridgehubAddr>")
.action(async (cmd) => {
// Moving ownership for ValidatorTimelock
const validatorTimelockAddr = ethers.utils.getAddress(cmd.validatorTimelockAddr);
console.log(`Using ValidatorTimelock address: ${validatorTimelockAddr}`);
const stmAddr = ethers.utils.getAddress(cmd.stmAddr);
console.log("Using STM address: ", stmAddr);
const l1SharedBridgeAddr = ethers.utils.getAddress(cmd.l1SharedBridgeAddr);
console.log("Using L1 Shared Bridge address: ", l1SharedBridgeAddr);
const bridgehubAddr = ethers.utils.getAddress(cmd.bridgehubAddr);
console.log("Using Bridgehub address: ", bridgehubAddr);

const addresses = [validatorTimelockAddr, stmAddr, l1SharedBridgeAddr, bridgehubAddr];

const govCalls = addresses.map(acceptOwnershipCall);

const govOperation = {
calls: govCalls,
predecessor: ethers.constants.HashZero,
salt: ethers.constants.HashZero,
};

const scheduleData = governanceInterface.encodeFunctionData("scheduleTransparent", [govOperation, 0]);
const executeData = governanceInterface.encodeFunctionData("execute", [govOperation]);

console.log("Calldata for scheduling: ", scheduleData);
console.log("Calldata for execution: ", executeData);
});

await program.parseAsync(process.argv);
}

main()
.then(() => process.exit(0))
.catch((err) => {
console.error("Error:", err);
process.exit(1);
});

async function transferOwnership1StepTo(wallet: ethers.Wallet, contractAddress: string, newOwner: string) {
const contract = new ethers.Contract(contractAddress, ownable2StepInterface, wallet);
console.log("Transferring ownership of contract: ", contractAddress, " to: ", newOwner);
const tx = await contract.transferOwnership(newOwner);
console.log("Tx hash", tx.hash);
await tx.wait();
const newPendingOwner = await contract.pendingOwner();
console.log("New pending owner: ", newPendingOwner);
}

function acceptOwnershipCall(target: string) {
const data = ownable2StepInterface.encodeFunctionData("acceptOwnership", []);
return {
target,
value: 0,
data,
};
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/// This script is needed for testing the behavior of the shared bridge on Era chain.
/// Unlike other chains, Era also contains a legacy ERC20 bridge, which is also supported by the corresponding L2SharedBridge.
///
/// This script ensures that the LegacyBridge is set up correctly and that the L2SharedBridge references it correctly too.
/// IMPORTANT: this script should be only used for local testing.

// hardhat import should be the first import in the file
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import * as hardhat from "hardhat";
Expand Down Expand Up @@ -91,6 +97,10 @@ async function main() {
// For the server to start up.
console.log("Waiting for server to start up");
await waitForServer(l2Provider);
console.log("Server started up");

// Wait a bit more after the server is ready to ensure that all of its components are ready.
await sleep(2);

const l2SharedBridge = new ethers.Contract(
l2SharedBridgeAddress,
Expand Down
Loading

0 comments on commit 493cfc2

Please sign in to comment.