diff --git a/l1-contracts/contracts/dev-contracts/test/AdminFacetTest.sol b/l1-contracts/contracts/dev-contracts/test/AdminFacetTest.sol index 614c34bb9c..3c4ec6bb8b 100644 --- a/l1-contracts/contracts/dev-contracts/test/AdminFacetTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/AdminFacetTest.sol @@ -8,7 +8,7 @@ contract AdminFacetTest is AdminFacet { // add this to be excluded from coverage report function test() internal virtual {} - constructor() { + constructor(uint256 _l1ChainId) AdminFacet(_l1ChainId) { s.admin = msg.sender; s.stateTransitionManager = msg.sender; } diff --git a/l1-contracts/contracts/dev-contracts/test/DummyHyperchain.sol b/l1-contracts/contracts/dev-contracts/test/DummyHyperchain.sol index 74ae9827f6..dc417dac25 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyHyperchain.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyHyperchain.sol @@ -5,7 +5,11 @@ import {MailboxFacet} from "../../state-transition/chain-deps/facets/Mailbox.sol import {FeeParams, PubdataPricingMode} from "../../state-transition/chain-deps/ZkSyncHyperchainStorage.sol"; contract DummyHyperchain is MailboxFacet { - constructor(address bridgeHubAddress, uint256 _eraChainId) MailboxFacet(_eraChainId) { + constructor( + address bridgeHubAddress, + uint256 _eraChainId, + uint256 _l1ChainId + ) MailboxFacet(_eraChainId, _l1ChainId) { s.bridgehub = bridgeHubAddress; } diff --git a/l1-contracts/contracts/dev-contracts/test/MailboxFacetTest.sol b/l1-contracts/contracts/dev-contracts/test/MailboxFacetTest.sol index d5a4155105..81c2dcca3e 100644 --- a/l1-contracts/contracts/dev-contracts/test/MailboxFacetTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/MailboxFacetTest.sol @@ -10,7 +10,7 @@ contract MailboxFacetTest is MailboxFacet { // add this to be excluded from coverage report function test() internal virtual {} - constructor(uint256 _eraChainId) MailboxFacet(_eraChainId) { + constructor(uint256 _eraChainId, uint256 _l1ChainId) MailboxFacet(_eraChainId, _l1ChainId) { s.admin = msg.sender; } diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index e68bf5623a..48a6af4acb 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -27,6 +27,19 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { /// @inheritdoc IZkSyncHyperchainBase string public constant override getName = "AdminFacet"; + /// @notice The chain id of L1. This contract can be deployed on multiple layers, but this value is still equal to the + /// L1 that is at the most base layer. + uint256 internal immutable L1_CHAIN_ID; + + constructor(uint256 _l1ChainId) { + L1_CHAIN_ID = _l1ChainId; + } + + modifier onlyL1() { + require(block.chainid == L1_CHAIN_ID, "AdminFacet: not L1"); + _; + } + /// @inheritdoc IAdmin function setPendingAdmin(address _newPendingAdmin) external onlyAdmin { // Save previous value into the stack to put it into the event later @@ -72,7 +85,7 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { } /// @inheritdoc IAdmin - function changeFeeParams(FeeParams calldata _newFeeParams) external onlyAdminOrStateTransitionManager { + function changeFeeParams(FeeParams calldata _newFeeParams) external onlyAdminOrStateTransitionManager onlyL1 { // Double checking that the new fee params are valid, i.e. // the maximal pubdata per batch is not less than the maximal pubdata per priority transaction. require(_newFeeParams.maxPubdataPerBatch >= _newFeeParams.priorityTxMaxPubdata, "n6"); @@ -87,7 +100,10 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { } /// @inheritdoc IAdmin - function setTokenMultiplier(uint128 _nominator, uint128 _denominator) external onlyAdminOrStateTransitionManager { + function setTokenMultiplier( + uint128 _nominator, + uint128 _denominator + ) external onlyAdminOrStateTransitionManager onlyL1 { require(_denominator != 0, "AF: denominator 0"); uint128 oldNominator = s.baseTokenGasPriceMultiplierNominator; uint128 oldDenominator = s.baseTokenGasPriceMultiplierDenominator; @@ -99,14 +115,14 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { } /// @inheritdoc IAdmin - function setPubdataPricingMode(PubdataPricingMode _pricingMode) external onlyAdmin { + function setPubdataPricingMode(PubdataPricingMode _pricingMode) external onlyAdmin onlyL1 { require(s.totalBatchesCommitted == 0, "AdminFacet: set validium only after genesis"); // Validium mode can be set only before the first batch is processed s.feeParams.pubdataPricingMode = _pricingMode; emit ValidiumModeStatusUpdate(_pricingMode); } /// @inheritdoc IAdmin - function setTransactionFilterer(address _transactionFilterer) external onlyAdmin { + function setTransactionFilterer(address _transactionFilterer) external onlyAdmin onlyL1 { address oldTransactionFilterer = s.transactionFilterer; s.transactionFilterer = _transactionFilterer; emit NewTransactionFilterer(oldTransactionFilterer, _transactionFilterer); @@ -214,12 +230,10 @@ contract AdminFacet is ZkSyncHyperchainBase, IAdmin { address _prevMsgSender, bytes calldata ) external payable override onlyBridgehub returns (bytes memory chainBridgeMintData) { - // (address _newSettlementLayerAdmin, bytes memory _diamondCut) = abi.decode(_data, (address, bytes)); require(s.settlementLayer == address(0), "Af: already migrated"); require(_prevMsgSender == s.admin, "Af: not chainAdmin"); IStateTransitionManager stm = IStateTransitionManager(s.stateTransitionManager); - // address chainBaseToken = hyperchain.getBaseToken(); uint256 currentProtocolVersion = s.protocolVersion; uint256 protocolVersion = stm.protocolVersion(); diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol index 32642fcbaf..c1b3c71d02 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol @@ -44,8 +44,18 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { /// @dev Era's chainID uint256 internal immutable ERA_CHAIN_ID; - constructor(uint256 _eraChainId) { + /// @notice The chain id of L1. This contract can be deployed on multiple layers, but this value is still equal to the + /// L1 that is at the most base layer. + uint256 internal immutable L1_CHAIN_ID; + + modifier onlyL1() { + require(block.chainid == L1_CHAIN_ID, "MailboxFacet: not L1"); + _; + } + + constructor(uint256 _eraChainId, uint256 _l1ChainId) { ERA_CHAIN_ID = _eraChainId; + L1_CHAIN_ID = _l1ChainId; } /// @inheritdoc IMailbox @@ -297,7 +307,7 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { bytes[] calldata _factoryDeps, bytes32 _canonicalTxHash, uint64 _expirationTimestamp - ) external override returns (bytes32 canonicalTxHash) { + ) external override onlyL1 returns (bytes32 canonicalTxHash) { require(IBridgehub(s.bridgehub).whitelistedSettlementLayers(s.chainId), "Mailbox SL: not SL"); require( IStateTransitionManager(s.stateTransitionManager).getHyperchain(_chainId) == msg.sender, @@ -531,7 +541,7 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { uint16 _l2TxNumberInBatch, bytes calldata _message, bytes32[] calldata _merkleProof - ) external nonReentrant { + ) external nonReentrant onlyL1 { require(s.chainId == ERA_CHAIN_ID, "Mailbox: finalizeEthWithdrawal only available for Era on mailbox"); IL1AssetRouter(s.baseTokenBridge).finalizeWithdrawal({ _chainId: ERA_CHAIN_ID, @@ -552,7 +562,7 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { uint256 _l2GasPerPubdataByteLimit, bytes[] calldata _factoryDeps, address _refundRecipient - ) external payable returns (bytes32 canonicalTxHash) { + ) external payable onlyL1 returns (bytes32 canonicalTxHash) { require(s.chainId == ERA_CHAIN_ID, "Mailbox: legacy interface only available for Era"); canonicalTxHash = _requestL2TransactionSender( BridgehubL2TransactionRequest({ diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 4e71bb02fd..170488d965 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -452,12 +452,14 @@ contract DeployL1Script is Script { console.log("ExecutorFacet deployed at:", executorFacet); addresses.stateTransition.executorFacet = executorFacet; - address adminFacet = deployViaCreate2(type(AdminFacet).creationCode); + address adminFacet = deployViaCreate2( + abi.encodePacked(type(AdminFacet).creationCode, abi.encode(config.l1ChainId)) + ); console.log("AdminFacet deployed at:", adminFacet); addresses.stateTransition.adminFacet = adminFacet; address mailboxFacet = deployViaCreate2( - abi.encodePacked(type(MailboxFacet).creationCode, abi.encode(config.eraChainId)) + abi.encodePacked(type(MailboxFacet).creationCode, abi.encode(config.eraChainId, config.l1ChainId)) ); console.log("MailboxFacet deployed at:", mailboxFacet); addresses.stateTransition.mailboxFacet = mailboxFacet; diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 806b59962d..66c69c5590 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -420,10 +420,9 @@ export class Deployer { } public async deployBridgehubImplementation(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { - const l1ChainId = this.isZkMode() ? getNumberFromEnv("ETH_CLIENT_CHAIN_ID") : await this.deployWallet.getChainId(); const contractAddress = await this.deployViaCreate2( "Bridgehub", - [l1ChainId, this.addresses.Governance, getNumberFromEnv("CONTRACTS_MAX_NUMBER_OF_HYPERCHAINS")], + [await this.getL1ChainId(), this.addresses.Governance, getNumberFromEnv("CONTRACTS_MAX_NUMBER_OF_HYPERCHAINS")], create2Salt, ethTxOptions ); @@ -560,7 +559,12 @@ export class Deployer { } public async deployAdminFacet(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { - const contractAddress = await this.deployViaCreate2("AdminFacet", [], create2Salt, ethTxOptions); + const contractAddress = await this.deployViaCreate2( + "AdminFacet", + [await this.getL1ChainId()], + create2Salt, + ethTxOptions + ); if (this.verbose) { console.log(`CONTRACTS_ADMIN_FACET_ADDR=${contractAddress}`); @@ -571,7 +575,12 @@ export class Deployer { public async deployMailboxFacet(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { const eraChainId = getNumberFromEnv("CONTRACTS_ERA_CHAIN_ID"); - const contractAddress = await this.deployViaCreate2("MailboxFacet", [eraChainId], create2Salt, ethTxOptions); + const contractAddress = await this.deployViaCreate2( + "MailboxFacet", + [eraChainId, await this.getL1ChainId()], + create2Salt, + ethTxOptions + ); if (this.verbose) { console.log(`Mailbox deployed with era chain id: ${eraChainId}`); @@ -1545,4 +1554,9 @@ export class Deployer { public proxyAdminContract(signerOrProvider: Signer | providers.Provider) { return ProxyAdminFactory.connect(this.addresses.TransparentProxyAdmin, signerOrProvider); } + + private async getL1ChainId(): Promise { + const l1ChainId = this.isZkMode() ? getNumberFromEnv("ETH_CLIENT_CHAIN_ID") : await this.deployWallet.getChainId(); + return +l1ChainId; + } } diff --git a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol index 9c3777bdee..8d8fc003ee 100644 --- a/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -57,7 +57,7 @@ contract ExperimentalBridgeTest is Test { bridgeHub = Bridgehub(address(dummyBridgehub)); address weth = makeAddr("WETH"); mockSTM = new DummyStateTransitionManagerWBH(address(bridgeHub)); - mockChainContract = new DummyHyperchain(address(bridgeHub), eraChainId); + mockChainContract = new DummyHyperchain(address(bridgeHub), eraChainId, block.chainid); mockSharedBridge = new DummySharedBridge(keccak256("0xabc")); mockSecondSharedBridge = new DummySharedBridge(keccak256("0xdef")); ntv = new L1NativeTokenVault(weth, IL1AssetRouter(address(mockSharedBridge))); diff --git a/l1-contracts/test/foundry/unit/concrete/DiamondCut/FacetCut.t.sol b/l1-contracts/test/foundry/unit/concrete/DiamondCut/FacetCut.t.sol index adceecddba..a480aaac24 100644 --- a/l1-contracts/test/foundry/unit/concrete/DiamondCut/FacetCut.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/DiamondCut/FacetCut.t.sol @@ -29,7 +29,7 @@ contract FacetCutTest is DiamondCutTest { function setUp() public { eraChainId = 9; diamondCutTestContract = new DiamondCutTestContract(); - mailboxFacet = new MailboxFacet(eraChainId); + mailboxFacet = new MailboxFacet(eraChainId, block.chainid); gettersFacet = new GettersFacet(); executorFacet1 = new ExecutorFacet(); executorFacet2 = new ExecutorFacet(); diff --git a/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol b/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol index 6c736675f1..93c3ab30cc 100644 --- a/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/DiamondCut/UpgradeLogic.t.sol @@ -50,7 +50,7 @@ contract UpgradeLogicTest is DiamondCutTest { diamondCutTestContract = new DiamondCutTestContract(); diamondInit = new DiamondInit(); - adminFacet = new AdminFacet(); + adminFacet = new AdminFacet(block.chainid); gettersFacet = new GettersFacet(); Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](2); diff --git a/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol index 3325b9343e..3cc74d5be4 100644 --- a/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol @@ -157,10 +157,10 @@ contract ExecutorTest is Test { rollupL1DAValidator = new RollupL1DAValidator(); - admin = new AdminFacet(); + admin = new AdminFacet(block.chainid); getters = new GettersFacet(); executor = new TestExecutor(); - mailbox = new MailboxFacet(eraChainId); + mailbox = new MailboxFacet(eraChainId, block.chainid); DummyStateTransitionManager stateTransitionManager = new DummyStateTransitionManager(); vm.mockCall( diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol index a202e29bfe..75d697bb64 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol @@ -58,7 +58,7 @@ contract StateTransitionManagerTest is Test { ); facetCuts.push( Diamond.FacetCut({ - facet: address(new AdminFacet()), + facet: address(new AdminFacet(block.chainid)), action: Diamond.Action.Add, isFreezable: true, selectors: Utils.getAdminSelectors() diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol index a4419a342f..97c275f400 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol @@ -36,7 +36,7 @@ contract AdminTest is Test { function setUp() public virtual { Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](2); facetCuts[0] = Diamond.FacetCut({ - facet: address(new AdminFacet()), + facet: address(new AdminFacet(block.chainid)), action: Diamond.Action.Add, isFreezable: true, selectors: getAdminSelectors() diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol index 00e5584958..cc3590787a 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol @@ -39,7 +39,7 @@ contract MailboxTest is Test { Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](3); facetCuts[0] = Diamond.FacetCut({ - facet: address(new MailboxFacet(eraChainId)), + facet: address(new MailboxFacet(eraChainId, block.chainid)), action: Diamond.Action.Add, isFreezable: true, selectors: getMailboxSelectors() diff --git a/l1-contracts/test/unit_tests/governance_test.spec.ts b/l1-contracts/test/unit_tests/governance_test.spec.ts index 4ab11f2668..4cb28b7066 100644 --- a/l1-contracts/test/unit_tests/governance_test.spec.ts +++ b/l1-contracts/test/unit_tests/governance_test.spec.ts @@ -13,10 +13,11 @@ describe("Admin facet tests", function () { before(async () => { const contractFactory = await hardhat.ethers.getContractFactory("AdminFacetTest"); - const contract = await contractFactory.deploy(); + const contract = await contractFactory.deploy(await contractFactory.signer.getChainId()); adminFacetTest = AdminFacetTestFactory.connect(contract.address, contract.signer); - const governanceContract = await contractFactory.deploy(); + const governanceContract = await contractFactory.deploy(await contractFactory.signer.getChainId()); + const governance = GovernanceFactory.connect(governanceContract.address, governanceContract.signer); await adminFacetTest.setPendingAdmin(governance.address); diff --git a/l1-contracts/test/unit_tests/mailbox_test.spec.ts b/l1-contracts/test/unit_tests/mailbox_test.spec.ts index a45c97dd9d..97b55e410b 100644 --- a/l1-contracts/test/unit_tests/mailbox_test.spec.ts +++ b/l1-contracts/test/unit_tests/mailbox_test.spec.ts @@ -199,7 +199,10 @@ describe("Mailbox tests", function () { before(async () => { const mailboxTestContractFactory = await hardhat.ethers.getContractFactory("MailboxFacetTest"); - const mailboxTestContract = await mailboxTestContractFactory.deploy(chainId); + const mailboxTestContract = await mailboxTestContractFactory.deploy( + chainId, + await mailboxTestContractFactory.signer.getChainId() + ); testContract = MailboxFacetTestFactory.connect(mailboxTestContract.address, mailboxTestContract.signer); // Generating 10 more gas prices for test suit diff --git a/l1-contracts/test/unit_tests/proxy_test.spec.ts b/l1-contracts/test/unit_tests/proxy_test.spec.ts index ee448159c5..c338e0d43a 100644 --- a/l1-contracts/test/unit_tests/proxy_test.spec.ts +++ b/l1-contracts/test/unit_tests/proxy_test.spec.ts @@ -45,7 +45,7 @@ describe("Diamond proxy tests", function () { diamondInit = DiamondInitFactory.connect(diamondInitContract.address, diamondInitContract.signer); const adminFactory = await hardhat.ethers.getContractFactory("AdminFacet"); - const adminContract = await adminFactory.deploy(); + const adminContract = await adminFactory.deploy(await owner.getChainId()); adminFacet = AdminFacetFactory.connect(adminContract.address, adminContract.signer); const gettersFacetFactory = await hardhat.ethers.getContractFactory("GettersFacet"); @@ -53,7 +53,7 @@ describe("Diamond proxy tests", function () { gettersFacet = GettersFacetFactory.connect(gettersFacetContract.address, gettersFacetContract.signer); const mailboxFacetFactory = await hardhat.ethers.getContractFactory("MailboxFacet"); - const mailboxFacetContract = await mailboxFacetFactory.deploy(chainId); + const mailboxFacetContract = await mailboxFacetFactory.deploy(chainId, await owner.getChainId()); mailboxFacet = MailboxFacetFactory.connect(mailboxFacetContract.address, mailboxFacetContract.signer); const executorFactory = await hardhat.ethers.getContractFactory("ExecutorFacet");