From e0a27a3e72a850879c1ffb3a7bfff16e97d452ff Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 17 Sep 2024 13:04:47 +0100 Subject: [PATCH 01/42] bridge fixes --- .../contracts/bridge/BridgedStandardERC20.sol | 11 ++++++++++- l1-contracts/contracts/bridge/L2WrappedBaseToken.sol | 8 ++++++++ .../contracts/bridge/asset-router/IL2AssetRouter.sol | 4 +++- .../contracts/bridge/asset-router/L2AssetRouter.sol | 8 ++++---- .../bridge/interfaces/IBridgedStandardToken.sol | 4 ++++ .../contracts/bridge/ntv/NativeTokenVault.sol | 9 +++++++-- l1-contracts/src.ts/deploy.ts | 4 +++- 7 files changed, 39 insertions(+), 9 deletions(-) diff --git a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol index bd8d01110..b6dddb4d9 100644 --- a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol +++ b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol @@ -43,6 +43,9 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, /// @dev Address of the native token vault that is used as trustee who can mint/burn tokens address public nativeTokenVault; + /// @dev The assetId of the token. + bytes32 public assetId; + /// @dev This also sets the native token vault to the default value if it is not set. /// It is not set only on the L2s for legacy tokens. modifier onlyNTV() { @@ -74,14 +77,20 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, /// @notice Initializes a contract token for later use. Expected to be used in the proxy. /// @dev Stores the L1 address of the bridge and set `name`/`symbol`/`decimals` getters that L1 token has. + /// @param _assetId The assetId of the token. /// @param _originToken Address of the origin token that can be deposited to mint this bridged token /// @param _data The additional data that the L1 bridge provide for initialization. /// In this case, it is packed `name`/`symbol`/`decimals` of the L1 token. - function bridgeInitialize(address _originToken, bytes calldata _data) external initializer returns (uint256) { + function bridgeInitialize( + bytes32 _assetId, + address _originToken, + bytes calldata _data + ) external initializer returns (uint256) { if (_originToken == address(0)) { revert ZeroAddress(); } originToken = _originToken; + assetId = _assetId; nativeTokenVault = msg.sender; diff --git a/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol b/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol index 4319a8b7c..cc825cbee 100644 --- a/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol +++ b/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol @@ -6,6 +6,7 @@ import {ERC20PermitUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/tok import {IL2WrappedBaseToken} from "./interfaces/IL2WrappedBaseToken.sol"; import {IBridgedStandardToken} from "./interfaces/IBridgedStandardToken.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol"; import {ZeroAddress, Unauthorized, BridgeMintNotImplemented, WithdrawFailed} from "../common/L1ContractErrors.sol"; @@ -29,6 +30,12 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri /// @dev Address of the L1 base token. It can be deposited to mint this L2 token. address public override l1Address; + /// @dev Address of the native token vault. + address public override nativeTokenVault; + + /// @dev The assetId of the token. + bytes32 public override assetId; + modifier onlyBridge() { if (msg.sender != l2Bridge) { revert Unauthorized(msg.sender); @@ -70,6 +77,7 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri } l2Bridge = _l2Bridge; l1Address = _l1Address; + nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR; // Set decoded values for name and symbol. __ERC20_init_unchained(name_, symbol_); diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 34ce2ecd1..7cb2bd1ce 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -2,9 +2,11 @@ pragma solidity ^0.8.20; +import {IAssetRouterBase} from "./IAssetRouterBase.sol"; + /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -interface IL2AssetRouter { +interface IL2AssetRouter is IAssetRouterBase { event WithdrawalInitiatedAssetRouter( uint256 chainId, address indexed l2Sender, diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index b75eb58e8..e50dd4da1 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -101,7 +101,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { function setAssetHandlerAddressThisChain( bytes32 _assetRegistrationData, address _assetHandlerAddress - ) external override(AssetRouterBase) { + ) external override(AssetRouterBase, IAssetRouterBase) { _setAssetHandlerAddressThisChain(L2_NATIVE_TOKEN_VAULT_ADDR, _assetRegistrationData, _assetHandlerAddress); } @@ -117,7 +117,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { uint256, bytes32 _assetId, bytes calldata _transferData - ) public override onlyAssetRouterCounterpartOrSelf(L1_CHAIN_ID) { + ) public override(AssetRouterBase, IAssetRouterBase) onlyAssetRouterCounterpartOrSelf(L1_CHAIN_ID) { if (_assetId == BASE_TOKEN_ASSET_ID) { revert AssetIdNotSupported(BASE_TOKEN_ASSET_ID); } @@ -250,7 +250,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { } function _withdrawLegacy(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) internal { - bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, getL1TokenAddress(_l2Token)); + bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress(_l2Token)); bytes memory data = abi.encode(_amount, _l1Receiver); _withdrawSender(assetId, data, _sender, false); } @@ -258,7 +258,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @notice Legacy getL1TokenAddress. /// @param _l2Token The address of token on L2. /// @return The address of token on L1. - function getL1TokenAddress(address _l2Token) public view returns (address) { + function l1TokenAddress(address _l2Token) public view returns (address) { return IBridgedStandardToken(_l2Token).l1Address(); } diff --git a/l1-contracts/contracts/bridge/interfaces/IBridgedStandardToken.sol b/l1-contracts/contracts/bridge/interfaces/IBridgedStandardToken.sol index 952bc1871..2ba2a081b 100644 --- a/l1-contracts/contracts/bridge/interfaces/IBridgedStandardToken.sol +++ b/l1-contracts/contracts/bridge/interfaces/IBridgedStandardToken.sol @@ -18,4 +18,8 @@ interface IBridgedStandardToken { function originToken() external view returns (address); function l2Bridge() external view returns (address); + + function assetId() external view returns (bytes32); + + function nativeTokenVault() external view returns (address); } diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 02e865d5d..749e4697b 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -390,7 +390,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 bytes memory _erc20Data, address _expectedToken ) internal { - address deployedToken = _deployBridgedToken(_originChainId, _originToken, _erc20Data); + address deployedToken = _deployBridgedToken(_originChainId, _assetId, _originToken, _erc20Data); if (deployedToken != _expectedToken) { revert AddressMismatch(_expectedToken, deployedToken); } @@ -412,13 +412,18 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @return The address of the beacon proxy (bridged token). function _deployBridgedToken( uint256 _originChainId, + bytes32 _assetId, address _originToken, bytes memory _erc20Data ) internal returns (address) { bytes32 salt = _getCreate2Salt(_originChainId, _originToken); BeaconProxy l2Token = _deployBeaconProxy(salt); - uint256 tokenOriginChainId = BridgedStandardERC20(address(l2Token)).bridgeInitialize(_originToken, _erc20Data); + uint256 tokenOriginChainId = BridgedStandardERC20(address(l2Token)).bridgeInitialize( + _assetId, + _originToken, + _erc20Data + ); tokenOriginChainId = tokenOriginChainId == 0 ? L1_CHAIN_ID : tokenOriginChainId; originChainId[DataEncoding.encodeNTVAssetId(tokenOriginChainId, _originToken)] = tokenOriginChainId; return address(l2Token); diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index cd3e03baf..adeb2d2b9 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -1351,7 +1351,9 @@ export class Deployer { const alreadyRegisteredInCTM = (await chainTypeManager.getZKChain(inputChainId)) != ethers.constants.AddressZero; if (l2LegacySharedBridge) { - console.log("Setting L2 legacy shared bridge in L1Nullifier"); + if (this.verbose) { + console.log("Setting L2 legacy shared bridge in L1Nullifier"); + } await this.setL2LegacySharedBridgeInL1Nullifier(inputChainId); nonce++; } From 18167a27f76ccc8c59697911c120a47ca97d75ce Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 18 Sep 2024 18:00:03 +0100 Subject: [PATCH 02/42] fixes --- l1-contracts/contracts/bridge/L1Nullifier.sol | 4 ++-- .../bridge/asset-router/AssetRouterBase.sol | 6 ++++++ .../bridge/asset-router/L1AssetRouter.sol | 6 +++--- .../bridge/asset-router/L2AssetRouter.sol | 20 +++++++++++++++++++ .../bridge/ntv/INativeTokenVault.sol | 7 +++++-- .../bridge/ntv/L2NativeTokenVault.sol | 8 +++++--- .../contracts/bridge/ntv/NativeTokenVault.sol | 15 +++++++++----- .../bridgehub/CTMDeploymentTracker.sol | 2 +- .../bridgehub/ICTMDeploymentTracker.sol | 2 +- .../_L1SharedBridge_Shared.t.sol | 2 +- 10 files changed, 54 insertions(+), 18 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 6dda093c9..100e0b04e 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -642,7 +642,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, uint16 _l2TxNumberInBatch, bytes32[] calldata _merkleProof ) external override { - bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).getAssetId(block.chainid, _l1Token); + bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Token); // For legacy deposits, the l2 receiver is not required to check tx data hash // bytes memory transferData = abi.encode(_amount, _depositSender); bytes memory assetData = abi.encode(_amount, address(0)); @@ -696,7 +696,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, ) external override onlyLegacyBridge { bytes memory assetData = abi.encode(_amount, _depositSender); /// the legacy bridge can only be used with L1 native tokens. - bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).getAssetId(block.chainid, _l1Asset); + bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Asset); _verifyAndClearFailedTransfer({ _checkedInLegacyBridge: true, diff --git a/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol b/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol index 1a27e825f..d6ca41bdf 100644 --- a/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol +++ b/l1-contracts/contracts/bridge/asset-router/AssetRouterBase.sol @@ -149,6 +149,12 @@ abstract contract AssetRouterBase is IAssetRouterBase, Ownable2StepUpgradeable, }); } + /// @notice Ensures that token is registered with native token vault. + /// @dev Only used when deposit is made with legacy data encoding format. + /// @param _token The native token address which should be registered with native token vault. + /// @return assetId The asset ID of the token provided. + function _ensureTokenRegisteredWithNTV(address _token) internal virtual returns (bytes32 assetId); + /*////////////////////////////////////////////////////////////// PAUSE //////////////////////////////////////////////////////////////*/ diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 6e44f4cae..20e423f47 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -354,10 +354,10 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { /// @notice Ensures that token is registered with native token vault. /// @dev Only used when deposit is made with legacy data encoding format. - /// @param _token The L1 token address which should be registered with native token vault. + /// @param _token The native token address which should be registered with native token vault. /// @return assetId The asset ID of the token provided. - function _ensureTokenRegisteredWithNTV(address _token) internal returns (bytes32 assetId) { - assetId = nativeTokenVault.getAssetId(block.chainid, _token); + function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { + assetId = nativeTokenVault.calculateAssetId(block.chainid, _token); if (nativeTokenVault.tokenAddress(assetId) == address(0)) { nativeTokenVault.registerToken(_token); } diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index e50dd4da1..2a8e1cff9 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -126,6 +126,22 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { emit DepositFinalizedAssetRouter(L1_CHAIN_ID, _assetId, _transferData); } + /*////////////////////////////////////////////////////////////// + Internal & Helpers + //////////////////////////////////////////////////////////////*/ + + // kl todo add handle Legaacy data here, which calls esureTokenRegisteredWithNTV + // have handleLegacyData called from somewhere. + + /// @inheritdoc AssetRouterBase + function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { + IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); + assetId = nativeTokenVault.calculateAssetId(block.chainid, _token); + if (nativeTokenVault.tokenAddress(assetId) == address(0)) { + nativeTokenVault.registerToken(_token); + } + } + /*////////////////////////////////////////////////////////////// LEGACY FUNCTIONS //////////////////////////////////////////////////////////////*/ @@ -139,6 +155,10 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { _withdrawSender(_assetId, _assetData, msg.sender, true); } + function withdrawToken(bytes32 _assetId, bytes memory _assetData) public override { + _withdrawSender(_assetId, _assetData, msg.sender, true); + } + /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 /// where tokens would be unlocked /// @param _assetId The asset id of the withdrawn asset diff --git a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol index 0cd78cf54..f6887f6e6 100644 --- a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol @@ -25,8 +25,8 @@ interface INativeTokenVault { /// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration. function registerToken(address _l1Token) external; - /// @notice Used to get the assetId of a token - function getAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32); + /// @notice Used to calculate the assetId of a token + function calculateAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32); /// @notice Used to get the the ERC20 data for a token function getERC20Getters(address _token, uint256 _originChainId) external view returns (bytes memory); @@ -34,6 +34,9 @@ interface INativeTokenVault { /// @notice Used to get the token address of an assetId function tokenAddress(bytes32 assetId) external view returns (address); + /// @notice Used to get the assetId of a token + function assetId(address token) external view returns (bytes32); + /// @notice Used to get the expected bridged token address corresponding to its native counterpart function calculateCreate2TokenAddress(uint256 _originChainId, address _originToken) external view returns (address); } diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index e96a6d289..a6a0ff62d 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -85,9 +85,10 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { /// @notice Sets the legacy token asset ID for the given L2 token address. function setLegacyTokenAssetId(address _l2TokenAddress) public { address l1TokenAddress = L2_LEGACY_SHARED_BRIDGE.l1TokenAddress(_l2TokenAddress); - bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress); - tokenAddress[assetId] = _l2TokenAddress; - originChainId[assetId] = L1_CHAIN_ID; + bytes32 newAssetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, l1TokenAddress); + tokenAddress[newAssetId] = _l2TokenAddress; + assetId[_l2TokenAddress] = newAssetId; + originChainId[newAssetId] = L1_CHAIN_ID; } /// @notice Ensures that the token is deployed. @@ -114,6 +115,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { revert AddressMismatch(_originToken, l1LegacyToken); } tokenAddress[_assetId] = expectedToken; + assetId[expectedToken] = _assetId; } else { super._ensureTokenDeployedInner({ _originChainId: _originChainId, diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 749e4697b..5fb6394f5 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -52,12 +52,15 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev A mapping assetId => tokenAddress mapping(bytes32 assetId => address tokenAddress) public tokenAddress; + /// @dev A mapping tokenAddress => assetId + mapping(address tokenAddress => bytes32 assetId) public assetId; + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[47] private __gap; + uint256[46] private __gap; /// @notice Checks that the message sender is the bridgehub. modifier onlyAssetRouter() { @@ -322,7 +325,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @notice Returns the parsed assetId. /// @param _nativeToken The address of the token to be parsed. /// @dev Shows the assetId for a given chain and token address - function getAssetId(uint256 _chainId, address _nativeToken) external pure override returns (bytes32) { + function calculateAssetId(uint256 _chainId, address _nativeToken) external pure override returns (bytes32) { return DataEncoding.encodeNTVAssetId(_chainId, _nativeToken); } @@ -330,10 +333,11 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 /// @dev It does not perform any checks for the correctnesss of the token contract. /// @param _nativeToken The address of the token to be registered. function _unsafeRegisterNativeToken(address _nativeToken) internal { - bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); + bytes32 newAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _nativeToken); ASSET_ROUTER.setAssetHandlerAddressThisChain(bytes32(uint256(uint160(_nativeToken))), address(this)); - tokenAddress[assetId] = _nativeToken; - originChainId[assetId] = block.chainid; + tokenAddress[newAssetId] = _nativeToken; + assetId[_nativeToken] = newAssetId; + originChainId[newAssetId] = block.chainid; } function _handleChainBalanceIncrease( @@ -396,6 +400,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 } tokenAddress[_assetId] = _expectedToken; + assetId[_expectedToken] = _assetId; } /// @notice Calculates the bridged token address corresponding to native token counterpart. diff --git a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol index 6ffee2482..781d12640 100644 --- a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol @@ -115,7 +115,7 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable require(_assetHandlerAddressOnCounterpart == L2_BRIDGEHUB_ADDR, "CTMDT: wrong counter part"); } - function getAssetId(address _l1CTM) public view override returns (bytes32) { + function calculateAssetId(address _l1CTM) public view override returns (bytes32) { return keccak256(abi.encode(block.chainid, address(this), bytes32(uint256(uint160(_l1CTM))))); } diff --git a/l1-contracts/contracts/bridgehub/ICTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/ICTMDeploymentTracker.sol index 5f75aa990..1b7558f29 100644 --- a/l1-contracts/contracts/bridgehub/ICTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/ICTMDeploymentTracker.sol @@ -22,5 +22,5 @@ interface ICTMDeploymentTracker is IL1AssetDeploymentTracker { function registerCTMAssetOnL1(address _ctmAddress) external; - function getAssetId(address _l1CTM) external view returns (bytes32); + function calculateAssetId(address _l1CTM) external view returns (bytes32); } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol index c67fa6a36..abbf05a8c 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol @@ -211,7 +211,7 @@ contract L1AssetRouterTest is Test { // vm.mockCall( // address(bridgehubAddress), // abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector, address(token)), - // abi.encode(nativeTokenVault.getAssetId(address(token))) + // abi.encode(nativeTokenVault.calculateAssetId(address(token))) // ); token.mint(address(nativeTokenVault), amount); From 56b4e1faeba194b82ab0784b000ed5acaf5380b5 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 19 Sep 2024 09:15:26 +0100 Subject: [PATCH 03/42] register Token new function new msg format --- l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 2a8e1cff9..36a9ec5a1 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -155,8 +155,9 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { _withdrawSender(_assetId, _assetData, msg.sender, true); } - function withdrawToken(bytes32 _assetId, bytes memory _assetData) public override { - _withdrawSender(_assetId, _assetData, msg.sender, true); + function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { + bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); + _withdrawSender(assetId, _assetData, msg.sender, true); } /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 From 1e009c8a507aa84eff2cac84c4c88cdfd040f563 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 26 Sep 2024 21:59:25 +0100 Subject: [PATCH 04/42] copied some test mods from interop branch --- .../l2/unit/erc20/L2Erc20BridgeTest.t.sol | 30 +++-- .../test/foundry/l2/unit/utils/L2Utils.sol | 110 +++++++++++++----- 2 files changed, 99 insertions(+), 41 deletions(-) diff --git a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol b/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol index 5e195353e..62c74ad36 100644 --- a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol @@ -18,7 +18,7 @@ import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; -import {L2Utils} from "../utils/L2Utils.sol"; +import {L2Utils, SystemContractsArgs} from "../utils/L2Utils.sol"; contract L2Erc20BridgeTest is Test { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. @@ -59,16 +59,24 @@ contract L2Erc20BridgeTest is Test { beaconProxyBytecodeHash := extcodehash(beaconProxy) } - L2Utils.initSystemContracts(); - L2Utils.forceDeployAssetRouter(L1_CHAIN_ID, ERA_CHAIN_ID, ownerWallet, l1BridgeWallet, address(0)); - L2Utils.forceDeployNativeTokenVault({ - _l1ChainId: L1_CHAIN_ID, - _aliasedOwner: ownerWallet, - _l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, - _legacySharedBridge: address(0), - _l2TokenBeacon: address(beacon), - _contractsDeployedAlready: true - }); + address l2SharedBridge = L2Utils.deploySharedBridgeLegacy( + L1_CHAIN_ID, + ERA_CHAIN_ID, + ownerWallet, + l1BridgeWallet, + beaconProxyBytecodeHash + ); + L2Utils.initSystemContracts(SystemContractsArgs({ + l1ChainId: L1_CHAIN_ID, + eraChainId: ERA_CHAIN_ID, + l1AssetRouter: L2_ASSET_ROUTER_ADDR, + legacySharedBridge: l2SharedBridge, + l2TokenBeacon: address(beacon), + l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, + aliasedOwner: ownerWallet, + contractsDeployedAlready: true + })); + } function performDeposit(address depositor, address receiver, uint256 amount) internal { diff --git a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol index 9da81fe5d..3d41ddeaa 100644 --- a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol @@ -5,16 +5,32 @@ pragma solidity ^0.8.20; import {Vm} from "forge-std/Vm.sol"; import "forge-std/console.sol"; -import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR, L2_MESSAGE_ROOT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IContractDeployer, L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +struct SystemContractsArgs { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + address legacySharedBridge; + address l2TokenBeacon; + bytes32 l2TokenProxyBytecodeHash; + address aliasedOwner; + bool contractsDeployedAlready; +} + library L2Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); Vm internal constant vm = Vm(VM_ADDRESS); @@ -23,6 +39,7 @@ library L2Utils { string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; + string internal constant BRIDGEHUB_PATH = "./zkout/Bridgehub.sol/Bridgehub.json"; /// @notice Returns the bytecode of a given era contract from a `zkout` folder. function readEraBytecode(string memory _filename) internal returns (bytes memory bytecode) { @@ -54,9 +71,33 @@ library L2Utils { * @dev Initializes the system contracts. * @dev It is a hack needed to make the tests be able to call system contracts directly. */ - function initSystemContracts() internal { + function initSystemContracts(SystemContractsArgs memory _args) internal { bytes memory contractDeployerBytecode = readSystemContractsBytecode("ContractDeployer"); vm.etch(DEPLOYER_SYSTEM_CONTRACT, contractDeployerBytecode); + forceDeploySystemContracts(_args); + } + + function forceDeploySystemContracts( + SystemContractsArgs memory _args + ) internal { + forceDeployBridgehub(_args.l1ChainId, _args.eraChainId, _args.aliasedOwner, _args.l1AssetRouter, _args.legacySharedBridge); + forceDeployAssetRouter(_args.l1ChainId, _args.eraChainId, _args.aliasedOwner,_args.l1AssetRouter, _args.legacySharedBridge); + forceDeployNativeTokenVault(_args.l1ChainId, _args.aliasedOwner, _args.l2TokenProxyBytecodeHash, _args.legacySharedBridge, _args.l2TokenBeacon, _args.contractsDeployedAlready); + } + + function forceDeployBridgehub( + uint256 _l1ChainId, + uint256 _eraChainId, + address _aliasedOwner, + address _l1AssetRouter, + address _legacySharedBridge + ) internal { + new Bridgehub(_l1ChainId, _aliasedOwner, 100); + forceDeployWithConstructor("Bridgehub", L2_BRIDGEHUB_ADDR, abi.encode(_l1ChainId, _aliasedOwner, 100)); + Bridgehub bridgehub = Bridgehub(L2_BRIDGEHUB_ADDR); + address l1CTMDeployer = address(0x1); + vm.prank(_aliasedOwner); + bridgehub.setAddresses(L2_ASSET_ROUTER_ADDR, ICTMDeploymentTracker(l1CTMDeployer), IMessageRoot(L2_MESSAGE_ROOT_ADDR)); } /// @notice Deploys the L2AssetRouter contract. @@ -76,22 +117,8 @@ library L2Utils { { new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner); } + forceDeployWithConstructor("L2AssetRouter", L2_ASSET_ROUTER_ADDR, abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner)); - bytes memory bytecode = readEraBytecode("L2AssetRouter"); - - bytes32 bytecodehash = L2ContractHelper.hashL2Bytecode(bytecode); - - IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); - deployments[0] = IContractDeployer.ForceDeployment({ - bytecodeHash: bytecodehash, - newAddress: L2_ASSET_ROUTER_ADDR, - callConstructor: true, - value: 0, - input: abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner) - }); - - vm.prank(L2_FORCE_DEPLOYER_ADDR); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); } /// @notice Deploys the L2NativeTokenVault contract. @@ -123,34 +150,57 @@ library L2Utils { _baseTokenAssetId: ethAssetId }); } + forceDeployWithConstructor("L2NativeTokenVault", L2_NATIVE_TOKEN_VAULT_ADDR, abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready, address(0), ethAssetId)); + } - bytes memory bytecode = readEraBytecode("L2NativeTokenVault"); + function forceDeployWithConstructor( + string memory _contractName, + address _address, + bytes memory _constuctorArgs + ) public { + bytes memory bytecode = readEraBytecode(_contractName); bytes32 bytecodehash = L2ContractHelper.hashL2Bytecode(bytecode); IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); deployments[0] = IContractDeployer.ForceDeployment({ bytecodeHash: bytecodehash, - newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, + newAddress: _address, callConstructor: true, value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode( - _l1ChainId, - _aliasedOwner, - _l2TokenProxyBytecodeHash, - _legacySharedBridge, - _l2TokenBeacon, - _contractsDeployedAlready, - address(0), - ethAssetId - ) + input: _constuctorArgs }); vm.prank(L2_FORCE_DEPLOYER_ADDR); IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); } + function deploySharedBridgeLegacy( + uint256 _l1ChainId, + uint256 _eraChainId, + address _aliasedOwner, + address _l1SharedBridge, + bytes32 _l2TokenProxyBytecodeHash + ) internal returns (address) { + bytes32 ethAssetId = DataEncoding.encodeNTVAssetId(_l1ChainId, ETH_TOKEN_ADDRESS); + + L2SharedBridgeLegacy bridge = new L2SharedBridgeLegacy(); + console.log("bridge", address(bridge)); + address proxyAdmin = address(0x1); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(bridge), + proxyAdmin, + abi.encodeWithSelector( + L2SharedBridgeLegacy.initialize.selector, + _l1SharedBridge, + _l2TokenProxyBytecodeHash, + _aliasedOwner + ) + ); + console.log("proxy", address(proxy)); + return address(proxy); + } + /// @notice Encodes the token data. /// @param name The name of the token. /// @param symbol The symbol of the token. From acdf8e99caa64cf1fcadda158e1e510ad286e1ff Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 27 Sep 2024 13:08:26 +0100 Subject: [PATCH 05/42] some fixes --- l1-contracts/.env | 1 + .../chain-interfaces/IAdmin.sol | 3 + l1-contracts/deploy-scripts/DeployL1.s.sol | 231 +++++++++--------- .../integration/BridgeHubInvariantTests.t.sol | 6 +- .../l1/integration/BridgehubTests.t.sol | 6 +- .../l1/integration/DeploymentTest.t.sol | 90 +++++-- ...atewayTests.t.sol => L1GatewayTests.t.sol} | 159 ++++-------- .../_SharedL1ContractDeployer.t.sol | 47 ++-- .../integration/_SharedZKChainDeployer.t.sol | 2 +- 9 files changed, 282 insertions(+), 263 deletions(-) rename l1-contracts/test/foundry/l1/integration/{GatewayTests.t.sol => L1GatewayTests.t.sol} (65%) diff --git a/l1-contracts/.env b/l1-contracts/.env index 25ec2b87f..0cbe2dbd1 100644 --- a/l1-contracts/.env +++ b/l1-contracts/.env @@ -44,3 +44,4 @@ TOKENS_CONFIG=/script-config/config-deploy-erc20.toml ZK_CHAIN_CONFIG=/script-config/register-zk-chain.toml ZK_CHAIN_OUTPUT=/script-out/output-deploy-zk-chain-era.toml FORCE_DEPLOYMENTS_CONFIG=/script-config/generate-force-deployments-data.toml +GATEWAY_PREPARATION_L1_CONFIG=/script-config/gateway-preparation-l1.toml diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol index 4a2ad7170..daf155e3a 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol @@ -6,6 +6,7 @@ import {IZKChainBase} from "../chain-interfaces/IZKChainBase.sol"; import {Diamond} from "../libraries/Diamond.sol"; import {FeeParams, PubdataPricingMode} from "../chain-deps/ZKChainStorage.sol"; +import {ZKChainCommitment} from "../../common/Config.sol"; /// @title The interface of the Admin Contract that controls access rights for contract management. /// @author Matter Labs @@ -149,4 +150,6 @@ interface IAdmin is IZKChainBase { /// @dev Similar to IL1AssetHandler interface, used to receive chains. function forwardedBridgeMint(bytes calldata _data, bool _contractAlreadyDeployed) external payable; + + function prepareChainCommitment() external view returns (ZKChainCommitment memory commitment); } diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 1112d917e..f06ec50f0 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -69,110 +69,111 @@ struct FixedForceDeploymentsData { bytes32 messageRootBytecodeHash; } -contract DeployL1Script is Script { - using stdToml for string; +// solhint-disable-next-line gas-struct-packing +struct DeployedAddresses { + BridgehubDeployedAddresses bridgehub; + StateTransitionDeployedAddresses stateTransition; + BridgesDeployedAddresses bridges; + L1NativeTokenVaultAddresses vaults; + DataAvailabilityDeployedAddresses daAddresses; + address transparentProxyAdmin; + address governance; + address chainAdmin; + address accessControlRestrictionAddress; + address blobVersionedHashRetriever; + address validatorTimelock; + address create2Factory; +} - address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; - address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; +// solhint-disable-next-line gas-struct-packing +struct L1NativeTokenVaultAddresses { + address l1NativeTokenVaultImplementation; + address l1NativeTokenVaultProxy; +} - // solhint-disable-next-line gas-struct-packing - struct DeployedAddresses { - BridgehubDeployedAddresses bridgehub; - StateTransitionDeployedAddresses stateTransition; - BridgesDeployedAddresses bridges; - L1NativeTokenVaultAddresses vaults; - DataAvailabilityDeployedAddresses daAddresses; - address transparentProxyAdmin; - address governance; - address chainAdmin; - address accessControlRestrictionAddress; - address blobVersionedHashRetriever; - address validatorTimelock; - address create2Factory; - } +struct DataAvailabilityDeployedAddresses { + address l1RollupDAValidator; + address l1ValidiumDAValidator; +} - // solhint-disable-next-line gas-struct-packing - struct L1NativeTokenVaultAddresses { - address l1NativeTokenVaultImplementation; - address l1NativeTokenVaultProxy; - } +// solhint-disable-next-line gas-struct-packing +struct BridgehubDeployedAddresses { + address bridgehubImplementation; + address bridgehubProxy; + address ctmDeploymentTrackerImplementation; + address ctmDeploymentTrackerProxy; + address messageRootImplementation; + address messageRootProxy; +} - struct DataAvailabilityDeployedAddresses { - address l1RollupDAValidator; - address l1ValidiumDAValidator; - } +// solhint-disable-next-line gas-struct-packing +struct BridgesDeployedAddresses { + address erc20BridgeImplementation; + address erc20BridgeProxy; + address sharedBridgeImplementation; + address sharedBridgeProxy; + address l1NullifierImplementation; + address l1NullifierProxy; + address bridgedStandardERC20Implementation; + address bridgedTokenBeacon; +} - // solhint-disable-next-line gas-struct-packing - struct BridgehubDeployedAddresses { - address bridgehubImplementation; - address bridgehubProxy; - address ctmDeploymentTrackerImplementation; - address ctmDeploymentTrackerProxy; - address messageRootImplementation; - address messageRootProxy; - } +// solhint-disable-next-line gas-struct-packing +struct Config { + uint256 l1ChainId; + address deployerAddress; + uint256 eraChainId; + address ownerAddress; + bool testnetVerifier; + ContractsConfig contracts; + TokensConfig tokens; +} - // solhint-disable-next-line gas-struct-packing - struct BridgesDeployedAddresses { - address erc20BridgeImplementation; - address erc20BridgeProxy; - address sharedBridgeImplementation; - address sharedBridgeProxy; - address l1NullifierImplementation; - address l1NullifierProxy; - address bridgedStandardERC20Implementation; - address bridgedTokenBeacon; - } +// solhint-disable-next-line gas-struct-packing +struct ContractsConfig { + bytes32 create2FactorySalt; + address create2FactoryAddr; + address multicall3Addr; + uint256 validatorTimelockExecutionDelay; + bytes32 genesisRoot; + uint256 genesisRollupLeafIndex; + bytes32 genesisBatchCommitment; + uint256 latestProtocolVersion; + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; + uint256 priorityTxMaxGasLimit; + PubdataPricingMode diamondInitPubdataPricingMode; + uint256 diamondInitBatchOverheadL1Gas; + uint256 diamondInitMaxPubdataPerBatch; + uint256 diamondInitMaxL2GasPerBatch; + uint256 diamondInitPriorityTxMaxPubdata; + uint256 diamondInitMinimalL2GasPrice; + address governanceSecurityCouncilAddress; + uint256 governanceMinDelay; + uint256 maxNumberOfChains; + bytes diamondCutData; + bytes32 bootloaderHash; + bytes32 defaultAAHash; +} + +struct TokensConfig { + address tokenWethAddress; +} + +contract DeployL1Script is Script { + using stdToml for string; + + address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; + address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; - // solhint-disable-next-line gas-struct-packing - struct Config { - uint256 l1ChainId; - address deployerAddress; - uint256 eraChainId; - address ownerAddress; - bool testnetVerifier; - ContractsConfig contracts; - TokensConfig tokens; - } // solhint-disable-next-line gas-struct-packing struct GeneratedData { bytes forceDeploymentsData; } - // solhint-disable-next-line gas-struct-packing - struct ContractsConfig { - bytes32 create2FactorySalt; - address create2FactoryAddr; - address multicall3Addr; - uint256 validatorTimelockExecutionDelay; - bytes32 genesisRoot; - uint256 genesisRollupLeafIndex; - bytes32 genesisBatchCommitment; - uint256 latestProtocolVersion; - bytes32 recursionNodeLevelVkHash; - bytes32 recursionLeafLevelVkHash; - bytes32 recursionCircuitsSetVksHash; - uint256 priorityTxMaxGasLimit; - PubdataPricingMode diamondInitPubdataPricingMode; - uint256 diamondInitBatchOverheadL1Gas; - uint256 diamondInitMaxPubdataPerBatch; - uint256 diamondInitMaxL2GasPerBatch; - uint256 diamondInitPriorityTxMaxPubdata; - uint256 diamondInitMinimalL2GasPrice; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - uint256 maxNumberOfChains; - bytes diamondCutData; - bytes32 bootloaderHash; - bytes32 defaultAAHash; - } - - struct TokensConfig { - address tokenWethAddress; - } - - Config internal config; + Config public config; GeneratedData internal generatedData; DeployedAddresses internal addresses; @@ -186,6 +187,18 @@ contract DeployL1Script is Script { runInner(vm.envString("L1_CONFIG"), vm.envString("L1_OUTPUT")); } + function runForL2Test() public { + runL2Inner(vm.envString("L2_CONFIG"), vm.envString("L2_OUTPUT")); + } + + function getAddresses() public view returns (DeployedAddresses memory) { + return addresses; + } + + function getConfig() public view returns (Config memory) { + return config; + } + function runInner(string memory inputPath, string memory outputPath) internal { string memory root = vm.projectRoot(); inputPath = string.concat(root, inputPath); @@ -232,36 +245,16 @@ contract DeployL1Script is Script { saveOutput(outputPath); } - function getBridgehubProxyAddress() public view returns (address) { - return addresses.bridgehub.bridgehubProxy; - } - - function getSharedBridgeProxyAddress() public view returns (address) { - return addresses.bridges.sharedBridgeProxy; - } - - function getNativeTokenVaultProxyAddress() public view returns (address) { - return addresses.vaults.l1NativeTokenVaultProxy; - } - - function getL1NullifierProxyAddress() public view returns (address) { - return addresses.bridges.l1NullifierProxy; - } - - function getOwnerAddress() public view returns (address) { - return config.ownerAddress; - } - - function getCTM() public view returns (address) { - return addresses.stateTransition.chainTypeManagerProxy; - } + function runL2Inner(string memory inputPath, string memory outputPath) internal { + string memory root = vm.projectRoot(); + inputPath = string.concat(root, inputPath); + outputPath = string.concat(root, outputPath); - function getInitialDiamondCutData() public view returns (bytes memory) { - return config.contracts.diamondCutData; - } + initializeConfig(inputPath); - function getCTMDeploymentTrackerAddress() public view returns (address) { - return addresses.bridgehub.ctmDeploymentTrackerProxy; + // instantiateCreate2Factory(); + // deployIfNeededMulticall3(); + deployChainTypeManagerContract(); } function initializeConfig(string memory configPath) internal { diff --git a/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol b/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol index 6eb78944d..5d23e1ad6 100644 --- a/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol @@ -509,7 +509,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -565,7 +565,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -643,7 +643,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe } function getAddressesToExclude() public returns (address[] memory) { - addressesToExclude.push(bridgehubProxyAddress); + addressesToExclude.push(bridgeHubProxyAddress); addressesToExclude.push(address(sharedBridge)); for (uint256 i = 0; i < users.length; i++) { diff --git a/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol b/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol index 09b07cca0..a9e54825a 100644 --- a/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol @@ -509,7 +509,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -565,7 +565,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -643,7 +643,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe } function getAddressesToExclude() public returns (address[] memory) { - addressesToExclude.push(bridgehubProxyAddress); + addressesToExclude.push(bridgeHubProxyAddress); addressesToExclude.push(address(sharedBridge)); for (uint256 i = 0; i < users.length; i++) { diff --git a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol index 9224eea05..5f85d7d66 100644 --- a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; +import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; + import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; @@ -22,6 +24,8 @@ import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAdd import {IL1ERC20Bridge} from "contracts/bridge/interfaces/IL1ERC20Bridge.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker { uint256 constant TEST_USERS_COUNT = 10; @@ -69,30 +73,27 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, // equals the balance of L1Shared bridge. function test_initialDeployment() public { uint256 chainId = zkChainIds[0]; - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - address newChainAddress = bridgehub.getZKChain(chainId); - address admin = IZKChain(bridgehub.getZKChain(chainId)).getAdmin(); - IChainTypeManager ctm = IChainTypeManager(bridgehub.chainTypeManager(chainId)); + address newChainAddress = bridgeHub.getZKChain(chainId); + address admin = IZKChain(bridgeHub.getZKChain(chainId)).getAdmin(); assertNotEq(admin, address(0)); assertNotEq(newChainAddress, address(0)); - address[] memory chainAddresses = bridgehub.getAllZKChains(); + address[] memory chainAddresses = bridgeHub.getAllZKChains(); assertEq(chainAddresses.length, 1); assertEq(chainAddresses[0], newChainAddress); - uint256[] memory chainIds = bridgehub.getAllZKChainChainIDs(); + uint256[] memory chainIds = bridgeHub.getAllZKChainChainIDs(); assertEq(chainIds.length, 1); assertEq(chainIds[0], chainId); - uint256 protocolVersion = ctm.getProtocolVersion(chainId); + uint256 protocolVersion = chainTypeManager.getProtocolVersion(chainId); assertEq(protocolVersion, 0); } - function test_bridgehubSetter() public { - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); + function test_bridgeHubSetter() public { uint256 chainId = zkChainIds[0]; - IChainTypeManager chainTypeManager = IChainTypeManager(bridgehub.chainTypeManager(chainId)); + // IChainTypeManager chainTypeManager = IChainTypeManager(bridgeHub.chainTypeManager(chainId)); uint256 randomChainId = 123456; vm.mockCall( @@ -100,14 +101,75 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, abi.encodeWithSelector(IChainTypeManager.getZKChainLegacy.selector, randomChainId), abi.encode(address(0x01)) ); - vm.store(address(bridgehub), keccak256(abi.encode(randomChainId, 205)), bytes32(uint256(uint160(1)))); + vm.store(address(bridgeHub), keccak256(abi.encode(randomChainId, 205)), bytes32(uint256(uint160(1)))); vm.store( - address(bridgehub), + address(bridgeHub), keccak256(abi.encode(randomChainId, 204)), bytes32(uint256(uint160(address(chainTypeManager)))) ); - bridgehub.setLegacyBaseTokenAssetId(randomChainId); - bridgehub.setLegacyChainAddress(randomChainId); + bridgeHub.setLegacyBaseTokenAssetId(randomChainId); + bridgeHub.setLegacyChainAddress(randomChainId); + } + + function test_registerAlreadyDeployedZKChain() public { + address owner = Ownable(address(bridgeHub)).owner(); + + { + uint256 chainId = currentZKChainId++; + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); + + address chain = _deployZkChain( + chainId, + baseTokenAssetId, + address(bridgeHub.sharedBridge()), + owner, + chainTypeManager.protocolVersion(), + chainTypeManager.storedBatchZero(), + address(bridgeHub) + ); + + address stmAddr = IZKChain(chain).getChainTypeManager(); + + vm.startBroadcast(owner); + bridgeHub.addChainTypeManager(stmAddr); + bridgeHub.addTokenAssetId(baseTokenAssetId); + bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); + vm.stopBroadcast(); + + address bridgeHubStmForChain = bridgeHub.chainTypeManager(chainId); + bytes32 bridgeHubBaseAssetIdForChain = bridgeHub.baseTokenAssetId(chainId); + address bridgeHubChainAddressdForChain = bridgeHub.getZKChain(chainId); + address bhAddr = IZKChain(chain).getBridgehub(); + + assertEq(bridgeHubStmForChain, stmAddr); + assertEq(bridgeHubBaseAssetIdForChain, baseTokenAssetId); + assertEq(bridgeHubChainAddressdForChain, chain); + assertEq(bhAddr, address(bridgeHub)); + } + + { + uint256 chainId = currentZKChainId++; + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); + address chain = _deployZkChain( + chainId, + baseTokenAssetId, + address(bridgeHub.sharedBridge()), + owner, + chainTypeManager.protocolVersion(), + chainTypeManager.storedBatchZero(), + address(bridgeHub.sharedBridge()) + ); + + address stmAddr = IZKChain(chain).getChainTypeManager(); + + vm.startBroadcast(owner); + bridgeHub.addTokenAssetId(baseTokenAssetId); + vm.expectRevert( + abi.encodeWithSelector(IncorrectBridgeHubAddress.selector, address(bridgeHub.sharedBridge())) + ); + bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); + vm.stopBroadcast(); + } } // add this to be excluded from coverage report diff --git a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol similarity index 65% rename from l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol rename to l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index 0cfcbf049..443d431bb 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -5,6 +5,8 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; import "forge-std/console.sol"; +import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; + import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter, BridgehubMintCTMAssetData, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; @@ -21,11 +23,13 @@ import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; import {L2Message} from "contracts/common/Messaging.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IL1ERC20Bridge} from "contracts/bridge/interfaces/IL1ERC20Bridge.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {FinalizeL1DepositParams} from "contracts/bridge/L1Nullifier.sol"; -import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; @@ -35,6 +39,8 @@ import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {DeployedAddresses} from "deploy-scripts/DeployL1.s.sol"; + contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { uint256 constant TEST_USERS_COUNT = 10; address[] public users; @@ -85,15 +91,14 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T _initializeGatewayScript(); - vm.deal(Ownable(l1Script.getBridgehubProxyAddress()).owner(), 100000000000000000000000000000000000); - vm.deal(l1Script.getOwnerAddress(), 100000000000000000000000000000000000); - migratingChain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(migratingChainId)); - gatewayChain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(gatewayChainId)); + vm.deal(ecosystemConfig.ownerAddress, 100000000000000000000000000000000000); + migratingChain = IZKChain(IBridgehub(bridgeHub).getZKChain(migratingChainId)); + gatewayChain = IZKChain(IBridgehub(bridgeHub).getZKChain(gatewayChainId)); vm.deal(migratingChain.getAdmin(), 100000000000000000000000000000000000); vm.deal(gatewayChain.getAdmin(), 100000000000000000000000000000000000); // vm.deal(msg.sender, 100000000000000000000000000000000000); - // vm.deal(l1Script.getBridgehubProxyAddress(), 100000000000000000000000000000000000); + // vm.deal(bridgeHub, 100000000000000000000000000000000000); } // This is a method to simplify porting the tests for now. @@ -124,7 +129,7 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T _extractAccessControlRestriction(migratingChain.getAdmin()), migratingChainId ); - // require(bridgehub.settlementLayer()) + require(bridgeHub.settlementLayer(migratingChainId) == gatewayChainId, "Migration failed"); } function test_l2Registration() public { @@ -138,13 +143,14 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T gatewayScript.registerAssetIdInBridgehub(address(0x01), bytes32(0)); } - function test_finishMoveChain() public { - finishMoveChain(); - } - function test_startMessageToL3() public { - finishMoveChain(); - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); + IBridgehub bridgehub = IBridgehub(bridgeHub); uint256 expectedValue = 1000000000000000000000; L2TransactionRequestDirect memory request = _createL2TransactionRequestDirect( @@ -158,16 +164,6 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T bridgehub.requestL2TransactionDirect{value: expectedValue}(request); } - function test_forwardToL3OnGateway() public { - finishMoveChain(); - - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - vm.chainId(12345); - vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); - bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); - vm.stopBroadcast(); - } - function test_recoverFromFailedChainMigration() public { _setUpGatewayWithFilterer(); gatewayScript.migrateChainToGateway( @@ -177,16 +173,14 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T ); // Setup - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - IChainTypeManager ctm = IChainTypeManager(l1Script.getCTM()); + IBridgehub bridgehub = IBridgehub(bridgeHub); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); bytes memory transferData; { IZKChain chain = IZKChain(bridgehub.getZKChain(migratingChainId)); - bytes memory initialDiamondCut = l1Script.getInitialDiamondCutData(); bytes memory chainData = abi.encode(chain.getProtocolVersion()); - bytes memory ctmData = abi.encode(address(1), msg.sender, ctm.protocolVersion(), initialDiamondCut); + bytes memory ctmData = abi.encode(address(1), msg.sender, chainTypeManager.protocolVersion(), ecosystemConfig.contracts.diamondCutData); BridgehubBurnCTMAssetData memory data = BridgehubBurnCTMAssetData({ chainId: migratingChainId, ctmData: ctmData, @@ -244,74 +238,12 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T vm.stopBroadcast(); } - function test_registerAlreadyDeployedZKChain() public { - gatewayScript.governanceRegisterGateway(); - - IChainTypeManager stm = IChainTypeManager(l1Script.getCTM()); - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - address owner = Ownable(address(bridgeHub)).owner(); - - { - uint256 chainId = currentZKChainId++; - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); - - address chain = _deployZkChain( - chainId, - baseTokenAssetId, - address(bridgehub.sharedBridge()), - owner, - stm.protocolVersion(), - stm.storedBatchZero(), - address(bridgehub) - ); - - address stmAddr = IZKChain(chain).getChainTypeManager(); - - vm.startBroadcast(owner); - bridgeHub.addChainTypeManager(stmAddr); - bridgeHub.addTokenAssetId(baseTokenAssetId); - bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); - vm.stopBroadcast(); - - address bridgeHubStmForChain = bridgeHub.chainTypeManager(chainId); - bytes32 bridgeHubBaseAssetIdForChain = bridgeHub.baseTokenAssetId(chainId); - address bridgeHubChainAddressdForChain = bridgeHub.getZKChain(chainId); - address bhAddr = IZKChain(chain).getBridgehub(); - - assertEq(bridgeHubStmForChain, stmAddr); - assertEq(bridgeHubBaseAssetIdForChain, baseTokenAssetId); - assertEq(bridgeHubChainAddressdForChain, chain); - assertEq(bhAddr, address(bridgeHub)); - } - - { - uint256 chainId = currentZKChainId++; - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); - address chain = _deployZkChain( - chainId, - baseTokenAssetId, - address(bridgehub.sharedBridge()), - owner, - stm.protocolVersion(), - stm.storedBatchZero(), - address(bridgehub.sharedBridge()) - ); - - address stmAddr = IZKChain(chain).getChainTypeManager(); - - vm.startBroadcast(owner); - bridgeHub.addTokenAssetId(baseTokenAssetId); - vm.expectRevert( - abi.encodeWithSelector(IncorrectBridgeHubAddress.selector, address(bridgehub.sharedBridge())) - ); - bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); - vm.stopBroadcast(); - } + function test_migrateBackChain() public { + migrateBackChain(); } - function finishMoveChain() public { - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - IChainTypeManager ctm = IChainTypeManager(l1Script.getCTM()); + function migrateBackChain() public { + IBridgehub bridgehub = IBridgehub(bridgeHub); IZKChain migratingChain = IZKChain(bridgehub.getZKChain(migratingChainId)); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); @@ -320,21 +252,34 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T vm.stopBroadcast(); bytes32 baseTokenAssetId = keccak256("baseTokenAssetId"); - bytes memory initialDiamondCut = l1Script.getInitialDiamondCutData(); - bytes memory chainData = abi.encode(AdminFacet(address(migratingChain)).prepareChainCommitment()); - bytes memory ctmData = abi.encode(baseTokenAssetId, msg.sender, ctm.protocolVersion(), initialDiamondCut); - BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ - chainId: mintChainId, - baseTokenAssetId: baseTokenAssetId, - ctmData: ctmData, - chainData: chainData - }); - bytes memory bridgehubMintData = abi.encode(data); - vm.startBroadcast(address(bridgehub.sharedBridge())); + uint256 currentChainId = block.chainid; + // we are already on L1, so we have to set another chain id, it cannot be GW or mintChainId. vm.chainId(migratingChainId); - bridgehub.bridgeMint(gatewayChainId, assetId, bridgehubMintData); - vm.stopBroadcast(); + vm.mockCall( + address(bridgeHub), + abi.encodeWithSelector(IBridgehub.proveL2MessageInclusion.selector), + abi.encode(true) + ); + vm.mockCall( + address(bridgehub), + abi.encodeWithSelector(IBridgehub.ctmAssetIdFromChainId.selector), + abi.encode(assetId) + ); + vm.mockCall( + address(chainTypeManager), + abi.encodeWithSelector(IChainTypeManager.protocolVersion.selector), + abi.encode(chainTypeManager.protocolVersion()) + ); + + // bytes memory initialDiamondCut = getInitialDiamondCutData(); + // gatewayScript. + gatewayScript.finishMigrateChainFromGateway( + migratingChain, + migratingChainId, + gatewayChainId + ); + vm.chainId(currentChainId); assertEq(bridgehub.baseTokenAssetId(mintChainId), baseTokenAssetId); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index b180f6090..5b98d4089 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -11,12 +11,19 @@ import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployL1.s.sol"; + contract L1ContractDeployer is Test { using stdStorage for StdStorage; - address bridgehubProxyAddress; - address bridgehubOwnerAddress; + DeployL1Script l1Script; + DeployedAddresses public ecosystemAddresses; + Config public ecosystemConfig; + + address bridgeHubProxyAddress; + address bridgeHubOwnerAddress; Bridgehub bridgeHub; CTMDeploymentTracker ctmDeploymentTracker; @@ -25,7 +32,7 @@ contract L1ContractDeployer is Test { L1Nullifier public l1Nullifier; L1NativeTokenVault public l1NativeTokenVault; - DeployL1Script l1Script; + IChainTypeManager chainTypeManager; function _deployL1Contracts() internal { vm.setEnv("L1_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml"); @@ -38,28 +45,36 @@ contract L1ContractDeployer is Test { "ZK_CHAIN_OUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-era.toml" ); + vm.setEnv("GATEWAY_PREPARATION_L1_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml"); l1Script = new DeployL1Script(); l1Script.runForTest(); - bridgehubProxyAddress = l1Script.getBridgehubProxyAddress(); - bridgeHub = Bridgehub(bridgehubProxyAddress); - - address sharedBridgeProxyAddress = l1Script.getSharedBridgeProxyAddress(); - sharedBridge = L1AssetRouter(sharedBridgeProxyAddress); - - address l1NullifierProxyAddress = l1Script.getL1NullifierProxyAddress(); - l1Nullifier = L1Nullifier(l1NullifierProxyAddress); + ecosystemAddresses = l1Script.getAddresses(); + ecosystemConfig = l1Script.getConfig(); - address l1NativeTokenVaultProxyAddress = l1Script.getNativeTokenVaultProxyAddress(); - l1NativeTokenVault = L1NativeTokenVault(payable(l1NativeTokenVaultProxyAddress)); + bridgeHub = Bridgehub(ecosystemAddresses.bridgehub.bridgehubProxy); + chainTypeManager = IChainTypeManager(ecosystemAddresses.stateTransition.chainTypeManagerProxy); + ctmDeploymentTracker = CTMDeploymentTracker(ecosystemAddresses.bridgehub.ctmDeploymentTrackerProxy); - ctmDeploymentTracker = CTMDeploymentTracker(l1Script.getCTMDeploymentTrackerAddress()); + sharedBridge = L1AssetRouter(ecosystemAddresses.bridges.sharedBridgeProxy); + l1Nullifier = L1Nullifier(ecosystemAddresses.bridges.l1NullifierProxy); + l1NativeTokenVault = L1NativeTokenVault(payable(ecosystemAddresses.vaults.l1NativeTokenVaultProxy)); _acceptOwnership(); _setEraBatch(); - bridgehubOwnerAddress = bridgeHub.owner(); + bridgeHubOwnerAddress = bridgeHub.owner(); + } + + function _deployL2Contracts() internal { + vm.setEnv("L2_CONFIG", "/test/foundry/l2/integration/script-config/config-deploy-l2.toml"); + vm.setEnv("L2_OUTPUT", "/test/foundry/l2/integration/script-out/output-deploy-l2.toml"); + + l1Script = new DeployL1Script(); + l1Script.runForL2Test(); + + chainTypeManager = IChainTypeManager(ecosystemAddresses.stateTransition.chainTypeManagerProxy); } function _acceptOwnership() private { @@ -80,7 +95,7 @@ contract L1ContractDeployer is Test { function _registerNewToken(address _tokenAddress) internal { bytes32 tokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _tokenAddress); if (!bridgeHub.assetIdIsRegistered(tokenAssetId)) { - vm.prank(bridgehubOwnerAddress); + vm.prank(bridgeHubOwnerAddress); bridgeHub.addTokenAssetId(tokenAssetId); } } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol index 6220818ef..108f9ce4e 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol @@ -163,7 +163,7 @@ contract ZKChainDeployer is L1ContractDeployer { address _bridgeHub ) internal returns (address) { Diamond.DiamondCutData memory diamondCut = abi.decode( - l1Script.getInitialDiamondCutData(), + ecosystemConfig.contracts.diamondCutData, (Diamond.DiamondCutData) ); bytes memory initData; From 9791cec335c074cf17865ca06bbef4c51b3d8884 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 27 Sep 2024 13:25:22 +0100 Subject: [PATCH 06/42] linting --- .../bridge/asset-router/L2AssetRouter.sol | 4 +- l1-contracts/deploy-scripts/DeployL1.s.sol | 1 - .../deploy-scripts/GatewayPreparation.s.sol | 106 +++++++++++++- .../l1/integration/DeploymentTest.t.sol | 3 +- .../GatewayPreparationForTests.sol | 4 +- .../l1/integration/L1GatewayTests.t.sol | 15 +- .../integration/_SharedGatewayDeployer.t.sol | 4 +- .../_SharedL1ContractDeployer.t.sol | 6 +- .../script-config/gateway-preparation-l1.toml | 11 ++ .../foundry/l2/integration/L2Gateway.t.sol | 133 ++++++++++++++++++ .../l2/unit/erc20/L2Erc20BridgeTest.t.sol | 23 +-- .../test/foundry/l2/unit/utils/L2Utils.sol | 57 ++++++-- 12 files changed, 325 insertions(+), 42 deletions(-) create mode 100644 l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml create mode 100644 l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 36a9ec5a1..187039464 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -131,7 +131,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { //////////////////////////////////////////////////////////////*/ // kl todo add handle Legaacy data here, which calls esureTokenRegisteredWithNTV - // have handleLegacyData called from somewhere. + // have handleLegacyData called from somewhere. /// @inheritdoc AssetRouterBase function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { @@ -155,7 +155,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { _withdrawSender(_assetId, _assetData, msg.sender, true); } - function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { + function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); _withdrawSender(assetId, _assetData, msg.sender, true); } diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index f06ec50f0..b5a4a74a7 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -167,7 +167,6 @@ contract DeployL1Script is Script { address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; - // solhint-disable-next-line gas-struct-packing struct GeneratedData { bytes forceDeploymentsData; diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 91601d9bd..194f40ce9 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -22,6 +22,15 @@ import {GatewayTransactionFilterer} from "contracts/transactionFilterer/GatewayT import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {FinalizeL1DepositParams} from "contracts/bridge/interfaces/IL1Nullifier.sol"; + +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; /// @notice Scripts that is responsible for preparing the chain to become a gateway contract GatewayPreparation is Script { @@ -44,6 +53,7 @@ contract GatewayPreparation is Script { address gatewayChainAdmin; address gatewayAccessControlRestriction; address gatewayChainProxyAdmin; + address l1NullifierProxy; bytes gatewayDiamondCutData; } @@ -70,7 +80,7 @@ contract GatewayPreparation is Script { l1ChainId = block.chainid; string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-config/gateway-preparation-l1.toml"); + string memory path = string.concat(root, vm.envString("GATEWAY_PREPARATION_L1_CONFIG")); string memory toml = vm.readFile(path); // Config file must be parsed key by key, otherwise values returned @@ -89,7 +99,8 @@ contract GatewayPreparation is Script { gatewayDiamondCutData: toml.readBytes("$.gateway_diamond_cut_data"), gatewayChainAdmin: toml.readAddress("$.chain_admin"), gatewayAccessControlRestriction: toml.readAddress("$.access_control_restriction"), - gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin") + gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin"), + l1NullifierProxy: toml.readAddress("$.l1_nullifier_proxy") }); } @@ -240,7 +251,7 @@ contract GatewayPreparation is Script { uint256 currentSettlementLayer = IBridgehub(config.bridgehub).settlementLayer(chainId); if (currentSettlementLayer == config.gatewayChainId) { - console.log("Chain already whitelisted as settlement layer"); + console.log("Chain already using gateway as its settlement layer"); saveOutput(bytes32(0)); return; } @@ -272,6 +283,95 @@ contract GatewayPreparation is Script { saveOutput(l2TxHash); } + /// @dev Calling this function requires private key to the admin of the chain + function startMigrateChainFromGateway( + address chainAdmin, + address accessControlRestriction, + uint256 chainId + ) public { + initializeConfig(); + + bytes32 chainAssetId = IBridgehub(config.bridgehub).ctmAssetIdFromChainId(chainId); + + uint256 currentSettlementLayer = IBridgehub(config.bridgehub).settlementLayer(chainId); + // if (currentSettlementLayer == config.l1ChainId) { + // console.log("Chain already using L1 as its settlement layer"); + // saveOutput(bytes32(0)); + // return; + // } + + // if (currentSettlementLayer == address(0)) { + // console.log("Chain has never used Gateway as its settlement layer"); + // saveOutput(bytes32(0)); + // return; + // } + + // bytes memory bridgehubData = abi.encode( + // BridgehubBurnCTMAssetData({ + // chainId: chainId, + // ctmData: abi.encode(chainAdmin, config.diamondCutData), + // chainData: abi.encode(IZKChain(IBridgehub(config.bridgehub).getZKChain(chainId)).getProtocolVersion()) + // }) + // ); + + // L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); + // l2AssetRouter.withdraw( + // ctmAssetId, + // bridgehubBurnData + // ); + + // saveOutput(l2TxHash); + } + + function finishMigrateChainFromGateway( + IZKChain migratingChain, + uint256 migratingChainId, + uint256 gatewayChainId + ) public { + initializeConfig(); + + // TODO(EVM-746): Use L2-based chain admin contract + // address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); + IBridgehub bridgehub = IBridgehub(config.bridgehub); + bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); + bytes32 baseTokenAssetId = bridgehub.baseTokenAssetId(migratingChainId); + IChainTypeManager ctm = IChainTypeManager(bridgehub.chainTypeManager(migratingChainId)); + + bytes memory chainData = abi.encode(IAdmin(address(migratingChain)).prepareChainCommitment()); + bytes memory ctmData = abi.encode( + baseTokenAssetId, + msg.sender, + ctm.protocolVersion(), + config.gatewayDiamondCutData + ); + BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + chainId: migratingChainId, + baseTokenAssetId: baseTokenAssetId, + ctmData: ctmData, + chainData: chainData + }); + bytes memory bridgehubMintData = abi.encode(data); + + L1Nullifier l1Nullifier = L1Nullifier(config.l1NullifierProxy); + + l1Nullifier.finalizeDeposit( + FinalizeL1DepositParams({ + chainId: migratingChainId, + l2BatchNumber: 1, + l2MessageIndex: 1, + l2Sender: L2_ASSET_ROUTER_ADDR, + l2TxNumberInBatch: 1, + message: abi.encodePacked( + IAssetRouterBase.finalizeDeposit.selector, + gatewayChainId, + assetId, + bridgehubMintData + ), + merkleProof: new bytes32[](0) + }) + ); + } + /// @dev Calling this function requires private key to the admin of the chain function setDAValidatorPair( address chainAdmin, diff --git a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol index 5f85d7d66..499535b8d 100644 --- a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol @@ -93,7 +93,6 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, function test_bridgeHubSetter() public { uint256 chainId = zkChainIds[0]; - // IChainTypeManager chainTypeManager = IChainTypeManager(bridgeHub.chainTypeManager(chainId)); uint256 randomChainId = 123456; vm.mockCall( @@ -129,7 +128,7 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, ); address stmAddr = IZKChain(chain).getChainTypeManager(); - + vm.startBroadcast(owner); bridgeHub.addChainTypeManager(stmAddr); bridgeHub.addTokenAssetId(baseTokenAssetId); diff --git a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol b/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol index 271dfdf0c..edd4dc781 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol +++ b/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol @@ -22,12 +22,12 @@ contract GatewayPreparationForTests is GatewayPreparation { ); config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); - path = string.concat(root, vm.envString("GATEWAY_CONFIG")); + path = string.concat(root, vm.envString("GATEWAY_AS_CHAIN_CONFIG")); toml = vm.readFile(path); config.gatewayChainId = toml.readUint("$.chain.chain_chain_id"); - path = string.concat(root, vm.envString("GATEWAY_OUTPUT")); + path = string.concat(root, vm.envString("GATEWAY_AS_CHAIN_OUTPUT")); toml = vm.readFile(path); config.gatewayChainAdmin = toml.readAddress("$.chain_admin_addr"); diff --git a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index 443d431bb..f035d22f9 100644 --- a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -180,7 +180,12 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T { IZKChain chain = IZKChain(bridgehub.getZKChain(migratingChainId)); bytes memory chainData = abi.encode(chain.getProtocolVersion()); - bytes memory ctmData = abi.encode(address(1), msg.sender, chainTypeManager.protocolVersion(), ecosystemConfig.contracts.diamondCutData); + bytes memory ctmData = abi.encode( + address(1), + msg.sender, + chainTypeManager.protocolVersion(), + ecosystemConfig.contracts.diamondCutData + ); BridgehubBurnCTMAssetData memory data = BridgehubBurnCTMAssetData({ chainId: migratingChainId, ctmData: ctmData, @@ -254,7 +259,7 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T bytes32 baseTokenAssetId = keccak256("baseTokenAssetId"); uint256 currentChainId = block.chainid; - // we are already on L1, so we have to set another chain id, it cannot be GW or mintChainId. + // we are already on L1, so we have to set another chain id, it cannot be GW or mintChainId. vm.chainId(migratingChainId); vm.mockCall( address(bridgeHub), @@ -274,11 +279,7 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T // bytes memory initialDiamondCut = getInitialDiamondCutData(); // gatewayScript. - gatewayScript.finishMigrateChainFromGateway( - migratingChain, - migratingChainId, - gatewayChainId - ); + gatewayScript.finishMigrateChainFromGateway(migratingChain, migratingChainId, gatewayChainId); vm.chainId(currentChainId); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol index 66e0aa628..864035e40 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol @@ -17,11 +17,11 @@ contract GatewayDeployer is L1ContractDeployer { "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-10.toml" ); vm.setEnv( - "GATEWAY_CONFIG", + "GATEWAY_AS_CHAIN_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-11.toml" ); vm.setEnv( - "GATEWAY_OUTPUT", + "GATEWAY_AS_CHAIN_OUTPUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-11.toml" ); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index 5b98d4089..2cfd6e5b6 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -14,7 +14,6 @@ import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {DeployedAddresses, Config} from "deploy-scripts/DeployL1.s.sol"; - contract L1ContractDeployer is Test { using stdStorage for StdStorage; @@ -45,7 +44,10 @@ contract L1ContractDeployer is Test { "ZK_CHAIN_OUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-era.toml" ); - vm.setEnv("GATEWAY_PREPARATION_L1_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml"); + vm.setEnv( + "GATEWAY_PREPARATION_L1_CONFIG", + "/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml" + ); l1Script = new DeployL1Script(); l1Script.runForTest(); diff --git a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml new file mode 100644 index 000000000..666f86482 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml @@ -0,0 +1,11 @@ +bridgehub_proxy_addr = "0xe7ed3a4b2f210cb71033cfaeb3d15aa1bd1f7719" +ctm_deployment_tracker_proxy_addr = "0x69aaa8f53f6bcc1c9574b25e44712e85b63b01f7" +chain_type_manager_proxy_addr = "0x3301550e72d3042e9a1ca3350a66bdfec5b16ef8" +shared_bridge_proxy_addr = "0x04f59969801829d13f574129e2e1323c36d3239c" +governance = "0xc8c1e39e9173f80ba4d70e1910aeead49e75c0c6" +chain_chain_id = 506 +gateway_diamond_cut_data = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000e024f9e4e8fa2f08f768c1cb56bc4a6e3cbd88340000000000000000000000000000000000000000000000000000000000000d8000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000009e00000000000000000000000000000000000000000000000000000000000000be00000000000000000000000005caf5f2b06ca757c7cebacdcd6f163af45a6bb8300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000130e18b681000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000fc57565f000000000000000000000000000000000000000000000000000000001cc5d1030000000000000000000000000000000000000000000000000000000021f603d700000000000000000000000000000000000000000000000000000000235d9eb50000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000002878fe74000000000000000000000000000000000000000000000000000000003f42d5dd0000000000000000000000000000000000000000000000000000000041cf49bb000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000004dd18bf5000000000000000000000000000000000000000000000000000000006223258e0000000000000000000000000000000000000000000000000000000064b554ad0000000000000000000000000000000000000000000000000000000064bf8d6600000000000000000000000000000000000000000000000000000000a9f6d94100000000000000000000000000000000000000000000000000000000b784610700000000000000000000000000000000000000000000000000000000be6f11cf00000000000000000000000000000000000000000000000000000000e76db86500000000000000000000000000000000000000000000000000000000000000000000000000000000a0dcb2c61862bd44785423d454e5174a04f7d56e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002e06d49e5b00000000000000000000000000000000000000000000000000000000086a56f8000000000000000000000000000000000000000000000000000000000ec6b0b700000000000000000000000000000000000000000000000000000000fe26699e0000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000001de72e340000000000000000000000000000000000000000000000000000000022c5cf230000000000000000000000000000000000000000000000000000000029b98c670000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000003408e470000000000000000000000000000000000000000000000000000000003591c1a000000000000000000000000000000000000000000000000000000000396073820000000000000000000000000000000000000000000000000000000039d7d4aa0000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000052ef6b2c000000000000000000000000000000000000000000000000000000005a59033500000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000006a27e8b5000000000000000000000000000000000000000000000000000000006e9960c30000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000007a0ed627000000000000000000000000000000000000000000000000000000007b30c8da00000000000000000000000000000000000000000000000000000000946ebad100000000000000000000000000000000000000000000000000000000960dcf240000000000000000000000000000000000000000000000000000000098acd7a6000000000000000000000000000000000000000000000000000000009cd939e4000000000000000000000000000000000000000000000000000000009d1b5a8100000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000adfca15e00000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000b22dd78e00000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000cdffacc600000000000000000000000000000000000000000000000000000000d046815600000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000e5355c7500000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000ea6c029c00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000f5c1182c00000000000000000000000000000000000000000000000000000000facd743b00000000000000000000000000000000000000000000000000000000fd791f3c000000000000000000000000000000000000000000000000000000000000000000000000000000003275e4b7e3d062c0e5ff5c7c5ab873f4562627f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b042901c70000000000000000000000000000000000000000000000000000000012f43dab00000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000263b7f8e000000000000000000000000000000000000000000000000000000006c0960f9000000000000000000000000000000000000000000000000000000007efda2ae00000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000d077255100000000000000000000000000000000000000000000000000000000ddcc9eec00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000e717bab7000000000000000000000000000000000000000000000000000000000000000000000000000000007ed427e1c7de31d21a520eeceb835686927d199800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040f23da4300000000000000000000000000000000000000000000000000000000e12a61370000000000000000000000000000000000000000000000000000000098f8196200000000000000000000000000000000000000000000000000000000cf02827d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000868a8819e738818dabe8bfb671ae8e027372dd7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010008c753336bc8d1ddca235602b9f31d346412b2d463cd342899f7bfb73baf0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e00000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000001d4c00000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000182b8000000000000000000000000000000000000000000000000000000000ee6b2800000000000000000000000000000000000000000000000000000000000000001" +chain_proxy_admin = "0xfd285e11becbd6d81d32944947be1f97e321784f" +chain_admin = "0xa6fba37e028416e514b769ad767d3264775e78a1" +access_control_restriction = "0x9be3e70228e06b02306a934cf17a8eb7c09b048b" +l1_nullifier_proxy = "0xcaf6EA7c7c4912616e8ba88FcfD3D6401867BB1D" diff --git a/l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol b/l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol new file mode 100644 index 000000000..cdc96762e --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {L2Utils} from "../unit/utils/L2Utils.sol"; +import {SystemContractsArgs} from "../unit/utils/L2Utils.sol"; + +import {L1ContractDeployer} from "../../l1/integration/_SharedL1ContractDeployer.t.sol"; +import {ZKChainDeployer} from "../../l1/integration/_SharedZKChainDeployer.t.sol"; +import {TokenDeployer} from "../../l1/integration/_SharedTokenDeployer.t.sol"; + +contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer { + // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. + // It is a bit easier to use EOA and it is sufficient for the tests. + address internal l1BridgeWallet = address(1); + address internal aliasedL1BridgeWallet; + + // The owner of the beacon and the native token vault + address internal ownerWallet = address(2); + + BridgedStandardERC20 internal standardErc20Impl; + + UpgradeableBeacon internal beacon; + BeaconProxy internal proxy; + + IL2AssetRouter l2AssetRouter = IL2AssetRouter(L2_ASSET_ROUTER_ADDR); + IBridgehub l2Bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); + + uint256 internal constant L1_CHAIN_ID = 9; + uint256 internal ERA_CHAIN_ID = 270; + address internal l1AssetRouter; + + address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); + address internal l1CTM = makeAddr("l1CTM"); + bytes32 internal ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, l1CTMDeployer, abi.encode(l1CTM))); + + bytes32 internal baseTokenAssetId = + keccak256(abi.encode(L1_CHAIN_ID, L2_NATIVE_TOKEN_VAULT_ADDR, abi.encode(ETH_TOKEN_ADDRESS))); + + bytes internal exampleChainCommitment; + + function setUp() public { + aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); + + standardErc20Impl = new BridgedStandardERC20(); + beacon = new UpgradeableBeacon(address(standardErc20Impl)); + beacon.transferOwnership(ownerWallet); + + // One of the purposes of deploying it here is to publish its bytecode + BeaconProxy beaconProxy = new BeaconProxy(address(beacon), new bytes(0)); + proxy = beaconProxy; + bytes32 beaconProxyBytecodeHash; + assembly { + beaconProxyBytecodeHash := extcodehash(beaconProxy) + } + + address l2SharedBridge = makeAddr("l2SharedBridge"); + + L2Utils.initSystemContracts( + SystemContractsArgs({ + l1ChainId: L1_CHAIN_ID, + eraChainId: ERA_CHAIN_ID, + l1AssetRouter: l1AssetRouter, + legacySharedBridge: l2SharedBridge, + l2TokenBeacon: address(beacon), + l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, + aliasedOwner: ownerWallet, + contractsDeployedAlready: true + }) + ); + + _deployL2Contracts(); + + vm.prank(l1AssetRouter); + l2AssetRouter.setAssetHandlerAddress(L1_CHAIN_ID, ctmAssetId, L2_BRIDGEHUB_ADDR); + vm.prank(l1CTMDeployer); + l2Bridgehub.setAssetHandlerAddress(bytes32(uint256(uint160(l1CTM))), address(chainTypeManager)); + } + + function test_gatewayShouldFinalizeDeposit() public { + finalizeDeposit(); + } + + function test_forwardToL3OnGateway() public { + finalizeDeposit(); + + IBridgehub bridgehub = IBridgehub(bridgeHub); + // vm.chainId(12345); + vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); + bridgehub.forwardTransactionOnGateway(ERA_CHAIN_ID, bytes32(0), 0); + vm.stopBroadcast(); + } + + function finalizeDeposit() public { + bytes memory chainData = abi.encode(exampleChainCommitment); + bytes memory ctmData = abi.encode( + baseTokenAssetId, + msg.sender, + chainTypeManager.protocolVersion(), + ecosystemConfig.contracts.diamondCutData + ); + + BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + chainId: ERA_CHAIN_ID, + baseTokenAssetId: baseTokenAssetId, + ctmData: ctmData, + chainData: chainData + }); + vm.prank(l1AssetRouter); + l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + } +} diff --git a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol b/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol index 62c74ad36..c9902366f 100644 --- a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol @@ -66,17 +66,18 @@ contract L2Erc20BridgeTest is Test { l1BridgeWallet, beaconProxyBytecodeHash ); - L2Utils.initSystemContracts(SystemContractsArgs({ - l1ChainId: L1_CHAIN_ID, - eraChainId: ERA_CHAIN_ID, - l1AssetRouter: L2_ASSET_ROUTER_ADDR, - legacySharedBridge: l2SharedBridge, - l2TokenBeacon: address(beacon), - l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, - aliasedOwner: ownerWallet, - contractsDeployedAlready: true - })); - + L2Utils.initSystemContracts( + SystemContractsArgs({ + l1ChainId: L1_CHAIN_ID, + eraChainId: ERA_CHAIN_ID, + l1AssetRouter: L2_ASSET_ROUTER_ADDR, + legacySharedBridge: l2SharedBridge, + l2TokenBeacon: address(beacon), + l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, + aliasedOwner: ownerWallet, + contractsDeployedAlready: true + }) + ); } function performDeposit(address depositor, address receiver, uint256 amount) internal { diff --git a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol index 3d41ddeaa..5b678fce6 100644 --- a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol @@ -77,12 +77,29 @@ library L2Utils { forceDeploySystemContracts(_args); } - function forceDeploySystemContracts( - SystemContractsArgs memory _args - ) internal { - forceDeployBridgehub(_args.l1ChainId, _args.eraChainId, _args.aliasedOwner, _args.l1AssetRouter, _args.legacySharedBridge); - forceDeployAssetRouter(_args.l1ChainId, _args.eraChainId, _args.aliasedOwner,_args.l1AssetRouter, _args.legacySharedBridge); - forceDeployNativeTokenVault(_args.l1ChainId, _args.aliasedOwner, _args.l2TokenProxyBytecodeHash, _args.legacySharedBridge, _args.l2TokenBeacon, _args.contractsDeployedAlready); + function forceDeploySystemContracts(SystemContractsArgs memory _args) internal { + forceDeployBridgehub( + _args.l1ChainId, + _args.eraChainId, + _args.aliasedOwner, + _args.l1AssetRouter, + _args.legacySharedBridge + ); + forceDeployAssetRouter( + _args.l1ChainId, + _args.eraChainId, + _args.aliasedOwner, + _args.l1AssetRouter, + _args.legacySharedBridge + ); + forceDeployNativeTokenVault( + _args.l1ChainId, + _args.aliasedOwner, + _args.l2TokenProxyBytecodeHash, + _args.legacySharedBridge, + _args.l2TokenBeacon, + _args.contractsDeployedAlready + ); } function forceDeployBridgehub( @@ -97,7 +114,11 @@ library L2Utils { Bridgehub bridgehub = Bridgehub(L2_BRIDGEHUB_ADDR); address l1CTMDeployer = address(0x1); vm.prank(_aliasedOwner); - bridgehub.setAddresses(L2_ASSET_ROUTER_ADDR, ICTMDeploymentTracker(l1CTMDeployer), IMessageRoot(L2_MESSAGE_ROOT_ADDR)); + bridgehub.setAddresses( + L2_ASSET_ROUTER_ADDR, + ICTMDeploymentTracker(l1CTMDeployer), + IMessageRoot(L2_MESSAGE_ROOT_ADDR) + ); } /// @notice Deploys the L2AssetRouter contract. @@ -117,8 +138,11 @@ library L2Utils { { new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner); } - forceDeployWithConstructor("L2AssetRouter", L2_ASSET_ROUTER_ADDR, abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner)); - + forceDeployWithConstructor( + "L2AssetRouter", + L2_ASSET_ROUTER_ADDR, + abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner) + ); } /// @notice Deploys the L2NativeTokenVault contract. @@ -150,7 +174,20 @@ library L2Utils { _baseTokenAssetId: ethAssetId }); } - forceDeployWithConstructor("L2NativeTokenVault", L2_NATIVE_TOKEN_VAULT_ADDR, abi.encode(_l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, _legacySharedBridge, _l2TokenBeacon, _contractsDeployedAlready, address(0), ethAssetId)); + forceDeployWithConstructor( + "L2NativeTokenVault", + L2_NATIVE_TOKEN_VAULT_ADDR, + abi.encode( + _l1ChainId, + _aliasedOwner, + _l2TokenProxyBytecodeHash, + _legacySharedBridge, + _l2TokenBeacon, + _contractsDeployedAlready, + address(0), + ethAssetId + ) + ); } function forceDeployWithConstructor( From c2c2dbaa075e9579f584cd7c235c9d6038b746b2 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Fri, 27 Sep 2024 16:33:43 +0400 Subject: [PATCH 07/42] (feat): add legacy read for withdrawal finalization, update l2-l1 encoding / decoding --- l1-contracts/contracts/bridge/L1Nullifier.sol | 2 +- .../contracts/bridge/asset-router/IL1AssetRouter.sol | 6 ++++++ .../contracts/bridge/asset-router/L1AssetRouter.sol | 9 +++++++++ .../contracts/bridge/asset-router/L2AssetRouter.sol | 8 ++++---- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 100e0b04e..3c3d3d539 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -610,7 +610,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, // The data is expected to be at least 36 bytes long to contain assetId. require(_l2ToL1message.length >= 36, "L1N: wrong msg len"); // wrong message length // slither-disable-next-line unused-return - (, offset) = UnsafeBytes.readBytes32(_l2ToL1message, offset); // originChainId, not used for L2->L1 txs + (, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); // originChainId, not used for L2->L1 txs (assetId, offset) = UnsafeBytes.readBytes32(_l2ToL1message, offset); transferData = UnsafeBytes.readRemainingBytes(_l2ToL1message, offset); } else { diff --git a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol index 55b5b9560..5e04fd2ea 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol @@ -168,4 +168,10 @@ interface IL1AssetRouter is IAssetRouterBase { /// @param _txDataHash The keccak256 hash of 0x01 || abi.encode(bytes32, bytes) to identify deposits. /// @param _txHash The hash of the L1->L2 transaction to confirm the deposit. function bridgehubConfirmL2Transaction(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external; + + function isWithdrawalFinalized( + uint256 _chainId, + uint256 _l2BatchNumber, + uint256 _l2MessageIndex + ) external view returns (bool); } diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 20e423f47..458bc20b8 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -585,4 +585,13 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { _merkleProof: _merkleProof }); } + + /// @notice Legacy read method, which forwards the call to L1Nullifier to check if withdrawal was finalized + function isWithdrawalFinalized( + uint256 _chainId, + uint256 _l2BatchNumber, + uint256 _l2MessageIndex + ) external view returns (bool) { + return L1_NULLIFIER.isWithdrawalFinalized(_chainId, _l2BatchNumber, _l2MessageIndex); + } } diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 36a9ec5a1..0e19b79b7 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -131,7 +131,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { //////////////////////////////////////////////////////////////*/ // kl todo add handle Legaacy data here, which calls esureTokenRegisteredWithNTV - // have handleLegacyData called from somewhere. + // have handleLegacyData called from somewhere. /// @inheritdoc AssetRouterBase function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { @@ -155,7 +155,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { _withdrawSender(_assetId, _assetData, msg.sender, true); } - function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { + function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); _withdrawSender(assetId, _assetData, msg.sender, true); } @@ -207,9 +207,9 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { function _getAssetRouterWithdrawMessage( bytes32 _assetId, bytes memory _l1bridgeMintData - ) internal pure returns (bytes memory) { + ) internal view returns (bytes memory) { // solhint-disable-next-line func-named-parameters - return abi.encodePacked(IAssetRouterBase.finalizeDeposit.selector, _assetId, _l1bridgeMintData); + return abi.encodePacked(IAssetRouterBase.finalizeDeposit.selector, block.chainid, _assetId, _l1bridgeMintData); } /// @notice Encodes the message for l2ToL1log sent during withdraw initialization. From 0c7cc45afa1d0f61c8ed215cc85379aa7b82494e Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 27 Sep 2024 19:57:10 +0100 Subject: [PATCH 08/42] fixed test --- .../deploy-scripts/GatewayPreparation.s.sol | 32 +++++------ .../deploy-scripts/RegisterZKChain.s.sol | 56 ++++++++++--------- .../l1/integration/DeploymentTest.t.sol | 2 +- .../l1/integration/L1GatewayTests.t.sol | 18 +++--- ...ts.sol => _GatewayPreparationForTests.sol} | 1 + .../integration/_SharedGatewayDeployer.t.sol | 2 +- .../integration/_SharedZKChainDeployer.t.sol | 5 ++ .../script-config/config-deploy-l1.toml | 2 +- .../script-config/gateway-preparation-l1.toml | 11 ---- 9 files changed, 65 insertions(+), 64 deletions(-) rename l1-contracts/test/foundry/l1/integration/{GatewayPreparationForTests.sol => _GatewayPreparationForTests.sol} (94%) delete mode 100644 l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 194f40ce9..a3568f10c 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -32,6 +32,21 @@ import {FinalizeL1DepositParams} from "contracts/bridge/interfaces/IL1Nullifier. import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +// solhint-disable-next-line gas-struct-packing +struct Config { + address bridgehub; + address ctmDeploymentTracker; + address chainTypeManagerProxy; + address sharedBridgeProxy; + address governance; + uint256 gatewayChainId; + address gatewayChainAdmin; + address gatewayAccessControlRestriction; + address gatewayChainProxyAdmin; + address l1NullifierProxy; + bytes gatewayDiamondCutData; +} + /// @notice Scripts that is responsible for preparing the chain to become a gateway contract GatewayPreparation is Script { using stdToml for string; @@ -42,21 +57,6 @@ contract GatewayPreparation is Script { address deployerAddress; uint256 l1ChainId; - // solhint-disable-next-line gas-struct-packing - struct Config { - address bridgehub; - address ctmDeploymentTracker; - address chainTypeManagerProxy; - address sharedBridgeProxy; - address governance; - uint256 gatewayChainId; - address gatewayChainAdmin; - address gatewayAccessControlRestriction; - address gatewayChainProxyAdmin; - address l1NullifierProxy; - bytes gatewayDiamondCutData; - } - struct Output { bytes32 governanceL2TxHash; address gatewayTransactionFiltererImplementation; @@ -335,7 +335,7 @@ contract GatewayPreparation is Script { IBridgehub bridgehub = IBridgehub(config.bridgehub); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); bytes32 baseTokenAssetId = bridgehub.baseTokenAssetId(migratingChainId); - IChainTypeManager ctm = IChainTypeManager(bridgehub.chainTypeManager(migratingChainId)); + IChainTypeManager ctm = IChainTypeManager(config.chainTypeManagerProxy); bytes memory chainData = abi.encode(IAdmin(address(migratingChain)).prepareChainCommitment()); bytes memory ctmData = abi.encode( diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index 3a374e674..b1c8ae292 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -28,38 +28,38 @@ import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; +// solhint-disable-next-line gas-struct-packing +struct Config { + address deployerAddress; + address ownerAddress; + uint256 chainChainId; + bool validiumMode; + uint256 bridgehubCreateNewChainSalt; + address validatorSenderOperatorCommitEth; + address validatorSenderOperatorBlobsEth; + address baseToken; + bytes32 baseTokenAssetId; + uint128 baseTokenGasPriceMultiplierNominator; + uint128 baseTokenGasPriceMultiplierDenominator; + address bridgehub; + // TODO(EVM-744): maybe rename to asset router + address sharedBridgeProxy; + address nativeTokenVault; + address chainTypeManagerProxy; + address validatorTimelock; + bytes diamondCutData; + bytes forceDeployments; + address governanceSecurityCouncilAddress; + uint256 governanceMinDelay; + address l1Nullifier; +} + contract RegisterZKChainScript is Script { using stdToml for string; address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; bytes32 internal constant STATE_TRANSITION_NEW_CHAIN_HASH = keccak256("NewZKChain(uint256,address)"); - // solhint-disable-next-line gas-struct-packing - struct Config { - address deployerAddress; - address ownerAddress; - uint256 chainChainId; - bool validiumMode; - uint256 bridgehubCreateNewChainSalt; - address validatorSenderOperatorCommitEth; - address validatorSenderOperatorBlobsEth; - address baseToken; - bytes32 baseTokenAssetId; - uint128 baseTokenGasPriceMultiplierNominator; - uint128 baseTokenGasPriceMultiplierDenominator; - address bridgehub; - // TODO(EVM-744): maybe rename to asset router - address sharedBridgeProxy; - address nativeTokenVault; - address chainTypeManagerProxy; - address validatorTimelock; - bytes diamondCutData; - bytes forceDeployments; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - address l1Nullifier; - } - struct Output { address governance; address diamondProxy; @@ -167,6 +167,10 @@ contract RegisterZKChainScript is Script { config.validatorSenderOperatorBlobsEth = toml.readAddress("$.chain.validator_sender_operator_blobs_eth"); } + function getConfig() public view returns (Config memory) { + return config; + } + function initializeConfigTest() internal { // Grab config from output of l1 deployment string memory root = vm.projectRoot(); diff --git a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol index 499535b8d..d9abe13da 100644 --- a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol @@ -88,7 +88,7 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, assertEq(chainIds[0], chainId); uint256 protocolVersion = chainTypeManager.getProtocolVersion(chainId); - assertEq(protocolVersion, 0); + assertEq(protocolVersion, 25); } function test_bridgeHubSetter() public { diff --git a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index f035d22f9..271dcc73e 100644 --- a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -39,8 +39,6 @@ import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; -import {DeployedAddresses} from "deploy-scripts/DeployL1.s.sol"; - contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { uint256 constant TEST_USERS_COUNT = 10; address[] public users; @@ -244,6 +242,12 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T } function test_migrateBackChain() public { + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); migrateBackChain(); } @@ -256,7 +260,7 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T bridgehub.registerSettlementLayer(gatewayChainId, true); vm.stopBroadcast(); - bytes32 baseTokenAssetId = keccak256("baseTokenAssetId"); + bytes32 baseTokenAssetId = eraConfig.baseTokenAssetId; uint256 currentChainId = block.chainid; // we are already on L1, so we have to set another chain id, it cannot be GW or mintChainId. @@ -277,15 +281,13 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T abi.encode(chainTypeManager.protocolVersion()) ); - // bytes memory initialDiamondCut = getInitialDiamondCutData(); - // gatewayScript. gatewayScript.finishMigrateChainFromGateway(migratingChain, migratingChainId, gatewayChainId); vm.chainId(currentChainId); - assertEq(bridgehub.baseTokenAssetId(mintChainId), baseTokenAssetId); - IZKChain mintedZKChain = IZKChain(bridgehub.getZKChain(mintChainId)); - assertEq(mintedZKChain.getBaseTokenAssetId(), baseTokenAssetId); + assertEq(bridgehub.baseTokenAssetId(migratingChainId), baseTokenAssetId); + IZKChain migratingChainContract = IZKChain(bridgehub.getZKChain(migratingChainId)); + assertEq(migratingChainContract.getBaseTokenAssetId(), baseTokenAssetId); } // add this to be excluded from coverage report diff --git a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol b/l1-contracts/test/foundry/l1/integration/_GatewayPreparationForTests.sol similarity index 94% rename from l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol rename to l1-contracts/test/foundry/l1/integration/_GatewayPreparationForTests.sol index edd4dc781..26e754d9f 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol +++ b/l1-contracts/test/foundry/l1/integration/_GatewayPreparationForTests.sol @@ -35,6 +35,7 @@ contract GatewayPreparationForTests is GatewayPreparation { config.gatewayAccessControlRestriction = toml.readAddress( "$.deployed_addresses.access_control_restriction_addr" ); + config.l1NullifierProxy = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); console.log("chain chain id = ", config.gatewayChainId); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol index 864035e40..bd72835af 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; import {L1ContractDeployer} from "./_SharedL1ContractDeployer.t.sol"; -import {GatewayPreparationForTests} from "./GatewayPreparationForTests.sol"; +import {GatewayPreparationForTests} from "./_GatewayPreparationForTests.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import "@openzeppelin/contracts-v4/utils/Strings.sol"; diff --git a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol index 108f9ce4e..e5af52539 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol @@ -13,6 +13,8 @@ import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; import {IDiamondInit} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; +import {Config as ChainConfig} from "deploy-scripts/RegisterZKChain.s.sol"; + contract ZKChainDeployer is L1ContractDeployer { using stdStorage for StdStorage; @@ -29,6 +31,8 @@ contract ZKChainDeployer is L1ContractDeployer { uint128 baseTokenGasPriceMultiplierDenominator; } + ChainConfig internal eraConfig; + uint256 currentZKChainId = 10; uint256 eraZKChainId = 9; uint256[] public zkChainIds; @@ -47,6 +51,7 @@ contract ZKChainDeployer is L1ContractDeployer { vm.warp(100); deployScript.runForTest(); zkChainIds.push(eraZKChainId); + eraConfig = deployScript.getConfig(); } function _deployZKChain(address _baseToken) internal { diff --git a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml index 5683d081a..d52a6b704 100644 --- a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml +++ b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml @@ -12,7 +12,7 @@ validator_timelock_execution_delay = 0 genesis_root = "0x1000000000000000000000000000000000000000000000000000000000000000" genesis_rollup_leaf_index = 1 genesis_batch_commitment = "0x1000000000000000000000000000000000000000000000000000000000000000" -latest_protocol_version = 0 +latest_protocol_version = 25 recursion_node_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" recursion_leaf_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" recursion_circuits_set_vks_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml deleted file mode 100644 index 666f86482..000000000 --- a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml +++ /dev/null @@ -1,11 +0,0 @@ -bridgehub_proxy_addr = "0xe7ed3a4b2f210cb71033cfaeb3d15aa1bd1f7719" -ctm_deployment_tracker_proxy_addr = "0x69aaa8f53f6bcc1c9574b25e44712e85b63b01f7" -chain_type_manager_proxy_addr = "0x3301550e72d3042e9a1ca3350a66bdfec5b16ef8" -shared_bridge_proxy_addr = "0x04f59969801829d13f574129e2e1323c36d3239c" -governance = "0xc8c1e39e9173f80ba4d70e1910aeead49e75c0c6" -chain_chain_id = 506 -gateway_diamond_cut_data = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000e024f9e4e8fa2f08f768c1cb56bc4a6e3cbd88340000000000000000000000000000000000000000000000000000000000000d8000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000009e00000000000000000000000000000000000000000000000000000000000000be00000000000000000000000005caf5f2b06ca757c7cebacdcd6f163af45a6bb8300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000130e18b681000000000000000000000000000000000000000000000000000000001733894500000000000000000000000000000000000000000000000000000000fc57565f000000000000000000000000000000000000000000000000000000001cc5d1030000000000000000000000000000000000000000000000000000000021f603d700000000000000000000000000000000000000000000000000000000235d9eb50000000000000000000000000000000000000000000000000000000027ae4c16000000000000000000000000000000000000000000000000000000002878fe74000000000000000000000000000000000000000000000000000000003f42d5dd0000000000000000000000000000000000000000000000000000000041cf49bb000000000000000000000000000000000000000000000000000000004623c91d000000000000000000000000000000000000000000000000000000004dd18bf5000000000000000000000000000000000000000000000000000000006223258e0000000000000000000000000000000000000000000000000000000064b554ad0000000000000000000000000000000000000000000000000000000064bf8d6600000000000000000000000000000000000000000000000000000000a9f6d94100000000000000000000000000000000000000000000000000000000b784610700000000000000000000000000000000000000000000000000000000be6f11cf00000000000000000000000000000000000000000000000000000000e76db86500000000000000000000000000000000000000000000000000000000000000000000000000000000a0dcb2c61862bd44785423d454e5174a04f7d56e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000002e06d49e5b00000000000000000000000000000000000000000000000000000000086a56f8000000000000000000000000000000000000000000000000000000000ec6b0b700000000000000000000000000000000000000000000000000000000fe26699e0000000000000000000000000000000000000000000000000000000018e3a941000000000000000000000000000000000000000000000000000000001de72e340000000000000000000000000000000000000000000000000000000022c5cf230000000000000000000000000000000000000000000000000000000029b98c670000000000000000000000000000000000000000000000000000000033ce93fe000000000000000000000000000000000000000000000000000000003408e470000000000000000000000000000000000000000000000000000000003591c1a000000000000000000000000000000000000000000000000000000000396073820000000000000000000000000000000000000000000000000000000039d7d4aa0000000000000000000000000000000000000000000000000000000046657fe90000000000000000000000000000000000000000000000000000000052ef6b2c000000000000000000000000000000000000000000000000000000005a59033500000000000000000000000000000000000000000000000000000000631f4bac000000000000000000000000000000000000000000000000000000006a27e8b5000000000000000000000000000000000000000000000000000000006e9960c30000000000000000000000000000000000000000000000000000000074f4d30d0000000000000000000000000000000000000000000000000000000079823c9a000000000000000000000000000000000000000000000000000000007a0ed627000000000000000000000000000000000000000000000000000000007b30c8da00000000000000000000000000000000000000000000000000000000946ebad100000000000000000000000000000000000000000000000000000000960dcf240000000000000000000000000000000000000000000000000000000098acd7a6000000000000000000000000000000000000000000000000000000009cd939e4000000000000000000000000000000000000000000000000000000009d1b5a8100000000000000000000000000000000000000000000000000000000a1954fc500000000000000000000000000000000000000000000000000000000adfca15e00000000000000000000000000000000000000000000000000000000af6a2dcd00000000000000000000000000000000000000000000000000000000b22dd78e00000000000000000000000000000000000000000000000000000000b8c2f66f00000000000000000000000000000000000000000000000000000000bd7c541200000000000000000000000000000000000000000000000000000000c3bbd2d700000000000000000000000000000000000000000000000000000000cdffacc600000000000000000000000000000000000000000000000000000000d046815600000000000000000000000000000000000000000000000000000000d86970d800000000000000000000000000000000000000000000000000000000db1f0bf900000000000000000000000000000000000000000000000000000000e5355c7500000000000000000000000000000000000000000000000000000000e81e0ba100000000000000000000000000000000000000000000000000000000ea6c029c00000000000000000000000000000000000000000000000000000000ef3f0bae00000000000000000000000000000000000000000000000000000000f5c1182c00000000000000000000000000000000000000000000000000000000facd743b00000000000000000000000000000000000000000000000000000000fd791f3c000000000000000000000000000000000000000000000000000000000000000000000000000000003275e4b7e3d062c0e5ff5c7c5ab873f4562627f7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000b042901c70000000000000000000000000000000000000000000000000000000012f43dab00000000000000000000000000000000000000000000000000000000eb67241900000000000000000000000000000000000000000000000000000000263b7f8e000000000000000000000000000000000000000000000000000000006c0960f9000000000000000000000000000000000000000000000000000000007efda2ae00000000000000000000000000000000000000000000000000000000b473318e00000000000000000000000000000000000000000000000000000000d077255100000000000000000000000000000000000000000000000000000000ddcc9eec00000000000000000000000000000000000000000000000000000000e4948f4300000000000000000000000000000000000000000000000000000000e717bab7000000000000000000000000000000000000000000000000000000000000000000000000000000007ed427e1c7de31d21a520eeceb835686927d199800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040f23da4300000000000000000000000000000000000000000000000000000000e12a61370000000000000000000000000000000000000000000000000000000098f8196200000000000000000000000000000000000000000000000000000000cf02827d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000868a8819e738818dabe8bfb671ae8e027372dd7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010008c753336bc8d1ddca235602b9f31d346412b2d463cd342899f7bfb73baf0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e00000000000000000000000000000000000000000000000000000000044aa200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000001d4c00000000000000000000000000000000000000000000000000000000004c4b40000000000000000000000000000000000000000000000000000000000000182b8000000000000000000000000000000000000000000000000000000000ee6b2800000000000000000000000000000000000000000000000000000000000000001" -chain_proxy_admin = "0xfd285e11becbd6d81d32944947be1f97e321784f" -chain_admin = "0xa6fba37e028416e514b769ad767d3264775e78a1" -access_control_restriction = "0x9be3e70228e06b02306a934cf17a8eb7c09b048b" -l1_nullifier_proxy = "0xcaf6EA7c7c4912616e8ba88FcfD3D6401867BB1D" From 18dc862bc0e5cf7f0d3d294a5bffea0bd36d93b6 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sat, 28 Sep 2024 21:57:21 +0100 Subject: [PATCH 09/42] some fixes, enabling l2 integrationt tests --- l1-contracts/deploy-scripts/DeployL1.s.sol | 492 ++---------------- l1-contracts/deploy-scripts/DeployUtils.s.sol | 454 ++++++++++++++++ l1-contracts/package.json | 4 +- .../_SharedL1ContractDeployer.t.sol | 11 +- .../{L2Gateway.t.sol => L2GatewayTests.t.sol} | 72 +-- .../_SharedL2ContractDeployer.t.sol | 41 ++ .../l2/unit/erc20/L2Erc20BridgeTest.t.sol | 6 +- .../test/foundry/l2/unit/utils/L2Utils.sol | 40 +- 8 files changed, 625 insertions(+), 495 deletions(-) create mode 100644 l1-contracts/deploy-scripts/DeployUtils.s.sol rename l1-contracts/test/foundry/l2/integration/{L2Gateway.t.sol => L2GatewayTests.t.sol} (66%) create mode 100644 l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index b5a4a74a7..5380b56ca 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -56,126 +56,14 @@ import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.s import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; import {ValidiumL1DAValidator} from "contracts/state-transition/data-availability/ValidiumL1DAValidator.sol"; -struct FixedForceDeploymentsData { - uint256 l1ChainId; - uint256 eraChainId; - address l1AssetRouter; - bytes32 l2TokenProxyBytecodeHash; - address aliasedL1Governance; - uint256 maxNumberOfZKChains; - bytes32 bridgehubBytecodeHash; - bytes32 l2AssetRouterBytecodeHash; - bytes32 l2NtvBytecodeHash; - bytes32 messageRootBytecodeHash; -} - -// solhint-disable-next-line gas-struct-packing -struct DeployedAddresses { - BridgehubDeployedAddresses bridgehub; - StateTransitionDeployedAddresses stateTransition; - BridgesDeployedAddresses bridges; - L1NativeTokenVaultAddresses vaults; - DataAvailabilityDeployedAddresses daAddresses; - address transparentProxyAdmin; - address governance; - address chainAdmin; - address accessControlRestrictionAddress; - address blobVersionedHashRetriever; - address validatorTimelock; - address create2Factory; -} - -// solhint-disable-next-line gas-struct-packing -struct L1NativeTokenVaultAddresses { - address l1NativeTokenVaultImplementation; - address l1NativeTokenVaultProxy; -} - -struct DataAvailabilityDeployedAddresses { - address l1RollupDAValidator; - address l1ValidiumDAValidator; -} - -// solhint-disable-next-line gas-struct-packing -struct BridgehubDeployedAddresses { - address bridgehubImplementation; - address bridgehubProxy; - address ctmDeploymentTrackerImplementation; - address ctmDeploymentTrackerProxy; - address messageRootImplementation; - address messageRootProxy; -} +import {DeployUtils, GeneratedData, Config, DeployedAddresses, FixedForceDeploymentsData} from "./DeployUtils.s.sol"; -// solhint-disable-next-line gas-struct-packing -struct BridgesDeployedAddresses { - address erc20BridgeImplementation; - address erc20BridgeProxy; - address sharedBridgeImplementation; - address sharedBridgeProxy; - address l1NullifierImplementation; - address l1NullifierProxy; - address bridgedStandardERC20Implementation; - address bridgedTokenBeacon; -} - -// solhint-disable-next-line gas-struct-packing -struct Config { - uint256 l1ChainId; - address deployerAddress; - uint256 eraChainId; - address ownerAddress; - bool testnetVerifier; - ContractsConfig contracts; - TokensConfig tokens; -} - -// solhint-disable-next-line gas-struct-packing -struct ContractsConfig { - bytes32 create2FactorySalt; - address create2FactoryAddr; - address multicall3Addr; - uint256 validatorTimelockExecutionDelay; - bytes32 genesisRoot; - uint256 genesisRollupLeafIndex; - bytes32 genesisBatchCommitment; - uint256 latestProtocolVersion; - bytes32 recursionNodeLevelVkHash; - bytes32 recursionLeafLevelVkHash; - bytes32 recursionCircuitsSetVksHash; - uint256 priorityTxMaxGasLimit; - PubdataPricingMode diamondInitPubdataPricingMode; - uint256 diamondInitBatchOverheadL1Gas; - uint256 diamondInitMaxPubdataPerBatch; - uint256 diamondInitMaxL2GasPerBatch; - uint256 diamondInitPriorityTxMaxPubdata; - uint256 diamondInitMinimalL2GasPrice; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - uint256 maxNumberOfChains; - bytes diamondCutData; - bytes32 bootloaderHash; - bytes32 defaultAAHash; -} - -struct TokensConfig { - address tokenWethAddress; -} - -contract DeployL1Script is Script { +contract DeployL1Script is Script, DeployUtils { using stdToml for string; address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; - // solhint-disable-next-line gas-struct-packing - struct GeneratedData { - bytes forceDeploymentsData; - } - - Config public config; - GeneratedData internal generatedData; - DeployedAddresses internal addresses; - function run() public { console.log("Deploying L1 contracts"); @@ -186,10 +74,6 @@ contract DeployL1Script is Script { runInner(vm.envString("L1_CONFIG"), vm.envString("L1_OUTPUT")); } - function runForL2Test() public { - runL2Inner(vm.envString("L2_CONFIG"), vm.envString("L2_OUTPUT")); - } - function getAddresses() public view returns (DeployedAddresses memory) { return addresses; } @@ -237,6 +121,7 @@ contract DeployL1Script is Script { deployBlobVersionedHashRetriever(); deployChainTypeManagerContract(); + registerChainTypeManager(); setChainTypeManagerInValidatorTimelock(); updateOwners(); @@ -244,71 +129,6 @@ contract DeployL1Script is Script { saveOutput(outputPath); } - function runL2Inner(string memory inputPath, string memory outputPath) internal { - string memory root = vm.projectRoot(); - inputPath = string.concat(root, inputPath); - outputPath = string.concat(root, outputPath); - - initializeConfig(inputPath); - - // instantiateCreate2Factory(); - // deployIfNeededMulticall3(); - deployChainTypeManagerContract(); - } - - function initializeConfig(string memory configPath) internal { - string memory toml = vm.readFile(configPath); - - config.l1ChainId = block.chainid; - config.deployerAddress = msg.sender; - - // Config file must be parsed key by key, otherwise values returned - // are parsed alfabetically and not by key. - // https://book.getfoundry.sh/cheatcodes/parse-toml - config.eraChainId = toml.readUint("$.era_chain_id"); - config.ownerAddress = toml.readAddress("$.owner_address"); - config.testnetVerifier = toml.readBool("$.testnet_verifier"); - - config.contracts.governanceSecurityCouncilAddress = toml.readAddress( - "$.contracts.governance_security_council_address" - ); - config.contracts.governanceMinDelay = toml.readUint("$.contracts.governance_min_delay"); - config.contracts.maxNumberOfChains = toml.readUint("$.contracts.max_number_of_chains"); - config.contracts.create2FactorySalt = toml.readBytes32("$.contracts.create2_factory_salt"); - if (vm.keyExistsToml(toml, "$.contracts.create2_factory_addr")) { - config.contracts.create2FactoryAddr = toml.readAddress("$.contracts.create2_factory_addr"); - } - config.contracts.validatorTimelockExecutionDelay = toml.readUint( - "$.contracts.validator_timelock_execution_delay" - ); - config.contracts.genesisRoot = toml.readBytes32("$.contracts.genesis_root"); - config.contracts.genesisRollupLeafIndex = toml.readUint("$.contracts.genesis_rollup_leaf_index"); - config.contracts.genesisBatchCommitment = toml.readBytes32("$.contracts.genesis_batch_commitment"); - config.contracts.latestProtocolVersion = toml.readUint("$.contracts.latest_protocol_version"); - config.contracts.recursionNodeLevelVkHash = toml.readBytes32("$.contracts.recursion_node_level_vk_hash"); - config.contracts.recursionLeafLevelVkHash = toml.readBytes32("$.contracts.recursion_leaf_level_vk_hash"); - config.contracts.recursionCircuitsSetVksHash = toml.readBytes32("$.contracts.recursion_circuits_set_vks_hash"); - config.contracts.priorityTxMaxGasLimit = toml.readUint("$.contracts.priority_tx_max_gas_limit"); - config.contracts.diamondInitPubdataPricingMode = PubdataPricingMode( - toml.readUint("$.contracts.diamond_init_pubdata_pricing_mode") - ); - config.contracts.diamondInitBatchOverheadL1Gas = toml.readUint( - "$.contracts.diamond_init_batch_overhead_l1_gas" - ); - config.contracts.diamondInitMaxPubdataPerBatch = toml.readUint( - "$.contracts.diamond_init_max_pubdata_per_batch" - ); - config.contracts.diamondInitMaxL2GasPerBatch = toml.readUint("$.contracts.diamond_init_max_l2_gas_per_batch"); - config.contracts.diamondInitPriorityTxMaxPubdata = toml.readUint( - "$.contracts.diamond_init_priority_tx_max_pubdata" - ); - config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); - config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); - config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); - - config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); - } - function initializeGeneratedData() internal { generatedData.forceDeploymentsData = prepareForceDeploymentsData(); } @@ -339,111 +159,31 @@ contract DeployL1Script is Script { function deployIfNeededMulticall3() internal { // Multicall3 is already deployed on public networks if (MULTICALL3_ADDRESS.code.length == 0) { - address contractAddress = deployViaCreate2(type(Multicall3).creationCode); + address contractAddress = deployViaCreate2(type(Multicall3).creationCode, ""); console.log("Multicall3 deployed at:", contractAddress); config.contracts.multicall3Addr = contractAddress; } else { config.contracts.multicall3Addr = MULTICALL3_ADDRESS; } } - - function deployVerifier() internal { - bytes memory code; - if (config.testnetVerifier) { - code = type(TestnetVerifier).creationCode; - } else { - code = type(Verifier).creationCode; - } - address contractAddress = deployViaCreate2(code); - console.log("Verifier deployed at:", contractAddress); - addresses.stateTransition.verifier = contractAddress; - } - - function deployDefaultUpgrade() internal { - address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode); - console.log("DefaultUpgrade deployed at:", contractAddress); - addresses.stateTransition.defaultUpgrade = contractAddress; - } - - function deployGenesisUpgrade() internal { - bytes memory bytecode = abi.encodePacked(type(L1GenesisUpgrade).creationCode); - address contractAddress = deployViaCreate2(bytecode); - console.log("GenesisUpgrade deployed at:", contractAddress); - addresses.stateTransition.genesisUpgrade = contractAddress; - } - function deployDAValidators() internal { - address contractAddress = deployViaCreate2(Utils.readRollupDAValidatorBytecode()); + address contractAddress = deployViaCreate2(Utils.readRollupDAValidatorBytecode(), ""); console.log("L1RollupDAValidator deployed at:", contractAddress); addresses.daAddresses.l1RollupDAValidator = contractAddress; - contractAddress = deployViaCreate2(type(ValidiumL1DAValidator).creationCode); + contractAddress = deployViaCreate2(type(ValidiumL1DAValidator).creationCode, ""); console.log("L1ValidiumDAValidator deployed at:", contractAddress); addresses.daAddresses.l1ValidiumDAValidator = contractAddress; } - - function deployValidatorTimelock() internal { - uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); - bytes memory bytecode = abi.encodePacked( - type(ValidatorTimelock).creationCode, - abi.encode(config.deployerAddress, executionDelay, config.eraChainId) - ); - address contractAddress = deployViaCreate2(bytecode); - console.log("ValidatorTimelock deployed at:", contractAddress); - addresses.validatorTimelock = contractAddress; - } - - function deployGovernance() internal { - bytes memory bytecode = abi.encodePacked( - type(Governance).creationCode, - abi.encode( - config.ownerAddress, - config.contracts.governanceSecurityCouncilAddress, - config.contracts.governanceMinDelay - ) - ); - address contractAddress = deployViaCreate2(bytecode); - console.log("Governance deployed at:", contractAddress); - addresses.governance = contractAddress; - } - - function deployChainAdmin() internal { - bytes memory accessControlRestrictionBytecode = abi.encodePacked( - type(AccessControlRestriction).creationCode, - abi.encode(uint256(0), config.ownerAddress) - ); - - address accessControlRestriction = deployViaCreate2(accessControlRestrictionBytecode); - console.log("Access control restriction deployed at:", accessControlRestriction); - address[] memory restrictions = new address[](1); - restrictions[0] = accessControlRestriction; - addresses.accessControlRestrictionAddress = accessControlRestriction; - - bytes memory bytecode = abi.encodePacked(type(ChainAdmin).creationCode, abi.encode(restrictions)); - address contractAddress = deployViaCreate2(bytecode); - console.log("ChainAdmin deployed at:", contractAddress); - addresses.chainAdmin = contractAddress; - } - - function deployTransparentProxyAdmin() internal { - vm.startBroadcast(); - ProxyAdmin proxyAdmin = new ProxyAdmin(); - proxyAdmin.transferOwnership(addresses.governance); - vm.stopBroadcast(); - console.log("Transparent Proxy Admin deployed at:", address(proxyAdmin)); - addresses.transparentProxyAdmin = address(proxyAdmin); - } - function deployBridgehubContract() internal { - bytes memory bridgeHubBytecode = abi.encodePacked( + address bridgehubImplementation = deployViaCreate2( type(Bridgehub).creationCode, abi.encode(config.l1ChainId, config.ownerAddress, (config.contracts.maxNumberOfChains)) ); - address bridgehubImplementation = deployViaCreate2(bridgeHubBytecode); console.log("Bridgehub Implementation deployed at:", bridgehubImplementation); addresses.bridgehub.bridgehubImplementation = bridgehubImplementation; - bytes memory bytecode = abi.encodePacked( + address bridgehubProxy = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode( bridgehubImplementation, @@ -451,21 +191,19 @@ contract DeployL1Script is Script { abi.encodeCall(Bridgehub.initialize, (config.deployerAddress)) ) ); - address bridgehubProxy = deployViaCreate2(bytecode); console.log("Bridgehub Proxy deployed at:", bridgehubProxy); addresses.bridgehub.bridgehubProxy = bridgehubProxy; } function deployMessageRootContract() internal { - bytes memory messageRootBytecode = abi.encodePacked( + address messageRootImplementation = deployViaCreate2( type(MessageRoot).creationCode, abi.encode(addresses.bridgehub.bridgehubProxy) ); - address messageRootImplementation = deployViaCreate2(messageRootBytecode); console.log("MessageRoot Implementation deployed at:", messageRootImplementation); addresses.bridgehub.messageRootImplementation = messageRootImplementation; - bytes memory bytecode = abi.encodePacked( + address messageRootProxy = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode( messageRootImplementation, @@ -473,21 +211,19 @@ contract DeployL1Script is Script { abi.encodeCall(MessageRoot.initialize, ()) ) ); - address messageRootProxy = deployViaCreate2(bytecode); console.log("Message Root Proxy deployed at:", messageRootProxy); addresses.bridgehub.messageRootProxy = messageRootProxy; } function deployCTMDeploymentTracker() internal { - bytes memory ctmDTBytecode = abi.encodePacked( + address ctmDTImplementation = deployViaCreate2( type(CTMDeploymentTracker).creationCode, abi.encode(addresses.bridgehub.bridgehubProxy, addresses.bridges.sharedBridgeProxy) ); - address ctmDTImplementation = deployViaCreate2(ctmDTBytecode); console.log("CTM Deployment Tracker Implementation deployed at:", ctmDTImplementation); addresses.bridgehub.ctmDeploymentTrackerImplementation = ctmDTImplementation; - bytes memory bytecode = abi.encodePacked( + address ctmDTProxy = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode( ctmDTImplementation, @@ -495,7 +231,6 @@ contract DeployL1Script is Script { abi.encodeCall(CTMDeploymentTracker.initialize, (config.deployerAddress)) ) ); - address ctmDTProxy = deployViaCreate2(bytecode); console.log("CTM Deployment Tracker Proxy deployed at:", ctmDTProxy); addresses.bridgehub.ctmDeploymentTrackerProxy = ctmDTProxy; } @@ -503,144 +238,10 @@ contract DeployL1Script is Script { function deployBlobVersionedHashRetriever() internal { // solc contracts/state-transition/utils/blobVersionedHashRetriever.yul --strict-assembly --bin bytes memory bytecode = hex"600b600b5f39600b5ff3fe5f358049805f5260205ff3"; - address contractAddress = deployViaCreate2(bytecode); + address contractAddress = deployViaCreate2(bytecode, ""); console.log("BlobVersionedHashRetriever deployed at:", contractAddress); addresses.blobVersionedHashRetriever = contractAddress; } - - function deployChainTypeManagerContract() internal { - deployStateTransitionDiamondFacets(); - deployChainTypeManagerImplementation(); - deployChainTypeManagerProxy(); - registerChainTypeManager(); - } - - function deployStateTransitionDiamondFacets() internal { - address executorFacet = deployViaCreate2(type(ExecutorFacet).creationCode); - console.log("ExecutorFacet deployed at:", executorFacet); - addresses.stateTransition.executorFacet = executorFacet; - - 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, config.l1ChainId)) - ); - console.log("MailboxFacet deployed at:", mailboxFacet); - addresses.stateTransition.mailboxFacet = mailboxFacet; - - address gettersFacet = deployViaCreate2(type(GettersFacet).creationCode); - console.log("GettersFacet deployed at:", gettersFacet); - addresses.stateTransition.gettersFacet = gettersFacet; - - address diamondInit = deployViaCreate2(type(DiamondInit).creationCode); - console.log("DiamondInit deployed at:", diamondInit); - addresses.stateTransition.diamondInit = diamondInit; - } - - function deployChainTypeManagerImplementation() internal { - bytes memory bytecode = abi.encodePacked( - type(ChainTypeManager).creationCode, - abi.encode(addresses.bridgehub.bridgehubProxy) - ); - address contractAddress = deployViaCreate2(bytecode); - console.log("ChainTypeManagerImplementation deployed at:", contractAddress); - addresses.stateTransition.chainTypeManagerImplementation = contractAddress; - } - - function deployChainTypeManagerProxy() internal { - Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); - facetCuts[0] = Diamond.FacetCut({ - facet: addresses.stateTransition.adminFacet, - action: Diamond.Action.Add, - isFreezable: false, - selectors: Utils.getAllSelectors(addresses.stateTransition.adminFacet.code) - }); - facetCuts[1] = Diamond.FacetCut({ - facet: addresses.stateTransition.gettersFacet, - action: Diamond.Action.Add, - isFreezable: false, - selectors: Utils.getAllSelectors(addresses.stateTransition.gettersFacet.code) - }); - facetCuts[2] = Diamond.FacetCut({ - facet: addresses.stateTransition.mailboxFacet, - action: Diamond.Action.Add, - isFreezable: true, - selectors: Utils.getAllSelectors(addresses.stateTransition.mailboxFacet.code) - }); - facetCuts[3] = Diamond.FacetCut({ - facet: addresses.stateTransition.executorFacet, - action: Diamond.Action.Add, - isFreezable: true, - selectors: Utils.getAllSelectors(addresses.stateTransition.executorFacet.code) - }); - - VerifierParams memory verifierParams = VerifierParams({ - recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, - recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, - recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash - }); - - FeeParams memory feeParams = FeeParams({ - pubdataPricingMode: config.contracts.diamondInitPubdataPricingMode, - batchOverheadL1Gas: uint32(config.contracts.diamondInitBatchOverheadL1Gas), - maxPubdataPerBatch: uint32(config.contracts.diamondInitMaxPubdataPerBatch), - maxL2GasPerBatch: uint32(config.contracts.diamondInitMaxL2GasPerBatch), - priorityTxMaxPubdata: uint32(config.contracts.diamondInitPriorityTxMaxPubdata), - minimalL2GasPrice: uint64(config.contracts.diamondInitMinimalL2GasPrice) - }); - - DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ - verifier: IVerifier(addresses.stateTransition.verifier), - verifierParams: verifierParams, - l2BootloaderBytecodeHash: config.contracts.bootloaderHash, - l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, - priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, - feeParams: feeParams, - blobVersionedHashRetriever: addresses.blobVersionedHashRetriever - }); - - Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ - facetCuts: facetCuts, - initAddress: addresses.stateTransition.diamondInit, - initCalldata: abi.encode(initializeData) - }); - - config.contracts.diamondCutData = abi.encode(diamondCut); - - ChainCreationParams memory chainCreationParams = ChainCreationParams({ - genesisUpgrade: addresses.stateTransition.genesisUpgrade, - genesisBatchHash: config.contracts.genesisRoot, - genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), - genesisBatchCommitment: config.contracts.genesisBatchCommitment, - diamondCut: diamondCut, - forceDeploymentsData: generatedData.forceDeploymentsData - }); - - ChainTypeManagerInitializeData memory diamondInitData = ChainTypeManagerInitializeData({ - owner: msg.sender, - validatorTimelock: addresses.validatorTimelock, - chainCreationParams: chainCreationParams, - protocolVersion: config.contracts.latestProtocolVersion - }); - - address contractAddress = deployViaCreate2( - abi.encodePacked( - type(TransparentUpgradeableProxy).creationCode, - abi.encode( - addresses.stateTransition.chainTypeManagerImplementation, - addresses.transparentProxyAdmin, - abi.encodeCall(ChainTypeManager.initialize, (diamondInitData)) - ) - ) - ); - console.log("ChainTypeManagerProxy deployed at:", contractAddress); - addresses.stateTransition.chainTypeManagerProxy = contractAddress; - } - function registerChainTypeManager() internal { Bridgehub bridgehub = Bridgehub(addresses.bridgehub.bridgehubProxy); vm.startBroadcast(msg.sender); @@ -689,11 +290,10 @@ contract DeployL1Script is Script { initAddress: address(0), initCalldata: "" }); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(DiamondProxy).creationCode, abi.encode(config.l1ChainId, diamondCut) ); - address contractAddress = deployViaCreate2(bytecode); console.log("DiamondProxy deployed at:", contractAddress); addresses.stateTransition.diamondProxy = contractAddress; } @@ -710,29 +310,27 @@ contract DeployL1Script is Script { function deployL1NullifierImplementation() internal { // TODO(EVM-743): allow non-dev nullifier in the local deployment - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1NullifierDev).creationCode, // solhint-disable-next-line func-named-parameters abi.encode(addresses.bridgehub.bridgehubProxy, config.eraChainId, addresses.stateTransition.diamondProxy) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NullifierImplementation deployed at:", contractAddress); addresses.bridges.l1NullifierImplementation = contractAddress; } function deployL1NullifierProxy() internal { bytes memory initCalldata = abi.encodeCall(L1Nullifier.initialize, (config.deployerAddress, 1, 1, 1, 0)); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.bridges.l1NullifierImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NullifierProxy deployed at:", contractAddress); addresses.bridges.l1NullifierProxy = contractAddress; } function deploySharedBridgeImplementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1AssetRouter).creationCode, // solhint-disable-next-line func-named-parameters abi.encode( @@ -743,18 +341,16 @@ contract DeployL1Script is Script { addresses.stateTransition.diamondProxy ) ); - address contractAddress = deployViaCreate2(bytecode); console.log("SharedBridgeImplementation deployed at:", contractAddress); addresses.bridges.sharedBridgeImplementation = contractAddress; } function deploySharedBridgeProxy() internal { bytes memory initCalldata = abi.encodeCall(L1AssetRouter.initialize, (config.deployerAddress)); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.bridges.sharedBridgeImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("SharedBridgeProxy deployed at:", contractAddress); addresses.bridges.sharedBridgeProxy = contractAddress; } @@ -774,7 +370,7 @@ contract DeployL1Script is Script { } function deployErc20BridgeImplementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1ERC20Bridge).creationCode, abi.encode( addresses.bridges.l1NullifierProxy, @@ -783,18 +379,16 @@ contract DeployL1Script is Script { config.eraChainId ) ); - address contractAddress = deployViaCreate2(bytecode); console.log("Erc20BridgeImplementation deployed at:", contractAddress); addresses.bridges.erc20BridgeImplementation = contractAddress; } function deployErc20BridgeProxy() internal { bytes memory initCalldata = abi.encodeCall(L1ERC20Bridge.initialize, ()); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.bridges.erc20BridgeImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("Erc20BridgeProxy deployed at:", contractAddress); addresses.bridges.erc20BridgeProxy = contractAddress; } @@ -807,22 +401,16 @@ contract DeployL1Script is Script { } function deployBridgedStandardERC20Implementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(BridgedStandardERC20).creationCode, // solhint-disable-next-line func-named-parameters abi.encode() ); - address contractAddress = deployViaCreate2(bytecode); console.log("BridgedStandardERC20Implementation deployed at:", contractAddress); addresses.bridges.bridgedStandardERC20Implementation = contractAddress; } function deployBridgedTokenBeacon() internal { - bytes memory bytecode = abi.encodePacked( - type(UpgradeableBeacon).creationCode, - // solhint-disable-next-line func-named-parameters - abi.encode(addresses.bridges.bridgedStandardERC20Implementation) - ); UpgradeableBeacon beacon = new UpgradeableBeacon(addresses.bridges.bridgedStandardERC20Implementation); address contractAddress = address(beacon); beacon.transferOwnership(config.ownerAddress); @@ -831,7 +419,7 @@ contract DeployL1Script is Script { } function deployL1NativeTokenVaultImplementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1NativeTokenVault).creationCode, // solhint-disable-next-line func-named-parameters abi.encode( @@ -841,7 +429,6 @@ contract DeployL1Script is Script { addresses.bridges.l1NullifierProxy ) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NativeTokenVaultImplementation deployed at:", contractAddress); addresses.vaults.l1NativeTokenVaultImplementation = contractAddress; } @@ -851,11 +438,10 @@ contract DeployL1Script is Script { L1NativeTokenVault.initialize, (config.ownerAddress, addresses.bridges.bridgedTokenBeacon) ); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.vaults.l1NativeTokenVaultImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NativeTokenVaultProxy deployed at:", contractAddress); addresses.vaults.l1NativeTokenVaultProxy = contractAddress; @@ -908,6 +494,32 @@ contract DeployL1Script is Script { console.log("Owners updated"); } + function saveDiamondSelectors() public { + AdminFacet adminFacet = new AdminFacet(1); + GettersFacet gettersFacet = new GettersFacet(); + MailboxFacet mailboxFacet = new MailboxFacet(1, 1); + ExecutorFacet executorFacet = new ExecutorFacet(); + bytes4[] memory adminFacetSelectors = Utils.getAllSelectors(address(adminFacet).code); + bytes4[] memory gettersFacetSelectors = Utils.getAllSelectors(address(gettersFacet).code); + bytes4[] memory mailboxFacetSelectors = Utils.getAllSelectors(address(mailboxFacet).code); + bytes4[] memory executorFacetSelectors = Utils.getAllSelectors(address(executorFacet).code); + + string memory root = vm.projectRoot(); + string memory outputPath = string.concat(root, "/script-out/diamond-selectors.toml"); + + bytes memory adminFacetSelectorsBytes = abi.encode(adminFacetSelectors); + bytes memory gettersFacetSelectorsBytes = abi.encode(gettersFacetSelectors); + bytes memory mailboxFacetSelectorsBytes = abi.encode(mailboxFacetSelectors); + bytes memory executorFacetSelectorsBytes = abi.encode(executorFacetSelectors); + + vm.serializeBytes("diamond_selectors", "admin_facet_selectors", adminFacetSelectorsBytes); + vm.serializeBytes("diamond_selectors", "getters_facet_selectors", gettersFacetSelectorsBytes); + vm.serializeBytes("diamond_selectors", "mailbox_facet_selectors", mailboxFacetSelectorsBytes); + string memory toml = vm.serializeBytes("diamond_selectors", "executor_facet_selectors", executorFacetSelectorsBytes); + + vm.writeToml(toml, outputPath); + } + function saveOutput(string memory outputPath) internal { vm.serializeAddress("bridgehub", "bridgehub_proxy_addr", addresses.bridgehub.bridgehubProxy); vm.serializeAddress("bridgehub", "bridgehub_implementation_addr", addresses.bridgehub.bridgehubImplementation); @@ -1071,10 +683,6 @@ contract DeployL1Script is Script { vm.writeToml(toml, outputPath); } - function deployViaCreate2(bytes memory _bytecode) internal returns (address) { - return Utils.deployViaCreate2(_bytecode, config.contracts.create2FactorySalt, addresses.create2Factory); - } - function prepareForceDeploymentsData() internal view returns (bytes memory) { require(addresses.governance != address(0), "Governance address is not set"); @@ -1101,5 +709,5 @@ contract DeployL1Script is Script { } // add this to be excluded from coverage report - function test() internal {} + function test() internal virtual override {} } diff --git a/l1-contracts/deploy-scripts/DeployUtils.s.sol b/l1-contracts/deploy-scripts/DeployUtils.s.sol new file mode 100644 index 000000000..9ccee1373 --- /dev/null +++ b/l1-contracts/deploy-scripts/DeployUtils.s.sol @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {StateTransitionDeployedAddresses, Utils, L2_BRIDGEHUB_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT_ADDRESS} from "./Utils.sol"; +import {Multicall3} from "contracts/dev-contracts/Multicall3.sol"; +import {Verifier} from "contracts/state-transition/Verifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; +import {Governance} from "contracts/governance/Governance.sol"; +import {L1GenesisUpgrade} from "contracts/upgrades/L1GenesisUpgrade.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; +import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; +import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; +import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; +import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; +import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; +import {ChainTypeManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IChainTypeManager.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; +import {InitializeDataNewChain as DiamondInitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; +import {FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1ERC20Bridge} from "contracts/bridge/L1ERC20Bridge.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {AddressHasNoCode} from "./ZkSyncScriptErrors.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IL2ContractDeployer} from "contracts/common/interfaces/IL2ContractDeployer.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {IL1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; +import {L1NullifierDev} from "contracts/dev-contracts/L1NullifierDev.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; + +struct FixedForceDeploymentsData { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + bytes32 l2TokenProxyBytecodeHash; + address aliasedL1Governance; + uint256 maxNumberOfZKChains; + bytes32 bridgehubBytecodeHash; + bytes32 l2AssetRouterBytecodeHash; + bytes32 l2NtvBytecodeHash; + bytes32 messageRootBytecodeHash; +} + +// solhint-disable-next-line gas-struct-packing +struct DeployedAddresses { + BridgehubDeployedAddresses bridgehub; + StateTransitionDeployedAddresses stateTransition; + BridgesDeployedAddresses bridges; + L1NativeTokenVaultAddresses vaults; + DataAvailabilityDeployedAddresses daAddresses; + address transparentProxyAdmin; + address governance; + address chainAdmin; + address accessControlRestrictionAddress; + address blobVersionedHashRetriever; + address validatorTimelock; + address create2Factory; +} + +// solhint-disable-next-line gas-struct-packing +struct L1NativeTokenVaultAddresses { + address l1NativeTokenVaultImplementation; + address l1NativeTokenVaultProxy; +} + +struct DataAvailabilityDeployedAddresses { + address l1RollupDAValidator; + address l1ValidiumDAValidator; +} + +// solhint-disable-next-line gas-struct-packing +struct BridgehubDeployedAddresses { + address bridgehubImplementation; + address bridgehubProxy; + address ctmDeploymentTrackerImplementation; + address ctmDeploymentTrackerProxy; + address messageRootImplementation; + address messageRootProxy; +} + +// solhint-disable-next-line gas-struct-packing +struct BridgesDeployedAddresses { + address erc20BridgeImplementation; + address erc20BridgeProxy; + address sharedBridgeImplementation; + address sharedBridgeProxy; + address l1NullifierImplementation; + address l1NullifierProxy; + address bridgedStandardERC20Implementation; + address bridgedTokenBeacon; +} + +// solhint-disable-next-line gas-struct-packing +struct Config { + uint256 l1ChainId; + address deployerAddress; + uint256 eraChainId; + address ownerAddress; + bool testnetVerifier; + ContractsConfig contracts; + TokensConfig tokens; +} + +// solhint-disable-next-line gas-struct-packing +struct ContractsConfig { + bytes32 create2FactorySalt; + address create2FactoryAddr; + address multicall3Addr; + uint256 validatorTimelockExecutionDelay; + bytes32 genesisRoot; + uint256 genesisRollupLeafIndex; + bytes32 genesisBatchCommitment; + uint256 latestProtocolVersion; + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; + uint256 priorityTxMaxGasLimit; + PubdataPricingMode diamondInitPubdataPricingMode; + uint256 diamondInitBatchOverheadL1Gas; + uint256 diamondInitMaxPubdataPerBatch; + uint256 diamondInitMaxL2GasPerBatch; + uint256 diamondInitPriorityTxMaxPubdata; + uint256 diamondInitMinimalL2GasPrice; + address governanceSecurityCouncilAddress; + uint256 governanceMinDelay; + uint256 maxNumberOfChains; + bytes diamondCutData; + bytes32 bootloaderHash; + bytes32 defaultAAHash; +} + +struct TokensConfig { + address tokenWethAddress; +} + +// solhint-disable-next-line gas-struct-packing +struct GeneratedData { + bytes forceDeploymentsData; +} + +contract DeployUtils is Script { + using stdToml for string; + + Config public config; + GeneratedData internal generatedData; + DeployedAddresses internal addresses; + + function initializeConfig(string memory configPath) internal { + string memory toml = vm.readFile(configPath); + + config.l1ChainId = block.chainid; + config.deployerAddress = msg.sender; + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + config.eraChainId = toml.readUint("$.era_chain_id"); + config.ownerAddress = toml.readAddress("$.owner_address"); + config.testnetVerifier = toml.readBool("$.testnet_verifier"); + + config.contracts.governanceSecurityCouncilAddress = toml.readAddress( + "$.contracts.governance_security_council_address" + ); + config.contracts.governanceMinDelay = toml.readUint("$.contracts.governance_min_delay"); + config.contracts.maxNumberOfChains = toml.readUint("$.contracts.max_number_of_chains"); + config.contracts.create2FactorySalt = toml.readBytes32("$.contracts.create2_factory_salt"); + if (vm.keyExistsToml(toml, "$.contracts.create2_factory_addr")) { + config.contracts.create2FactoryAddr = toml.readAddress("$.contracts.create2_factory_addr"); + } + config.contracts.validatorTimelockExecutionDelay = toml.readUint( + "$.contracts.validator_timelock_execution_delay" + ); + config.contracts.genesisRoot = toml.readBytes32("$.contracts.genesis_root"); + config.contracts.genesisRollupLeafIndex = toml.readUint("$.contracts.genesis_rollup_leaf_index"); + config.contracts.genesisBatchCommitment = toml.readBytes32("$.contracts.genesis_batch_commitment"); + config.contracts.latestProtocolVersion = toml.readUint("$.contracts.latest_protocol_version"); + config.contracts.recursionNodeLevelVkHash = toml.readBytes32("$.contracts.recursion_node_level_vk_hash"); + config.contracts.recursionLeafLevelVkHash = toml.readBytes32("$.contracts.recursion_leaf_level_vk_hash"); + config.contracts.recursionCircuitsSetVksHash = toml.readBytes32("$.contracts.recursion_circuits_set_vks_hash"); + config.contracts.priorityTxMaxGasLimit = toml.readUint("$.contracts.priority_tx_max_gas_limit"); + config.contracts.diamondInitPubdataPricingMode = PubdataPricingMode( + toml.readUint("$.contracts.diamond_init_pubdata_pricing_mode") + ); + config.contracts.diamondInitBatchOverheadL1Gas = toml.readUint( + "$.contracts.diamond_init_batch_overhead_l1_gas" + ); + config.contracts.diamondInitMaxPubdataPerBatch = toml.readUint( + "$.contracts.diamond_init_max_pubdata_per_batch" + ); + config.contracts.diamondInitMaxL2GasPerBatch = toml.readUint("$.contracts.diamond_init_max_l2_gas_per_batch"); + config.contracts.diamondInitPriorityTxMaxPubdata = toml.readUint( + "$.contracts.diamond_init_priority_tx_max_pubdata" + ); + config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); + config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); + config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); + + config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); + } + + function deployViaCreate2(bytes memory creationCode, bytes memory constructorArgs) internal virtual returns (address) { + return Utils.deployViaCreate2(abi.encodePacked(creationCode, constructorArgs), config.contracts.create2FactorySalt, addresses.create2Factory); + } + + function deployVerifier() internal { + bytes memory code; + if (config.testnetVerifier) { + code = type(TestnetVerifier).creationCode; + } else { + code = type(Verifier).creationCode; + } + address contractAddress = deployViaCreate2(code, ""); + console.log("Verifier deployed at:", contractAddress); + addresses.stateTransition.verifier = contractAddress; + } + + function deployDefaultUpgrade() internal { + address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode, ""); + console.log("DefaultUpgrade deployed at:", contractAddress); + addresses.stateTransition.defaultUpgrade = contractAddress; + } + + function deployGenesisUpgrade() internal { + address contractAddress = deployViaCreate2(type(L1GenesisUpgrade).creationCode, ""); + console.log("GenesisUpgrade deployed at:", contractAddress); + addresses.stateTransition.genesisUpgrade = contractAddress; + } + + function deployValidatorTimelock() internal { + uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); + address contractAddress = deployViaCreate2( + type(ValidatorTimelock).creationCode, + abi.encode(config.deployerAddress, executionDelay, config.eraChainId) + ); + console.log("ValidatorTimelock deployed at:", contractAddress); + addresses.validatorTimelock = contractAddress; + } + + function deployGovernance() internal { + address contractAddress = deployViaCreate2( + type(Governance).creationCode, + abi.encode( + config.ownerAddress, + config.contracts.governanceSecurityCouncilAddress, + config.contracts.governanceMinDelay + ) + ); + console.log("Governance deployed at:", contractAddress); + addresses.governance = contractAddress; + } + + function deployChainAdmin() internal { + address accessControlRestriction = deployViaCreate2( + type(AccessControlRestriction).creationCode, + abi.encode(uint256(0), config.ownerAddress) + ); + + console.log("Access control restriction deployed at:", accessControlRestriction); + address[] memory restrictions = new address[](1); + restrictions[0] = accessControlRestriction; + addresses.accessControlRestrictionAddress = accessControlRestriction; + + address contractAddress = deployViaCreate2( + type(ChainAdmin).creationCode, + abi.encode(restrictions) + ); + console.log("ChainAdmin deployed at:", contractAddress); + addresses.chainAdmin = contractAddress; + } + + function deployTransparentProxyAdmin() internal { + vm.startBroadcast(); + ProxyAdmin proxyAdmin = new ProxyAdmin(); + proxyAdmin.transferOwnership(addresses.governance); + vm.stopBroadcast(); + console.log("Transparent Proxy Admin deployed at:", address(proxyAdmin)); + addresses.transparentProxyAdmin = address(proxyAdmin); + } + + function deployChainTypeManagerContract() internal { + deployStateTransitionDiamondFacets(); + deployChainTypeManagerImplementation(); + deployChainTypeManagerProxy(); + } + + function deployStateTransitionDiamondFacets() internal { + address executorFacet = deployViaCreate2(type(ExecutorFacet).creationCode, abi.encode()); + console.log("ExecutorFacet deployed at:", executorFacet); + addresses.stateTransition.executorFacet = executorFacet; + + address adminFacet = deployViaCreate2( + type(AdminFacet).creationCode, + abi.encode(config.l1ChainId) + ); + console.log("AdminFacet deployed at:", adminFacet); + addresses.stateTransition.adminFacet = adminFacet; + + address mailboxFacet = deployViaCreate2( + type(MailboxFacet).creationCode, + abi.encode(config.eraChainId, config.l1ChainId) + ); + console.log("MailboxFacet deployed at:", mailboxFacet); + addresses.stateTransition.mailboxFacet = mailboxFacet; + + address gettersFacet = deployViaCreate2(type(GettersFacet).creationCode, ""); + console.log("GettersFacet deployed at:", gettersFacet); + addresses.stateTransition.gettersFacet = gettersFacet; + + address diamondInit = deployViaCreate2(type(DiamondInit).creationCode, ""); + console.log("DiamondInit deployed at:", diamondInit); + addresses.stateTransition.diamondInit = diamondInit; + } + + function deployChainTypeManagerImplementation() internal { + bytes memory bytecode = type(ChainTypeManager).creationCode; + bytes memory constructorArgs = abi.encode(addresses.bridgehub.bridgehubProxy); + address contractAddress = deployViaCreate2(bytecode, constructorArgs); + console.log("ChainTypeManagerImplementation deployed at:", contractAddress); + addresses.stateTransition.chainTypeManagerImplementation = contractAddress; + } + + function deployChainTypeManagerProxy() internal { + string memory root = vm.projectRoot(); + string memory inputPath = string.concat(root, "/script-out/diamond-selectors.toml"); + string memory toml = vm.readFile(inputPath); + + bytes memory adminFacetSelectors = toml.readBytes("$.admin_facet_selectors"); + bytes memory gettersFacetSelectors = toml.readBytes("$.getters_facet_selectors"); + bytes memory mailboxFacetSelectors = toml.readBytes("$.mailbox_facet_selectors"); + bytes memory executorFacetSelectors = toml.readBytes("$.executor_facet_selectors"); + + bytes4[] memory adminFacetSelectorsArray = abi.decode(adminFacetSelectors, (bytes4[])); + bytes4[] memory gettersFacetSelectorsArray = abi.decode(gettersFacetSelectors, (bytes4[])); + bytes4[] memory mailboxFacetSelectorsArray = abi.decode(mailboxFacetSelectors, (bytes4[])); + bytes4[] memory executorFacetSelectorsArray = abi.decode(executorFacetSelectors, (bytes4[])); + + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); + facetCuts[0] = Diamond.FacetCut({ + facet: addresses.stateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: adminFacetSelectorsArray + }); + facetCuts[1] = Diamond.FacetCut({ + facet: addresses.stateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: gettersFacetSelectorsArray + }); + facetCuts[2] = Diamond.FacetCut({ + facet: addresses.stateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: mailboxFacetSelectorsArray + }); + facetCuts[3] = Diamond.FacetCut({ + facet: addresses.stateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: executorFacetSelectorsArray + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash + }); + + FeeParams memory feeParams = FeeParams({ + pubdataPricingMode: config.contracts.diamondInitPubdataPricingMode, + batchOverheadL1Gas: uint32(config.contracts.diamondInitBatchOverheadL1Gas), + maxPubdataPerBatch: uint32(config.contracts.diamondInitMaxPubdataPerBatch), + maxL2GasPerBatch: uint32(config.contracts.diamondInitMaxL2GasPerBatch), + priorityTxMaxPubdata: uint32(config.contracts.diamondInitPriorityTxMaxPubdata), + minimalL2GasPrice: uint64(config.contracts.diamondInitMinimalL2GasPrice) + }); + + DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ + verifier: IVerifier(addresses.stateTransition.verifier), + verifierParams: verifierParams, + l2BootloaderBytecodeHash: config.contracts.bootloaderHash, + l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, + priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, + feeParams: feeParams, + blobVersionedHashRetriever: addresses.blobVersionedHashRetriever + }); + + Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: addresses.stateTransition.diamondInit, + initCalldata: abi.encode(initializeData) + }); + + config.contracts.diamondCutData = abi.encode(diamondCut); + + ChainCreationParams memory chainCreationParams = ChainCreationParams({ + genesisUpgrade: addresses.stateTransition.genesisUpgrade, + genesisBatchHash: config.contracts.genesisRoot, + genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), + genesisBatchCommitment: config.contracts.genesisBatchCommitment, + diamondCut: diamondCut, + forceDeploymentsData: generatedData.forceDeploymentsData + }); + + ChainTypeManagerInitializeData memory diamondInitData = ChainTypeManagerInitializeData({ + owner: msg.sender, + validatorTimelock: addresses.validatorTimelock, + chainCreationParams: chainCreationParams, + protocolVersion: config.contracts.latestProtocolVersion + }); + + address contractAddress = deployViaCreate2( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + addresses.stateTransition.chainTypeManagerImplementation, + addresses.transparentProxyAdmin, + abi.encodeCall(ChainTypeManager.initialize, (diamondInitData)) + ) + ); + console.log("ChainTypeManagerProxy deployed at:", contractAddress); + addresses.stateTransition.chainTypeManagerProxy = contractAddress; + } + + + + function test() internal virtual {} +} diff --git a/l1-contracts/package.json b/l1-contracts/package.json index 6d68729e2..e4537c08a 100644 --- a/l1-contracts/package.json +++ b/l1-contracts/package.json @@ -58,8 +58,8 @@ "clean": "hardhat clean && CONTRACTS_BASE_NETWORK_ZKSYNC=true hardhat clean", "clean:foundry": "forge clean", "test": "yarn workspace da-contracts build && hardhat test test/unit_tests/*.spec.ts --network hardhat", - "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*'", - "test:zkfoundry": "forge test --zksync --match-path 'test/foundry/l2/*'", + "test:foundry": "forge script --sig 0x2dd0ebe3 DeployL1Script --ffi && forge test --ffi --match-path 'test/foundry/l1/*'", + "test:zkfoundry": "forge script --sig 0x2dd0ebe3 DeployL1Script --ffi && forge test --zksync --match-path 'test/foundry/l2/*'", "test:fork": "TEST_CONTRACTS_FORK=1 yarn run hardhat test test/unit_tests/*.fork.ts --network hardhat", "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/bridge/.*L2.*.sol'", "deploy-no-build": "ts-node scripts/deploy.ts", diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index 2cfd6e5b6..0b8efde4c 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -12,7 +12,7 @@ import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; -import {DeployedAddresses, Config} from "deploy-scripts/DeployL1.s.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; contract L1ContractDeployer is Test { using stdStorage for StdStorage; @@ -69,15 +69,6 @@ contract L1ContractDeployer is Test { bridgeHubOwnerAddress = bridgeHub.owner(); } - function _deployL2Contracts() internal { - vm.setEnv("L2_CONFIG", "/test/foundry/l2/integration/script-config/config-deploy-l2.toml"); - vm.setEnv("L2_OUTPUT", "/test/foundry/l2/integration/script-out/output-deploy-l2.toml"); - - l1Script = new DeployL1Script(); - l1Script.runForL2Test(); - - chainTypeManager = IChainTypeManager(ecosystemAddresses.stateTransition.chainTypeManagerProxy); - } function _acceptOwnership() private { vm.startPrank(bridgeHub.pendingOwner()); diff --git a/l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol similarity index 66% rename from l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol rename to l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index cdc96762e..aa18507e1 100644 --- a/l1-contracts/test/foundry/l2/integration/L2Gateway.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -26,11 +26,10 @@ import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2Utils} from "../unit/utils/L2Utils.sol"; import {SystemContractsArgs} from "../unit/utils/L2Utils.sol"; -import {L1ContractDeployer} from "../../l1/integration/_SharedL1ContractDeployer.t.sol"; -import {ZKChainDeployer} from "../../l1/integration/_SharedZKChainDeployer.t.sol"; -import {TokenDeployer} from "../../l1/integration/_SharedTokenDeployer.t.sol"; +import {L2ContractDeployer} from "./_SharedL2ContractDeployer.t.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; -contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer { +contract L2GatewayTests is Test, L2ContractDeployer { // is L1ContractDeployer, ZKChainDeployer, TokenDeployer { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. address internal l1BridgeWallet = address(1); @@ -49,7 +48,8 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer { uint256 internal constant L1_CHAIN_ID = 9; uint256 internal ERA_CHAIN_ID = 270; - address internal l1AssetRouter; + address internal l1AssetRouter = makeAddr("l1AssetRouter"); + address internal aliasedL1AssetRouter = AddressAliasHelper.applyL1ToL2Alias(l1AssetRouter); address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); address internal l1CTM = makeAddr("l1CTM"); @@ -60,6 +60,10 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer { bytes internal exampleChainCommitment; + IChainTypeManager internal chainTypeManager; + + // L2ContractDeployer internal deployScript; + function setUp() public { aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); @@ -86,16 +90,19 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer { l2TokenBeacon: address(beacon), l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, aliasedOwner: ownerWallet, - contractsDeployedAlready: true + contractsDeployedAlready: false, + l1CtmDeployer: l1CTMDeployer }) ); + deployL2Contracts(); - _deployL2Contracts(); - - vm.prank(l1AssetRouter); + vm.prank(aliasedL1AssetRouter); l2AssetRouter.setAssetHandlerAddress(L1_CHAIN_ID, ctmAssetId, L2_BRIDGEHUB_ADDR); - vm.prank(l1CTMDeployer); - l2Bridgehub.setAssetHandlerAddress(bytes32(uint256(uint160(l1CTM))), address(chainTypeManager)); + vm.prank(ownerWallet); + l2Bridgehub.addChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); + vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1CTMDeployer)); + l2Bridgehub.setAssetHandlerAddress(bytes32(uint256(uint160(l1CTM))), address(addresses.stateTransition.chainTypeManagerProxy)); + chainTypeManager = IChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); } function test_gatewayShouldFinalizeDeposit() public { @@ -103,31 +110,30 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer { } function test_forwardToL3OnGateway() public { - finalizeDeposit(); + // todo fix this test + // finalizeDeposit(); - IBridgehub bridgehub = IBridgehub(bridgeHub); - // vm.chainId(12345); - vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); - bridgehub.forwardTransactionOnGateway(ERA_CHAIN_ID, bytes32(0), 0); - vm.stopBroadcast(); + // IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); + // vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); + // bridgehub.forwardTransactionOnGateway(ERA_CHAIN_ID, bytes32(0), 0); } function finalizeDeposit() public { - bytes memory chainData = abi.encode(exampleChainCommitment); - bytes memory ctmData = abi.encode( - baseTokenAssetId, - msg.sender, - chainTypeManager.protocolVersion(), - ecosystemConfig.contracts.diamondCutData - ); - - BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ - chainId: ERA_CHAIN_ID, - baseTokenAssetId: baseTokenAssetId, - ctmData: ctmData, - chainData: chainData - }); - vm.prank(l1AssetRouter); - l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + // bytes memory chainData = abi.encode(exampleChainCommitment); + // bytes memory ctmData = abi.encode( + // baseTokenAssetId, + // msg.sender, + // chainTypeManager.protocolVersion(), + // config.contracts.diamondCutData + // ); + + // BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + // chainId: ERA_CHAIN_ID, + // baseTokenAssetId: baseTokenAssetId, + // ctmData: ctmData, + // chainData: chainData + // }); + // vm.prank(aliasedL1AssetRouter); + // l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); } } diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol new file mode 100644 index 000000000..00a28122b --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {StdStorage, stdStorage, stdToml} from "forge-std/Test.sol"; +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +import {L2Utils} from "../unit/utils/L2Utils.sol"; + + +contract L2ContractDeployer is DeployUtils { + using stdToml for string; + + function deployViaCreate2(bytes memory creationCode, bytes memory constructorArgs) internal override returns (address) { + console.log("Deploying via create2 L2"); + return L2Utils.deployViaCreat2L2(creationCode, constructorArgs, config.contracts.create2FactorySalt); + } + + function deployL2Contracts() public { + string memory root = vm.projectRoot(); + string memory inputPath = string.concat(root, "/script-config/config-deploy-l1.toml"); + initializeConfig(inputPath); + addresses.transparentProxyAdmin = address(0x1); + deployGenesisUpgrade(); + deployChainTypeManagerContract(); + } + + // add this to be excluded from coverage report + function test() internal virtual override {} +} diff --git a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol b/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol index c9902366f..d29d2b7ff 100644 --- a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol @@ -43,6 +43,7 @@ contract L2Erc20BridgeTest is Test { string internal constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; string internal constant TOKEN_DEFAULT_SYMBOL = "TET"; uint8 internal constant TOKEN_DEFAULT_DECIMALS = 18; + address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); function setUp() public { aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); @@ -70,12 +71,13 @@ contract L2Erc20BridgeTest is Test { SystemContractsArgs({ l1ChainId: L1_CHAIN_ID, eraChainId: ERA_CHAIN_ID, - l1AssetRouter: L2_ASSET_ROUTER_ADDR, + l1AssetRouter: l1BridgeWallet, legacySharedBridge: l2SharedBridge, l2TokenBeacon: address(beacon), l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, aliasedOwner: ownerWallet, - contractsDeployedAlready: true + contractsDeployedAlready: false, + l1CtmDeployer: l1CTMDeployer }) ); } diff --git a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol index 5b678fce6..ee0daaef3 100644 --- a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol @@ -5,6 +5,8 @@ pragma solidity ^0.8.20; import {Vm} from "forge-std/Vm.sol"; import "forge-std/console.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR, L2_MESSAGE_ROOT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IContractDeployer, L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -19,6 +21,10 @@ import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; + +import {SystemContractsCaller} from "contracts/common/libraries/SystemContractsCaller.sol"; +import {DeployFailed} from "contracts/common/L1ContractErrors.sol"; struct SystemContractsArgs { uint256 l1ChainId; @@ -29,6 +35,7 @@ struct SystemContractsArgs { bytes32 l2TokenProxyBytecodeHash; address aliasedOwner; bool contractsDeployedAlready; + address l1CtmDeployer; } library L2Utils { @@ -83,7 +90,8 @@ library L2Utils { _args.eraChainId, _args.aliasedOwner, _args.l1AssetRouter, - _args.legacySharedBridge + _args.legacySharedBridge, + _args.l1CtmDeployer ); forceDeployAssetRouter( _args.l1ChainId, @@ -107,16 +115,16 @@ library L2Utils { uint256 _eraChainId, address _aliasedOwner, address _l1AssetRouter, - address _legacySharedBridge + address _legacySharedBridge, + address _l1CtmDeployer ) internal { new Bridgehub(_l1ChainId, _aliasedOwner, 100); forceDeployWithConstructor("Bridgehub", L2_BRIDGEHUB_ADDR, abi.encode(_l1ChainId, _aliasedOwner, 100)); Bridgehub bridgehub = Bridgehub(L2_BRIDGEHUB_ADDR); - address l1CTMDeployer = address(0x1); vm.prank(_aliasedOwner); bridgehub.setAddresses( L2_ASSET_ROUTER_ADDR, - ICTMDeploymentTracker(l1CTMDeployer), + ICTMDeploymentTracker(_l1CtmDeployer), IMessageRoot(L2_MESSAGE_ROOT_ADDR) ); } @@ -193,7 +201,7 @@ library L2Utils { function forceDeployWithConstructor( string memory _contractName, address _address, - bytes memory _constuctorArgs + bytes memory _constructorArgs ) public { bytes memory bytecode = readEraBytecode(_contractName); @@ -205,7 +213,7 @@ library L2Utils { newAddress: _address, callConstructor: true, value: 0, - input: _constuctorArgs + input: _constructorArgs }); vm.prank(L2_FORCE_DEPLOYER_ADDR); @@ -253,4 +261,24 @@ library L2Utils { return abi.encode(encodedName, encodedSymbol, encodedDecimals); } + + function deployViaCreat2L2( + bytes memory creationCode, + bytes memory constructorargs, + bytes32 create2salt + ) internal returns (address) { + bytes memory bytecode = abi.encodePacked(creationCode, constructorargs); + address contractAddress; + assembly { + contractAddress := create2(0, add(bytecode, 0x20), mload(bytecode), create2salt) + } + uint32 size; + assembly { + size := extcodesize(contractAddress) + } + if (size == 0) { + revert DeployFailed(); + } + return contractAddress; + } } From 4c4e3f6cc448f524c5d0e79688c3be0e73958016 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sat, 28 Sep 2024 22:01:26 +0100 Subject: [PATCH 10/42] lint --- l1-contracts/deploy-scripts/DeployL1.s.sol | 8 +++++-- l1-contracts/deploy-scripts/DeployUtils.s.sol | 24 +++++++++---------- .../_SharedL1ContractDeployer.t.sol | 1 - .../l2/integration/L2GatewayTests.t.sol | 12 ++++++---- .../_SharedL2ContractDeployer.t.sol | 6 +++-- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 5380b56ca..34a160d11 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -503,7 +503,7 @@ contract DeployL1Script is Script, DeployUtils { bytes4[] memory gettersFacetSelectors = Utils.getAllSelectors(address(gettersFacet).code); bytes4[] memory mailboxFacetSelectors = Utils.getAllSelectors(address(mailboxFacet).code); bytes4[] memory executorFacetSelectors = Utils.getAllSelectors(address(executorFacet).code); - + string memory root = vm.projectRoot(); string memory outputPath = string.concat(root, "/script-out/diamond-selectors.toml"); @@ -515,7 +515,11 @@ contract DeployL1Script is Script, DeployUtils { vm.serializeBytes("diamond_selectors", "admin_facet_selectors", adminFacetSelectorsBytes); vm.serializeBytes("diamond_selectors", "getters_facet_selectors", gettersFacetSelectorsBytes); vm.serializeBytes("diamond_selectors", "mailbox_facet_selectors", mailboxFacetSelectorsBytes); - string memory toml = vm.serializeBytes("diamond_selectors", "executor_facet_selectors", executorFacetSelectorsBytes); + string memory toml = vm.serializeBytes( + "diamond_selectors", + "executor_facet_selectors", + executorFacetSelectorsBytes + ); vm.writeToml(toml, outputPath); } diff --git a/l1-contracts/deploy-scripts/DeployUtils.s.sol b/l1-contracts/deploy-scripts/DeployUtils.s.sol index 9ccee1373..5aebdccd1 100644 --- a/l1-contracts/deploy-scripts/DeployUtils.s.sol +++ b/l1-contracts/deploy-scripts/DeployUtils.s.sol @@ -225,8 +225,16 @@ contract DeployUtils is Script { config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); } - function deployViaCreate2(bytes memory creationCode, bytes memory constructorArgs) internal virtual returns (address) { - return Utils.deployViaCreate2(abi.encodePacked(creationCode, constructorArgs), config.contracts.create2FactorySalt, addresses.create2Factory); + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal virtual returns (address) { + return + Utils.deployViaCreate2( + abi.encodePacked(creationCode, constructorArgs), + config.contracts.create2FactorySalt, + addresses.create2Factory + ); } function deployVerifier() internal { @@ -287,10 +295,7 @@ contract DeployUtils is Script { restrictions[0] = accessControlRestriction; addresses.accessControlRestrictionAddress = accessControlRestriction; - address contractAddress = deployViaCreate2( - type(ChainAdmin).creationCode, - abi.encode(restrictions) - ); + address contractAddress = deployViaCreate2(type(ChainAdmin).creationCode, abi.encode(restrictions)); console.log("ChainAdmin deployed at:", contractAddress); addresses.chainAdmin = contractAddress; } @@ -315,10 +320,7 @@ contract DeployUtils is Script { console.log("ExecutorFacet deployed at:", executorFacet); addresses.stateTransition.executorFacet = executorFacet; - address adminFacet = deployViaCreate2( - type(AdminFacet).creationCode, - abi.encode(config.l1ChainId) - ); + address adminFacet = deployViaCreate2(type(AdminFacet).creationCode, abi.encode(config.l1ChainId)); console.log("AdminFacet deployed at:", adminFacet); addresses.stateTransition.adminFacet = adminFacet; @@ -448,7 +450,5 @@ contract DeployUtils is Script { addresses.stateTransition.chainTypeManagerProxy = contractAddress; } - - function test() internal virtual {} } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index 0b8efde4c..d7d7866d9 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -69,7 +69,6 @@ contract L1ContractDeployer is Test { bridgeHubOwnerAddress = bridgeHub.owner(); } - function _acceptOwnership() private { vm.startPrank(bridgeHub.pendingOwner()); bridgeHub.acceptOwnership(); diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index aa18507e1..595876a91 100644 --- a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -29,7 +29,10 @@ import {SystemContractsArgs} from "../unit/utils/L2Utils.sol"; import {L2ContractDeployer} from "./_SharedL2ContractDeployer.t.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; -contract L2GatewayTests is Test, L2ContractDeployer { // is L1ContractDeployer, ZKChainDeployer, TokenDeployer { +contract L2GatewayTests is + Test, + L2ContractDeployer // is L1ContractDeployer, ZKChainDeployer, TokenDeployer { +{ // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. address internal l1BridgeWallet = address(1); @@ -101,7 +104,10 @@ contract L2GatewayTests is Test, L2ContractDeployer { // is L1ContractDeployer, vm.prank(ownerWallet); l2Bridgehub.addChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1CTMDeployer)); - l2Bridgehub.setAssetHandlerAddress(bytes32(uint256(uint160(l1CTM))), address(addresses.stateTransition.chainTypeManagerProxy)); + l2Bridgehub.setAssetHandlerAddress( + bytes32(uint256(uint160(l1CTM))), + address(addresses.stateTransition.chainTypeManagerProxy) + ); chainTypeManager = IChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); } @@ -112,7 +118,6 @@ contract L2GatewayTests is Test, L2ContractDeployer { // is L1ContractDeployer, function test_forwardToL3OnGateway() public { // todo fix this test // finalizeDeposit(); - // IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); // vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); // bridgehub.forwardTransactionOnGateway(ERA_CHAIN_ID, bytes32(0), 0); @@ -126,7 +131,6 @@ contract L2GatewayTests is Test, L2ContractDeployer { // is L1ContractDeployer, // chainTypeManager.protocolVersion(), // config.contracts.diamondCutData // ); - // BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ // chainId: ERA_CHAIN_ID, // baseTokenAssetId: baseTokenAssetId, diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol index 00a28122b..549fbef8c 100644 --- a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol @@ -18,11 +18,13 @@ import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; import {L2Utils} from "../unit/utils/L2Utils.sol"; - contract L2ContractDeployer is DeployUtils { using stdToml for string; - function deployViaCreate2(bytes memory creationCode, bytes memory constructorArgs) internal override returns (address) { + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override returns (address) { console.log("Deploying via create2 L2"); return L2Utils.deployViaCreat2L2(creationCode, constructorArgs, config.contracts.create2FactorySalt); } From e7cf415af8bc1b1629d90bf46ffe5fea58c04f2c Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sat, 28 Sep 2024 22:20:21 +0100 Subject: [PATCH 11/42] diamond selectors --- l1-contracts/deploy-scripts/DeployL1.s.sol | 1 + l1-contracts/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 34a160d11..76cb17b21 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -87,6 +87,7 @@ contract DeployL1Script is Script, DeployUtils { inputPath = string.concat(root, inputPath); outputPath = string.concat(root, outputPath); + saveDiamondSelectors(); initializeConfig(inputPath); instantiateCreate2Factory(); diff --git a/l1-contracts/package.json b/l1-contracts/package.json index e4537c08a..c7baa67ab 100644 --- a/l1-contracts/package.json +++ b/l1-contracts/package.json @@ -58,7 +58,7 @@ "clean": "hardhat clean && CONTRACTS_BASE_NETWORK_ZKSYNC=true hardhat clean", "clean:foundry": "forge clean", "test": "yarn workspace da-contracts build && hardhat test test/unit_tests/*.spec.ts --network hardhat", - "test:foundry": "forge script --sig 0x2dd0ebe3 DeployL1Script --ffi && forge test --ffi --match-path 'test/foundry/l1/*'", + "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*'", "test:zkfoundry": "forge script --sig 0x2dd0ebe3 DeployL1Script --ffi && forge test --zksync --match-path 'test/foundry/l2/*'", "test:fork": "TEST_CONTRACTS_FORK=1 yarn run hardhat test test/unit_tests/*.fork.ts --network hardhat", "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/bridge/.*L2.*.sol'", From 5449b51b5ef18de26077636593bbbc82fd3c98e7 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sat, 28 Sep 2024 22:36:50 +0100 Subject: [PATCH 12/42] add back test for coverage --- .../test/foundry/l1/integration/L1GatewayTests.t.sol | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index 271dcc73e..0d558b8db 100644 --- a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -39,7 +39,7 @@ import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; -contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { +contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { uint256 constant TEST_USERS_COUNT = 10; address[] public users; address[] public l2ContractAddresses; @@ -290,6 +290,15 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T assertEq(migratingChainContract.getBaseTokenAssetId(), baseTokenAssetId); } + /// to increase coverage, properly tested in L2GatewayTests + function test_forwardToL3OnGateway() public { + _setUpGatewayWithFilterer(); + vm.chainId(12345); + vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); + bridgeHub.forwardTransactionOnGateway(migratingChainId, bytes32(0), 0); + vm.stopBroadcast(); + } + // add this to be excluded from coverage report function test() internal override {} } From ae57b24d0b4a5dd48de34aa988ffac17303597d9 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sat, 28 Sep 2024 22:38:48 +0100 Subject: [PATCH 13/42] trying to fix foundry deploy script tests --- .../foundry/l2/integration/_SharedL2ContractDeployer.t.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol index 549fbef8c..95411eba8 100644 --- a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol @@ -31,7 +31,10 @@ contract L2ContractDeployer is DeployUtils { function deployL2Contracts() public { string memory root = vm.projectRoot(); - string memory inputPath = string.concat(root, "/script-config/config-deploy-l1.toml"); + string memory inputPath = string.concat( + root, + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml" + ); initializeConfig(inputPath); addresses.transparentProxyAdmin = address(0x1); deployGenesisUpgrade(); From da58aa0021f1fe1d0ba89d565580bd2fd64998e1 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 12:25:34 +0100 Subject: [PATCH 14/42] fixing l1 and l2 tests --- .../contracts/bridgehub/Bridgehub.sol | 7 +- l1-contracts/deploy-scripts/DeployL1.s.sol | 24 --- l1-contracts/deploy-scripts/DeployUtils.s.sol | 25 +++ .../l1/integration/L2GatewayDummyTests.t.sol | 50 +++++ .../L2GatewayDummyTestsAbstract.t.sol | 175 ++++++++++++++++++ .../_SharedL2ContractDummyDeployer.sol | 123 ++++++++++++ .../l2/integration/L2GatewayTests.t.sol | 127 +++---------- .../_SharedL2ContractDeployer.t.sol | 25 +-- .../test/foundry/l2/unit/utils/L2Utils.sol | 22 +-- 9 files changed, 419 insertions(+), 159 deletions(-) create mode 100644 l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol create mode 100644 l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol create mode 100644 l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..e9bb2a305 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -153,14 +153,17 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus // This is indeed true, since the only methods where this immutable is used are the ones with `onlyL1` modifier. // We will change this with interop. ETH_TOKEN_ASSET_ID = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, ETH_TOKEN_ADDRESS); - _transferOwnership(_owner); - whitelistedSettlementLayers[_l1ChainId] = true; + _initialize(_owner); } /// @notice used to initialize the contract /// @notice this contract is also deployed on L2 as a system contract there the owner and the related functions will not be used /// @param _owner the owner of the contract function initialize(address _owner) external reentrancyGuardInitializer { + _initialize(_owner); + } + + function _initialize(address _owner) internal { _transferOwnership(_owner); assetIdIsRegistered[ETH_TOKEN_ASSET_ID] = true; whitelistedSettlementLayers[L1_CHAIN_ID] = true; diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 76cb17b21..3f49947ff 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -62,7 +62,6 @@ contract DeployL1Script is Script, DeployUtils { using stdToml for string; address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; - address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; function run() public { console.log("Deploying L1 contracts"); @@ -134,29 +133,6 @@ contract DeployL1Script is Script, DeployUtils { generatedData.forceDeploymentsData = prepareForceDeploymentsData(); } - function instantiateCreate2Factory() internal { - address contractAddress; - - bool isDeterministicDeployed = DETERMINISTIC_CREATE2_ADDRESS.code.length > 0; - bool isConfigured = config.contracts.create2FactoryAddr != address(0); - - if (isConfigured) { - if (config.contracts.create2FactoryAddr.code.length == 0) { - revert AddressHasNoCode(config.contracts.create2FactoryAddr); - } - contractAddress = config.contracts.create2FactoryAddr; - console.log("Using configured Create2Factory address:", contractAddress); - } else if (isDeterministicDeployed) { - contractAddress = DETERMINISTIC_CREATE2_ADDRESS; - console.log("Using deterministic Create2Factory address:", contractAddress); - } else { - contractAddress = Utils.deployCreate2Factory(); - console.log("Create2Factory deployed at:", contractAddress); - } - - addresses.create2Factory = contractAddress; - } - function deployIfNeededMulticall3() internal { // Multicall3 is already deployed on public networks if (MULTICALL3_ADDRESS.code.length == 0) { diff --git a/l1-contracts/deploy-scripts/DeployUtils.s.sol b/l1-contracts/deploy-scripts/DeployUtils.s.sol index 5aebdccd1..428024998 100644 --- a/l1-contracts/deploy-scripts/DeployUtils.s.sol +++ b/l1-contracts/deploy-scripts/DeployUtils.s.sol @@ -168,6 +168,8 @@ struct GeneratedData { contract DeployUtils is Script { using stdToml for string; + address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + Config public config; GeneratedData internal generatedData; DeployedAddresses internal addresses; @@ -225,6 +227,29 @@ contract DeployUtils is Script { config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); } + function instantiateCreate2Factory() internal { + address contractAddress; + + bool isDeterministicDeployed = DETERMINISTIC_CREATE2_ADDRESS.code.length > 0; + bool isConfigured = config.contracts.create2FactoryAddr != address(0); + + if (isConfigured) { + if (config.contracts.create2FactoryAddr.code.length == 0) { + revert AddressHasNoCode(config.contracts.create2FactoryAddr); + } + contractAddress = config.contracts.create2FactoryAddr; + console.log("Using configured Create2Factory address:", contractAddress); + } else if (isDeterministicDeployed) { + contractAddress = DETERMINISTIC_CREATE2_ADDRESS; + console.log("Using deterministic Create2Factory address:", contractAddress); + } else { + contractAddress = Utils.deployCreate2Factory(); + console.log("Create2Factory deployed at:", contractAddress); + } + + addresses.create2Factory = contractAddress; + } + function deployViaCreate2( bytes memory creationCode, bytes memory constructorArgs diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol b/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol new file mode 100644 index 000000000..a588bc841 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {L2ContractDummyDeployer} from "./_SharedL2ContractDummyDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {L2GatewayDummyTests} from "./L2GatewayDummyTests.t.sol"; + +contract L2GatewayDummyTests is Test, L2ContractDummyDeployer, L2GatewayDummyTests { + function test() internal virtual override(DeployUtils, L2ContractDummyDeployer) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal virtual override(L2GatewayDummyTests, L2ContractDummyDeployer) { + super.initSystemContracts(_args); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public virtual override(L2GatewayDummyTests, L2ContractDummyDeployer) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol new file mode 100644 index 000000000..404822868 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {L2ContractDummyDeployer} from "./_SharedL2ContractDummyDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +abstract contract L2GatewayDummyAbstract is + Test, + DeployUtils + // L2ContractDummyDeployer +{ + // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. + // It is a bit easier to use EOA and it is sufficient for the tests. + address internal l1BridgeWallet = address(1); + address internal aliasedL1BridgeWallet; + + // The owner of the beacon and the native token vault + address internal ownerWallet = address(2); + + BridgedStandardERC20 internal standardErc20Impl; + + UpgradeableBeacon internal beacon; + BeaconProxy internal proxy; + + IL2AssetRouter l2AssetRouter = IL2AssetRouter(L2_ASSET_ROUTER_ADDR); + IBridgehub l2Bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); + + uint256 internal constant L1_CHAIN_ID = 10; // it cannot be 9, the default block.chainid + uint256 internal ERA_CHAIN_ID = 270; + uint256 internal mintChainId = 300; + address internal l1AssetRouter = makeAddr("l1AssetRouter"); + address internal aliasedL1AssetRouter = AddressAliasHelper.applyL1ToL2Alias(l1AssetRouter); + + address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); + address internal l1CTM = makeAddr("l1CTM"); + bytes32 internal ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, l1CTMDeployer, bytes32(uint256(uint160(l1CTM))))); + + bytes32 internal baseTokenAssetId = + keccak256(abi.encode(L1_CHAIN_ID, L2_NATIVE_TOKEN_VAULT_ADDR, abi.encode(ETH_TOKEN_ADDRESS))); + + bytes internal exampleChainCommitment; + + IChainTypeManager internal chainTypeManager; + + // L2ContractDeployer internal deployScript; + + function setUp() public { + aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); + + standardErc20Impl = new BridgedStandardERC20(); + beacon = new UpgradeableBeacon(address(standardErc20Impl)); + beacon.transferOwnership(ownerWallet); + + // One of the purposes of deploying it here is to publish its bytecode + BeaconProxy beaconProxy = new BeaconProxy(address(beacon), new bytes(0)); + proxy = beaconProxy; + bytes32 beaconProxyBytecodeHash; + assembly { + beaconProxyBytecodeHash := extcodehash(beaconProxy) + } + + address l2SharedBridge = makeAddr("l2SharedBridge"); + + initSystemContracts( + SystemContractsArgs({ + l1ChainId: L1_CHAIN_ID, + eraChainId: ERA_CHAIN_ID, + l1AssetRouter: l1AssetRouter, + legacySharedBridge: l2SharedBridge, + l2TokenBeacon: address(beacon), + l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, + aliasedOwner: ownerWallet, + contractsDeployedAlready: false, + l1CtmDeployer: l1CTMDeployer + }) + ); + deployL2Contracts(L1_CHAIN_ID); + + vm.prank(aliasedL1AssetRouter); + l2AssetRouter.setAssetHandlerAddress(L1_CHAIN_ID, ctmAssetId, L2_BRIDGEHUB_ADDR); + vm.prank(ownerWallet); + l2Bridgehub.addChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); + vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1CTMDeployer)); + l2Bridgehub.setAssetHandlerAddress( + bytes32(uint256(uint160(l1CTM))), + address(addresses.stateTransition.chainTypeManagerProxy) + ); + chainTypeManager = IChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); + getExampleChainCommitment(); + } + + function test_gatewayShouldFinalizeDeposit() public { + finalizeDeposit(); + } + + function getExampleChainCommitment() internal returns (bytes memory) { + vm.mockCall( + L2_ASSET_ROUTER_ADDR, + abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), + abi.encode(L2_ASSET_ROUTER_ADDR) + ); + vm.mockCall( + L2_ASSET_ROUTER_ADDR, + abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), + abi.encode(address(0)) + ); + vm.prank(L2_BRIDGEHUB_ADDR); + address chainAddress = chainTypeManager.createNewChain( + ERA_CHAIN_ID + 1, + baseTokenAssetId, + L2_ASSET_ROUTER_ADDR, + address(0x1), + abi.encode(config.contracts.diamondCutData, generatedData.forceDeploymentsData), + new bytes[](0) + ); + exampleChainCommitment = abi.encode(IZKChain(chainAddress).prepareChainCommitment()); + } + + function test_forwardToL3OnGateway() public { + // todo fix this test + finalizeDeposit(); + IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); + vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); + bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); + } + + function finalizeDeposit() public { + bytes memory chainData = exampleChainCommitment; + bytes memory ctmData = abi.encode( + baseTokenAssetId, + msg.sender, + chainTypeManager.protocolVersion(), + config.contracts.diamondCutData + ); + BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + chainId: mintChainId, + baseTokenAssetId: baseTokenAssetId, + ctmData: ctmData, + chainData: chainData + }); + vm.prank(aliasedL1AssetRouter); + l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + } + + function initSystemContracts(SystemContractsArgs memory _args) internal virtual; + function deployL2Contracts(uint256 _l1ChainId) public virtual; +} diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol b/l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol new file mode 100644 index 000000000..edc483b70 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {StdStorage, stdStorage, stdToml} from "forge-std/Test.sol"; +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +import {L2_MESSAGE_ROOT_ADDR, L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; + +struct SystemContractsArgs { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + address legacySharedBridge; + address l2TokenBeacon; + bytes32 l2TokenProxyBytecodeHash; + address aliasedOwner; + bool contractsDeployedAlready; + address l1CtmDeployer; +} + +contract L2ContractDummyDeployer is DeployUtils { + using stdToml for string; + using stdStorage for StdStorage; + + function initSystemContracts(SystemContractsArgs memory _args) internal virtual { + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(_args.l1ChainId, ETH_TOKEN_ADDRESS); + address wethToken = address(0x1); + + address messageRoot = address(new MessageRoot(IBridgehub(L2_BRIDGEHUB_ADDR))); + address bridgehub = address(new Bridgehub(_args.l1ChainId, _args.aliasedOwner, 100)); + address assetRouter = address( + new L2AssetRouter( + _args.l1ChainId, + _args.eraChainId, + _args.l1AssetRouter, + _args.legacySharedBridge, + baseTokenAssetId, + _args.aliasedOwner + ) + ); + address ntv = address( + new L2NativeTokenVault( + _args.l1ChainId, + _args.aliasedOwner, + _args.l2TokenProxyBytecodeHash, + _args.legacySharedBridge, + _args.l2TokenBeacon, + _args.contractsDeployedAlready, + wethToken, + baseTokenAssetId + ) + ); + + vm.etch(L2_MESSAGE_ROOT_ADDR, messageRoot.code); + MessageRoot(L2_MESSAGE_ROOT_ADDR).initialize(); + + vm.etch(L2_BRIDGEHUB_ADDR, bridgehub.code); + Bridgehub(L2_BRIDGEHUB_ADDR).initialize(_args.aliasedOwner); + vm.prank(_args.aliasedOwner); + Bridgehub(L2_BRIDGEHUB_ADDR).setAddresses( + L2_ASSET_ROUTER_ADDR, + ICTMDeploymentTracker(_args.l1CtmDeployer), + IMessageRoot(L2_MESSAGE_ROOT_ADDR) + ); + + vm.etch(L2_ASSET_ROUTER_ADDR, assetRouter.code); + stdstore.target(address(L2_ASSET_ROUTER_ADDR)).sig("l1AssetRouter()").checked_write(_args.l1AssetRouter); + + stdstore + .target(L2_ASSET_ROUTER_ADDR) + .sig("assetHandlerAddress(bytes32)") + .with_key(baseTokenAssetId) + .checked_write(bytes32(uint256(uint160(L2_NATIVE_TOKEN_VAULT_ADDR)))); + + vm.etch(L2_NATIVE_TOKEN_VAULT_ADDR, ntv.code); + + vm.store(L2_NATIVE_TOKEN_VAULT_ADDR, bytes32(uint256(251)), bytes32(uint256(_args.l2TokenProxyBytecodeHash))); + } + + function deployL2Contracts(uint256 _l1ChainId) public virtual { + string memory root = vm.projectRoot(); + string memory inputPath = string.concat( + root, + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml" + ); + initializeConfig(inputPath); + addresses.transparentProxyAdmin = address(0x1); + addresses.bridgehub.bridgehubProxy = L2_BRIDGEHUB_ADDR; + addresses.bridges.sharedBridgeProxy = L2_ASSET_ROUTER_ADDR; + addresses.vaults.l1NativeTokenVaultProxy = L2_NATIVE_TOKEN_VAULT_ADDR; + addresses.blobVersionedHashRetriever = address(0x1); + config.l1ChainId = _l1ChainId; + console.log("Deploying L2 contracts"); + instantiateCreate2Factory(); + deployGenesisUpgrade(); + deployVerifier(); + deployValidatorTimelock(); + deployChainTypeManagerContract(); + } + + // add this to be excluded from coverage report + function test() internal virtual override {} +} diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index 595876a91..0356a233b 100644 --- a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -7,12 +7,12 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import "forge-std/console.sol"; -import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +// import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; -import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; +// import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; @@ -21,6 +21,8 @@ import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2Utils} from "../unit/utils/L2Utils.sol"; @@ -28,116 +30,31 @@ import {SystemContractsArgs} from "../unit/utils/L2Utils.sol"; import {L2ContractDeployer} from "./_SharedL2ContractDeployer.t.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; -contract L2GatewayTests is - Test, - L2ContractDeployer // is L1ContractDeployer, ZKChainDeployer, TokenDeployer { -{ +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {L2GatewayDummyTestsAbstract} from "../../l1/integration/L2GatewayDummyTestsAbstract.t.sol"; +import {L2ContractDummyDeployer} from "../../l1/integration/_SharedL2ContractDummyDeployer.sol"; + +contract L2GatewayTests is Test, L2GatewayDummyTestsAbstract, L2ContractDeployer { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. - address internal l1BridgeWallet = address(1); - address internal aliasedL1BridgeWallet; - - // The owner of the beacon and the native token vault - address internal ownerWallet = address(2); - - BridgedStandardERC20 internal standardErc20Impl; - - UpgradeableBeacon internal beacon; - BeaconProxy internal proxy; - - IL2AssetRouter l2AssetRouter = IL2AssetRouter(L2_ASSET_ROUTER_ADDR); - IBridgehub l2Bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); - - uint256 internal constant L1_CHAIN_ID = 9; - uint256 internal ERA_CHAIN_ID = 270; - address internal l1AssetRouter = makeAddr("l1AssetRouter"); - address internal aliasedL1AssetRouter = AddressAliasHelper.applyL1ToL2Alias(l1AssetRouter); - - address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); - address internal l1CTM = makeAddr("l1CTM"); - bytes32 internal ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, l1CTMDeployer, abi.encode(l1CTM))); - - bytes32 internal baseTokenAssetId = - keccak256(abi.encode(L1_CHAIN_ID, L2_NATIVE_TOKEN_VAULT_ADDR, abi.encode(ETH_TOKEN_ADDRESS))); - - bytes internal exampleChainCommitment; - - IChainTypeManager internal chainTypeManager; - - // L2ContractDeployer internal deployScript; - - function setUp() public { - aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); - - standardErc20Impl = new BridgedStandardERC20(); - beacon = new UpgradeableBeacon(address(standardErc20Impl)); - beacon.transferOwnership(ownerWallet); - - // One of the purposes of deploying it here is to publish its bytecode - BeaconProxy beaconProxy = new BeaconProxy(address(beacon), new bytes(0)); - proxy = beaconProxy; - bytes32 beaconProxyBytecodeHash; - assembly { - beaconProxyBytecodeHash := extcodehash(beaconProxy) - } - - address l2SharedBridge = makeAddr("l2SharedBridge"); - - L2Utils.initSystemContracts( - SystemContractsArgs({ - l1ChainId: L1_CHAIN_ID, - eraChainId: ERA_CHAIN_ID, - l1AssetRouter: l1AssetRouter, - legacySharedBridge: l2SharedBridge, - l2TokenBeacon: address(beacon), - l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, - aliasedOwner: ownerWallet, - contractsDeployedAlready: false, - l1CtmDeployer: l1CTMDeployer - }) - ); - deployL2Contracts(); - - vm.prank(aliasedL1AssetRouter); - l2AssetRouter.setAssetHandlerAddress(L1_CHAIN_ID, ctmAssetId, L2_BRIDGEHUB_ADDR); - vm.prank(ownerWallet); - l2Bridgehub.addChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1CTMDeployer)); - l2Bridgehub.setAssetHandlerAddress( - bytes32(uint256(uint160(l1CTM))), - address(addresses.stateTransition.chainTypeManagerProxy) - ); - chainTypeManager = IChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); - } + function test() internal virtual override(DeployUtils, L2ContractDeployer) {} - function test_gatewayShouldFinalizeDeposit() public { - finalizeDeposit(); + function initSystemContracts( + SystemContractsArgs memory _args + ) internal override(L2GatewayDummyTests, L2ContractDeployer) { + super.initSystemContracts(_args); } - function test_forwardToL3OnGateway() public { - // todo fix this test - // finalizeDeposit(); - // IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); - // vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); - // bridgehub.forwardTransactionOnGateway(ERA_CHAIN_ID, bytes32(0), 0); + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override(DeployUtils, L2ContractDeployer) returns (address) { + return super.deployViaCreate2(creationCode, constructorArgs); } - function finalizeDeposit() public { - // bytes memory chainData = abi.encode(exampleChainCommitment); - // bytes memory ctmData = abi.encode( - // baseTokenAssetId, - // msg.sender, - // chainTypeManager.protocolVersion(), - // config.contracts.diamondCutData - // ); - // BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ - // chainId: ERA_CHAIN_ID, - // baseTokenAssetId: baseTokenAssetId, - // ctmData: ctmData, - // chainData: chainData - // }); - // vm.prank(aliasedL1AssetRouter); - // l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + function deployL2Contracts(uint256 _l1ChainId) public override(L2GatewayDummyTests, L2ContractDummyDeployer) { + super.deployL2Contracts(_l1ChainId); } } diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol index 95411eba8..31d69a7d4 100644 --- a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol @@ -16,31 +16,26 @@ import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + import {L2Utils} from "../unit/utils/L2Utils.sol"; +import {L2ContractDummyDeployer, SystemContractsArgs} from "../../l1/integration/_SharedL2ContractDummyDeployer.sol"; -contract L2ContractDeployer is DeployUtils { +contract L2ContractDeployer is DeployUtils, L2ContractDummyDeployer { using stdToml for string; + function initSystemContracts(SystemContractsArgs memory _args) internal virtual override { + L2Utils.initSystemContracts(_args); + } + function deployViaCreate2( bytes memory creationCode, bytes memory constructorArgs - ) internal override returns (address) { + ) internal virtual override returns (address) { console.log("Deploying via create2 L2"); return L2Utils.deployViaCreat2L2(creationCode, constructorArgs, config.contracts.create2FactorySalt); } - function deployL2Contracts() public { - string memory root = vm.projectRoot(); - string memory inputPath = string.concat( - root, - "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml" - ); - initializeConfig(inputPath); - addresses.transparentProxyAdmin = address(0x1); - deployGenesisUpgrade(); - deployChainTypeManagerContract(); - } - // add this to be excluded from coverage report - function test() internal virtual override {} + function test() internal virtual override(DeployUtils, L2ContractDummyDeployer) {} } diff --git a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol index ee0daaef3..2586fb836 100644 --- a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol @@ -16,7 +16,8 @@ import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; -import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {Bridgehub, IBridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; @@ -25,18 +26,7 @@ import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {SystemContractsCaller} from "contracts/common/libraries/SystemContractsCaller.sol"; import {DeployFailed} from "contracts/common/L1ContractErrors.sol"; - -struct SystemContractsArgs { - uint256 l1ChainId; - uint256 eraChainId; - address l1AssetRouter; - address legacySharedBridge; - address l2TokenBeacon; - bytes32 l2TokenProxyBytecodeHash; - address aliasedOwner; - bool contractsDeployedAlready; - address l1CtmDeployer; -} +import {SystemContractsArgs} from "../../../l1/integration/_SharedL2ContractDummyDeployer.sol"; library L2Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); @@ -85,6 +75,7 @@ library L2Utils { } function forceDeploySystemContracts(SystemContractsArgs memory _args) internal { + forceDeployMessageRoot(); forceDeployBridgehub( _args.l1ChainId, _args.eraChainId, @@ -110,6 +101,11 @@ library L2Utils { ); } + function forceDeployMessageRoot() internal { + new MessageRoot(IBridgehub(L2_BRIDGEHUB_ADDR)); + forceDeployWithConstructor("MessageRoot", L2_MESSAGE_ROOT_ADDR, abi.encode(L2_BRIDGEHUB_ADDR)); + } + function forceDeployBridgehub( uint256 _l1ChainId, uint256 _eraChainId, From acc2e773f23ecb1744c4027407a993dc0196cf4b Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 12:31:36 +0100 Subject: [PATCH 15/42] renaming --- .../test/foundry/l1/integration/L2GatewayDummyTests.t.sol | 8 ++++---- ...myTestsAbstract.t.sol => L2GatewayTestsAbstract.t.sol} | 3 +-- .../test/foundry/l2/integration/L2GatewayTests.t.sol | 8 ++++---- 3 files changed, 9 insertions(+), 10 deletions(-) rename l1-contracts/test/foundry/l1/integration/{L2GatewayDummyTestsAbstract.t.sol => L2GatewayTestsAbstract.t.sol} (98%) diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol b/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol index a588bc841..198a28041 100644 --- a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol @@ -31,20 +31,20 @@ import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {L2GatewayDummyTests} from "./L2GatewayDummyTests.t.sol"; +import {L2GatewayTestsAbstract} from "./L2GatewayTestsAbstract.t.sol"; -contract L2GatewayDummyTests is Test, L2ContractDummyDeployer, L2GatewayDummyTests { +contract L2GatewayDummyTests is Test, L2ContractDummyDeployer, L2GatewayTestsAbstract { function test() internal virtual override(DeployUtils, L2ContractDummyDeployer) {} function initSystemContracts( SystemContractsArgs memory _args - ) internal virtual override(L2GatewayDummyTests, L2ContractDummyDeployer) { + ) internal virtual override(L2GatewayTestsAbstract, L2ContractDummyDeployer) { super.initSystemContracts(_args); } function deployL2Contracts( uint256 _l1ChainId - ) public virtual override(L2GatewayDummyTests, L2ContractDummyDeployer) { + ) public virtual override(L2GatewayTestsAbstract, L2ContractDummyDeployer) { super.deployL2Contracts(_l1ChainId); } } diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol similarity index 98% rename from l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol rename to l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol index 404822868..a7da4bae2 100644 --- a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTestsAbstract.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol @@ -32,10 +32,9 @@ import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -abstract contract L2GatewayDummyAbstract is +abstract contract L2GatewayTestsAbstract is Test, DeployUtils - // L2ContractDummyDeployer { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index 0356a233b..3111ca2da 100644 --- a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -33,17 +33,17 @@ import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.so import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {L2GatewayDummyTestsAbstract} from "../../l1/integration/L2GatewayDummyTestsAbstract.t.sol"; +import {L2GatewayTestsAbstract} from "../../l1/integration/L2GatewayTestsAbstract.t.sol"; import {L2ContractDummyDeployer} from "../../l1/integration/_SharedL2ContractDummyDeployer.sol"; -contract L2GatewayTests is Test, L2GatewayDummyTestsAbstract, L2ContractDeployer { +contract L2GatewayTests is Test, L2GatewayTestsAbstract, L2ContractDeployer { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. function test() internal virtual override(DeployUtils, L2ContractDeployer) {} function initSystemContracts( SystemContractsArgs memory _args - ) internal override(L2GatewayDummyTests, L2ContractDeployer) { + ) internal override(L2GatewayTestsAbstract, L2ContractDeployer) { super.initSystemContracts(_args); } @@ -54,7 +54,7 @@ contract L2GatewayTests is Test, L2GatewayDummyTestsAbstract, L2ContractDeployer return super.deployViaCreate2(creationCode, constructorArgs); } - function deployL2Contracts(uint256 _l1ChainId) public override(L2GatewayDummyTests, L2ContractDummyDeployer) { + function deployL2Contracts(uint256 _l1ChainId) public override(L2GatewayTestsAbstract, L2ContractDummyDeployer) { super.deployL2Contracts(_l1ChainId); } } From 21d11f3f1eeb714d52a303724d4cca578d5ba594 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 12:34:46 +0100 Subject: [PATCH 16/42] undo bh initialize change --- l1-contracts/contracts/bridgehub/Bridgehub.sol | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index e9bb2a305..cd22a9cc7 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -153,17 +153,14 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus // This is indeed true, since the only methods where this immutable is used are the ones with `onlyL1` modifier. // We will change this with interop. ETH_TOKEN_ASSET_ID = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, ETH_TOKEN_ADDRESS); - _initialize(_owner); + _transferOwnership(_owner); + whitelistedSettlementLayers[L1_CHAIN_ID] = true; } /// @notice used to initialize the contract /// @notice this contract is also deployed on L2 as a system contract there the owner and the related functions will not be used /// @param _owner the owner of the contract function initialize(address _owner) external reentrancyGuardInitializer { - _initialize(_owner); - } - - function _initialize(address _owner) internal { _transferOwnership(_owner); assetIdIsRegistered[ETH_TOKEN_ASSET_ID] = true; whitelistedSettlementLayers[L1_CHAIN_ID] = true; From 3d8b20284e5d3654f56753e854418dc3bc1334f7 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 12:37:11 +0100 Subject: [PATCH 17/42] fmt --- .../test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol index a7da4bae2..a3b56dc15 100644 --- a/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol @@ -32,10 +32,7 @@ import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -abstract contract L2GatewayTestsAbstract is - Test, - DeployUtils -{ +abstract contract L2GatewayTestsAbstract is Test, DeployUtils { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. address internal l1BridgeWallet = address(1); From 72c83b0b3832e66333524898a7e9ad1d3eccf464 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 20:55:52 +0100 Subject: [PATCH 18/42] getting tests to work --- .../bridge/ntv/L2NativeTokenVault.sol | 4 +- .../test/L2NativeTokenVaultDev.sol | 76 +++++++++++++ .../L2Erc20L1Test.t.sol} | 15 +-- .../l2-dummy-tests/L2Erc20TestAbstract.t.sol} | 83 +++----------- .../l2-dummy-tests/L2GatewayL1Test.t.sol | 51 +++++++++ .../L2GatewayTestAbstract.t.sol | 65 +++++++++++ .../l2-dummy-tests/L2WethTestAbstract.t.sol} | 45 ++++---- .../_SharedL2ContractDeployer.sol} | 107 ++++++++++++------ .../_SharedL2ContractL1DeployerUtils.sol} | 6 +- .../l2/integration/L2ERC20BridgeTest.t.sol | 49 ++++++++ .../l2/integration/L2GatewayTests.t.sol | 22 ++-- .../{unit/utils => integration}/L2Utils.sol | 44 +------ .../test/foundry/l2/integration/WETH.t.sol | 36 ++++++ ...l => _SharedL2ContractL2DeployerUtils.sol} | 8 +- .../ValidiumL2DAValidator.t.sol | 2 +- 15 files changed, 421 insertions(+), 192 deletions(-) create mode 100644 l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol rename l1-contracts/test/foundry/l1/integration/{L2GatewayDummyTests.t.sol => l2-dummy-tests/L2Erc20L1Test.t.sol} (72%) rename l1-contracts/test/foundry/{l2/unit/erc20/L2Erc20BridgeTest.t.sol => l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol} (55%) create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol rename l1-contracts/test/foundry/{l2/unit/weth/WETH.t.sol => l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol} (61%) rename l1-contracts/test/foundry/l1/integration/{L2GatewayTestsAbstract.t.sol => l2-dummy-tests/_SharedL2ContractDeployer.sol} (63%) rename l1-contracts/test/foundry/l1/integration/{_SharedL2ContractDummyDeployer.sol => l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol} (94%) create mode 100644 l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol rename l1-contracts/test/foundry/l2/{unit/utils => integration}/L2Utils.sol (85%) create mode 100644 l1-contracts/test/foundry/l2/integration/WETH.t.sol rename l1-contracts/test/foundry/l2/integration/{_SharedL2ContractDeployer.t.sol => _SharedL2ContractL2DeployerUtils.sol} (81%) diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index a6a0ff62d..4b83037a7 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -132,7 +132,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { /// for the code of the proxy. /// @param _salt The salt used for beacon proxy deployment of L2 bridged token. /// @return proxy The beacon proxy, i.e. L2 bridged token. - function _deployBeaconProxy(bytes32 _salt) internal override returns (BeaconProxy proxy) { + function _deployBeaconProxy(bytes32 _salt) internal virtual override returns (BeaconProxy proxy) { if (address(L2_LEGACY_SHARED_BRIDGE) == address(0)) { // Deploy the beacon proxy for the L2 token @@ -177,7 +177,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { function calculateCreate2TokenAddress( uint256 _originChainId, address _l1Token - ) public view override(INativeTokenVault, NativeTokenVault) returns (address) { + ) public view virtual override(INativeTokenVault, NativeTokenVault) returns (address) { bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); if (address(L2_LEGACY_SHARED_BRIDGE) != address(0)) { diff --git a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol new file mode 100644 index 000000000..0c26d8d14 --- /dev/null +++ b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; +import {Create2} from "@openzeppelin/contracts-v4/utils/Create2.sol"; +import {IBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/IBeacon.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; +import {NativeTokenVault} from "contracts/bridge/ntv/NativeTokenVault.sol"; +import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; + +contract L2NativeTokenVaultDev is L2NativeTokenVault { + constructor( + uint256 _l1ChainId, + address _aliasedOwner, + bytes32 _l2TokenProxyBytecodeHash, + address _legacySharedBridge, + address _bridgedTokenBeacon, + bool _contractsDeployedAlready, + address _wethToken, + bytes32 _baseTokenAssetId + ) + L2NativeTokenVault( + _l1ChainId, + _aliasedOwner, + _l2TokenProxyBytecodeHash, + _legacySharedBridge, + _bridgedTokenBeacon, + _contractsDeployedAlready, + _wethToken, + _baseTokenAssetId + ) + {} + + /// @notice copied from L1NTV for L1 compilation + function calculateCreate2TokenAddress( + uint256 _originChainId, + address _l1Token + ) public view override(L2NativeTokenVault) returns (address) { + bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); + return + Create2.computeAddress( + salt, + keccak256(abi.encodePacked(type(BeaconProxy).creationCode, abi.encode(bridgedTokenBeacon, ""))) + ); + } + + function deployBridgedStandardERC20(address _owner) external { + _transferOwnership(_owner); + + address l2StandardToken = address(new BridgedStandardERC20{salt: bytes32(0)}()); + + UpgradeableBeacon tokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); + + tokenBeacon.transferOwnership(owner()); + bridgedTokenBeacon = IBeacon(address(tokenBeacon)); + emit L2TokenBeaconUpdated(address(bridgedTokenBeacon), l2TokenProxyBytecodeHash); + } + + function test() external pure { + // test + } + + function _deployBeaconProxy(bytes32 _salt) internal virtual override returns (BeaconProxy proxy) { + // Use CREATE2 to deploy the BeaconProxy + address proxyAddress = Create2.deploy( + 0, + _salt, + abi.encodePacked(type(BeaconProxy).creationCode, abi.encode(bridgedTokenBeacon, "")) + ); + return BeaconProxy(payable(proxyAddress)); + } +} diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol similarity index 72% rename from l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol index 198a28041..b6346a09d 100644 --- a/l1-contracts/test/foundry/l1/integration/L2GatewayDummyTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol @@ -25,26 +25,27 @@ import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import {L2ContractDummyDeployer} from "./_SharedL2ContractDummyDeployer.sol"; +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; -import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {L2GatewayTestsAbstract} from "./L2GatewayTestsAbstract.t.sol"; +import {L2Erc20TestAbstract} from "./L2Erc20TestAbstract.t.sol"; +import {SharedL2ContractL1DeployerUtils} from "./_SharedL2ContractL1DeployerUtils.sol"; -contract L2GatewayDummyTests is Test, L2ContractDummyDeployer, L2GatewayTestsAbstract { - function test() internal virtual override(DeployUtils, L2ContractDummyDeployer) {} +contract L2Erc20L1Test is Test, SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer, L2Erc20TestAbstract { + function test() internal virtual override(DeployUtils, SharedL2ContractL1DeployerUtils) {} function initSystemContracts( SystemContractsArgs memory _args - ) internal virtual override(L2GatewayTestsAbstract, L2ContractDummyDeployer) { + ) internal virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { super.initSystemContracts(_args); } function deployL2Contracts( uint256 _l1ChainId - ) public virtual override(L2GatewayTestsAbstract, L2ContractDummyDeployer) { + ) public virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { super.deployL2Contracts(_l1ChainId); } } diff --git a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol similarity index 55% rename from l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol index d29d2b7ff..fcabf0d01 100644 --- a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol @@ -14,82 +14,33 @@ import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol" import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; -import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import {L2Utils, SystemContractsArgs} from "../utils/L2Utils.sol"; - -contract L2Erc20BridgeTest is Test { - // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. - // It is a bit easier to use EOA and it is sufficient for the tests. - address internal l1BridgeWallet = address(1); - address internal aliasedL1BridgeWallet; - - // The owner of the beacon and the native token vault - address internal ownerWallet = address(2); - - BridgedStandardERC20 internal standardErc20Impl; - - UpgradeableBeacon internal beacon; - BeaconProxy internal proxy; - - uint256 internal constant L1_CHAIN_ID = 9; - uint256 internal ERA_CHAIN_ID = 270; - - // We won't actually deploy an L1 token in these tests, but we need some address for it. - address internal L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; - - string internal constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; - string internal constant TOKEN_DEFAULT_SYMBOL = "TET"; - uint8 internal constant TOKEN_DEFAULT_DECIMALS = 18; - address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); - - function setUp() public { - aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); - - standardErc20Impl = new BridgedStandardERC20(); - beacon = new UpgradeableBeacon(address(standardErc20Impl)); - beacon.transferOwnership(ownerWallet); - - // One of the purposes of deploying it here is to publish its bytecode - BeaconProxy beaconProxy = new BeaconProxy(address(beacon), new bytes(0)); - proxy = beaconProxy; - bytes32 beaconProxyBytecodeHash; - assembly { - beaconProxyBytecodeHash := extcodehash(beaconProxy) - } - - address l2SharedBridge = L2Utils.deploySharedBridgeLegacy( - L1_CHAIN_ID, - ERA_CHAIN_ID, - ownerWallet, - l1BridgeWallet, - beaconProxyBytecodeHash - ); - L2Utils.initSystemContracts( - SystemContractsArgs({ - l1ChainId: L1_CHAIN_ID, - eraChainId: ERA_CHAIN_ID, - l1AssetRouter: l1BridgeWallet, - legacySharedBridge: l2SharedBridge, - l2TokenBeacon: address(beacon), - l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, - aliasedOwner: ownerWallet, - contractsDeployedAlready: false, - l1CtmDeployer: l1CTMDeployer - }) - ); - } +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +abstract contract L2Erc20TestAbstract is Test, SharedL2ContractDeployer { function performDeposit(address depositor, address receiver, uint256 amount) internal { - vm.prank(aliasedL1BridgeWallet); + vm.prank(aliasedL1AssetRouter); L2AssetRouter(L2_ASSET_ROUTER_ADDR).finalizeDeposit({ _l1Sender: depositor, _l2Receiver: receiver, _l1Token: L1_TOKEN_ADDRESS, _amount: amount, - _data: L2Utils.encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) + _data: encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) }); } diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol new file mode 100644 index 000000000..3e8b04e42 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {L2GatewayTestAbstract} from "./L2GatewayTestAbstract.t.sol"; +import {SharedL2ContractL1DeployerUtils} from "./_SharedL2ContractL1DeployerUtils.sol"; + +contract L2GatewayL1Test is Test, SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer, L2GatewayTestAbstract { + function test() internal virtual override(DeployUtils, SharedL2ContractL1DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol new file mode 100644 index 000000000..e2bf33d84 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +abstract contract L2GatewayTestAbstract is Test, SharedL2ContractDeployer { + function test_gatewayShouldFinalizeDeposit() public { + finalizeDeposit(); + } + + function test_forwardToL3OnGateway() public { + // todo fix this test + finalizeDeposit(); + IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); + vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); + bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); + } + + function finalizeDeposit() public { + bytes memory chainData = exampleChainCommitment; + bytes memory ctmData = abi.encode( + baseTokenAssetId, + msg.sender, + chainTypeManager.protocolVersion(), + config.contracts.diamondCutData + ); + BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + chainId: mintChainId, + baseTokenAssetId: baseTokenAssetId, + ctmData: ctmData, + chainData: chainData + }); + vm.prank(aliasedL1AssetRouter); + l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + } +} diff --git a/l1-contracts/test/foundry/l2/unit/weth/WETH.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol similarity index 61% rename from l1-contracts/test/foundry/l2/unit/weth/WETH.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol index 6cbc44fa7..d2a43be2e 100644 --- a/l1-contracts/test/foundry/l2/unit/weth/WETH.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol @@ -2,33 +2,38 @@ pragma solidity ^0.8.20; -import {Test} from "forge-std/Test.sol"; - -import {L2WrappedBaseToken} from "contracts/bridge/L2WrappedBaseToken.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import {Unauthorized, UnimplementedMessage, BridgeMintNotImplemented} from "contracts/common/L1ContractErrors.sol"; +// solhint-disable gas-custom-errors -contract WethTest is Test { - L2WrappedBaseToken internal weth; +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; - // The owner of the proxy - address internal ownerWallet = address(2); +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; - address internal l2BridgeAddress = address(3); - address internal l1Address = address(4); +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; - function setUp() public { - ownerWallet = makeAddr("owner"); - L2WrappedBaseToken impl = new L2WrappedBaseToken(); +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(impl), ownerWallet, ""); +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; - weth = L2WrappedBaseToken(payable(proxy)); +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; - weth.initializeV2("Wrapped Ether", "WETH", l2BridgeAddress, l1Address); - } +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {Unauthorized, UnimplementedMessage, BridgeMintNotImplemented} from "contracts/common/L1ContractErrors.sol"; +abstract contract L2WethTestAbstract is Test, SharedL2ContractDeployer { function test_shouldDepositWethByCallingDeposit() public { uint256 amount = 100; weth.deposit{value: amount}(); @@ -102,7 +107,7 @@ contract WethTest is Test { function test_revertWhenCallingBridgeMint() public { vm.expectRevert(abi.encodeWithSelector(BridgeMintNotImplemented.selector)); - vm.prank(l2BridgeAddress); + vm.prank(L2_ASSET_ROUTER_ADDR); weth.bridgeMint(address(1), 1); } diff --git a/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol similarity index 63% rename from l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol index a3b56dc15..aa7e5676a 100644 --- a/l1-contracts/test/foundry/l1/integration/L2GatewayTestsAbstract.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol @@ -7,6 +7,8 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import "forge-std/console.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; + import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; @@ -24,19 +26,20 @@ import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2WrappedBaseToken} from "contracts/bridge/L2WrappedBaseToken.sol"; +import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; -import {L2ContractDummyDeployer} from "./_SharedL2ContractDummyDeployer.sol"; +// import {L2ContractL1Deployer} from "./_SharedL2ContractL1Deployer.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; -import {SystemContractsArgs} from "./_SharedL2ContractDummyDeployer.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -abstract contract L2GatewayTestsAbstract is Test, DeployUtils { - // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. - // It is a bit easier to use EOA and it is sufficient for the tests. - address internal l1BridgeWallet = address(1); - address internal aliasedL1BridgeWallet; +abstract contract SharedL2ContractDeployer is Test, DeployUtils { + L2WrappedBaseToken internal weth; + address internal l1WethAddress = address(4); // The owner of the beacon and the native token vault address internal ownerWallet = address(2); @@ -55,6 +58,12 @@ abstract contract L2GatewayTestsAbstract is Test, DeployUtils { address internal l1AssetRouter = makeAddr("l1AssetRouter"); address internal aliasedL1AssetRouter = AddressAliasHelper.applyL1ToL2Alias(l1AssetRouter); + // We won't actually deploy an L1 token in these tests, but we need some address for it. + address internal L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; + + string internal constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; + string internal constant TOKEN_DEFAULT_SYMBOL = "TET"; + uint8 internal constant TOKEN_DEFAULT_DECIMALS = 18; address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); address internal l1CTM = makeAddr("l1CTM"); bytes32 internal ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, l1CTMDeployer, bytes32(uint256(uint160(l1CTM))))); @@ -66,11 +75,7 @@ abstract contract L2GatewayTestsAbstract is Test, DeployUtils { IChainTypeManager internal chainTypeManager; - // L2ContractDeployer internal deployScript; - function setUp() public { - aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); - standardErc20Impl = new BridgedStandardERC20(); beacon = new UpgradeableBeacon(address(standardErc20Impl)); beacon.transferOwnership(ownerWallet); @@ -83,7 +88,15 @@ abstract contract L2GatewayTestsAbstract is Test, DeployUtils { beaconProxyBytecodeHash := extcodehash(beaconProxy) } - address l2SharedBridge = makeAddr("l2SharedBridge"); + address l2SharedBridge = deployL2SharedBridgeLegacy( + L1_CHAIN_ID, + ERA_CHAIN_ID, + ownerWallet, + l1AssetRouter, + beaconProxyBytecodeHash + ); + + L2WrappedBaseToken weth = deployL2Weth(); initSystemContracts( SystemContractsArgs({ @@ -113,10 +126,6 @@ abstract contract L2GatewayTestsAbstract is Test, DeployUtils { getExampleChainCommitment(); } - function test_gatewayShouldFinalizeDeposit() public { - finalizeDeposit(); - } - function getExampleChainCommitment() internal returns (bytes memory) { vm.mockCall( L2_ASSET_ROUTER_ADDR, @@ -140,30 +149,54 @@ abstract contract L2GatewayTestsAbstract is Test, DeployUtils { exampleChainCommitment = abi.encode(IZKChain(chainAddress).prepareChainCommitment()); } - function test_forwardToL3OnGateway() public { - // todo fix this test - finalizeDeposit(); - IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); - vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); - bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); + /// @notice Encodes the token data. + /// @param name The name of the token. + /// @param symbol The symbol of the token. + /// @param decimals The decimals of the token. + function encodeTokenData( + string memory name, + string memory symbol, + uint8 decimals + ) internal pure returns (bytes memory) { + bytes memory encodedName = abi.encode(name); + bytes memory encodedSymbol = abi.encode(symbol); + bytes memory encodedDecimals = abi.encode(decimals); + + return abi.encode(encodedName, encodedSymbol, encodedDecimals); } - function finalizeDeposit() public { - bytes memory chainData = exampleChainCommitment; - bytes memory ctmData = abi.encode( - baseTokenAssetId, - msg.sender, - chainTypeManager.protocolVersion(), - config.contracts.diamondCutData + function deployL2SharedBridgeLegacy( + uint256 _l1ChainId, + uint256 _eraChainId, + address _aliasedOwner, + address _l1SharedBridge, + bytes32 _l2TokenProxyBytecodeHash + ) internal returns (address) { + bytes32 ethAssetId = DataEncoding.encodeNTVAssetId(_l1ChainId, ETH_TOKEN_ADDRESS); + + L2SharedBridgeLegacy bridge = new L2SharedBridgeLegacy(); + console.log("bridge", address(bridge)); + address proxyAdmin = address(0x1); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(bridge), + proxyAdmin, + abi.encodeWithSelector( + L2SharedBridgeLegacy.initialize.selector, + _l1SharedBridge, + _l2TokenProxyBytecodeHash, + _aliasedOwner + ) ); - BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ - chainId: mintChainId, - baseTokenAssetId: baseTokenAssetId, - ctmData: ctmData, - chainData: chainData - }); - vm.prank(aliasedL1AssetRouter); - l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + console.log("proxy", address(proxy)); + return address(proxy); + } + + function deployL2Weth() internal returns (L2WrappedBaseToken) { + L2WrappedBaseToken wethImpl = new L2WrappedBaseToken(); + TransparentUpgradeableProxy wethProxy = new TransparentUpgradeableProxy(address(wethImpl), ownerWallet, ""); + weth = L2WrappedBaseToken(payable(wethProxy)); + weth.initializeV2("Wrapped Ether", "WETH", L2_ASSET_ROUTER_ADDR, l1WethAddress); + return weth; } function initSystemContracts(SystemContractsArgs memory _args) internal virtual; diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol similarity index 94% rename from l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol index edc483b70..b48b7f0bc 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL2ContractDummyDeployer.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol @@ -22,6 +22,7 @@ import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {L2NativeTokenVaultDev} from "contracts/dev-contracts/test/L2NativeTokenVaultDev.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; @@ -38,7 +39,7 @@ struct SystemContractsArgs { address l1CtmDeployer; } -contract L2ContractDummyDeployer is DeployUtils { +contract SharedL2ContractL1DeployerUtils is DeployUtils { using stdToml for string; using stdStorage for StdStorage; @@ -59,7 +60,7 @@ contract L2ContractDummyDeployer is DeployUtils { ) ); address ntv = address( - new L2NativeTokenVault( + new L2NativeTokenVaultDev( _args.l1ChainId, _args.aliasedOwner, _args.l2TokenProxyBytecodeHash, @@ -95,6 +96,7 @@ contract L2ContractDummyDeployer is DeployUtils { vm.etch(L2_NATIVE_TOKEN_VAULT_ADDR, ntv.code); vm.store(L2_NATIVE_TOKEN_VAULT_ADDR, bytes32(uint256(251)), bytes32(uint256(_args.l2TokenProxyBytecodeHash))); + L2NativeTokenVaultDev(L2_NATIVE_TOKEN_VAULT_ADDR).deployBridgedStandardERC20(_args.aliasedOwner); } function deployL2Contracts(uint256 _l1ChainId) public virtual { diff --git a/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol b/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol new file mode 100644 index 000000000..9b308fd9e --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {L2Utils, SystemContractsArgs} from "./L2Utils.sol"; +import {SharedL2ContractL2DeployerUtils} from "./_SharedL2ContractL2DeployerUtils.sol"; +import {L2Erc20TestAbstract} from "../../l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; + +contract L2Erc20Test is Test, L2Erc20TestAbstract, SharedL2ContractL2DeployerUtils { + function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal override(SharedL2ContractDeployer, SharedL2ContractL2DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override(DeployUtils, SharedL2ContractL2DeployerUtils) returns (address) { + return super.deployViaCreate2(creationCode, constructorArgs); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public override(SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index 3111ca2da..f9518d213 100644 --- a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -25,36 +25,38 @@ import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import {L2Utils} from "../unit/utils/L2Utils.sol"; -import {SystemContractsArgs} from "../unit/utils/L2Utils.sol"; +import {L2Utils, SystemContractsArgs} from "./L2Utils.sol"; -import {L2ContractDeployer} from "./_SharedL2ContractDeployer.t.sol"; +import {SharedL2ContractL2DeployerUtils} from "./_SharedL2ContractL2DeployerUtils.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {L2GatewayTestsAbstract} from "../../l1/integration/L2GatewayTestsAbstract.t.sol"; -import {L2ContractDummyDeployer} from "../../l1/integration/_SharedL2ContractDummyDeployer.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {L2GatewayTestAbstract} from "../../l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; -contract L2GatewayTests is Test, L2GatewayTestsAbstract, L2ContractDeployer { +contract L2GatewayTests is Test, L2GatewayTestAbstract, SharedL2ContractL2DeployerUtils { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. // It is a bit easier to use EOA and it is sufficient for the tests. - function test() internal virtual override(DeployUtils, L2ContractDeployer) {} + function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} function initSystemContracts( SystemContractsArgs memory _args - ) internal override(L2GatewayTestsAbstract, L2ContractDeployer) { + ) internal override(SharedL2ContractDeployer, SharedL2ContractL2DeployerUtils) { super.initSystemContracts(_args); } function deployViaCreate2( bytes memory creationCode, bytes memory constructorArgs - ) internal override(DeployUtils, L2ContractDeployer) returns (address) { + ) internal override(DeployUtils, SharedL2ContractL2DeployerUtils) returns (address) { return super.deployViaCreate2(creationCode, constructorArgs); } - function deployL2Contracts(uint256 _l1ChainId) public override(L2GatewayTestsAbstract, L2ContractDummyDeployer) { + function deployL2Contracts( + uint256 _l1ChainId + ) public override(SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer) { super.deployL2Contracts(_l1ChainId); } } diff --git a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol b/l1-contracts/test/foundry/l2/integration/L2Utils.sol similarity index 85% rename from l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol rename to l1-contracts/test/foundry/l2/integration/L2Utils.sol index 2586fb836..9a0afb563 100644 --- a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/integration/L2Utils.sol @@ -26,7 +26,7 @@ import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {SystemContractsCaller} from "contracts/common/libraries/SystemContractsCaller.sol"; import {DeployFailed} from "contracts/common/L1ContractErrors.sol"; -import {SystemContractsArgs} from "../../../l1/integration/_SharedL2ContractDummyDeployer.sol"; +import {SystemContractsArgs} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; library L2Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); @@ -216,48 +216,6 @@ library L2Utils { IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); } - function deploySharedBridgeLegacy( - uint256 _l1ChainId, - uint256 _eraChainId, - address _aliasedOwner, - address _l1SharedBridge, - bytes32 _l2TokenProxyBytecodeHash - ) internal returns (address) { - bytes32 ethAssetId = DataEncoding.encodeNTVAssetId(_l1ChainId, ETH_TOKEN_ADDRESS); - - L2SharedBridgeLegacy bridge = new L2SharedBridgeLegacy(); - console.log("bridge", address(bridge)); - address proxyAdmin = address(0x1); - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(bridge), - proxyAdmin, - abi.encodeWithSelector( - L2SharedBridgeLegacy.initialize.selector, - _l1SharedBridge, - _l2TokenProxyBytecodeHash, - _aliasedOwner - ) - ); - console.log("proxy", address(proxy)); - return address(proxy); - } - - /// @notice Encodes the token data. - /// @param name The name of the token. - /// @param symbol The symbol of the token. - /// @param decimals The decimals of the token. - function encodeTokenData( - string memory name, - string memory symbol, - uint8 decimals - ) internal pure returns (bytes memory) { - bytes memory encodedName = abi.encode(name); - bytes memory encodedSymbol = abi.encode(symbol); - bytes memory encodedDecimals = abi.encode(decimals); - - return abi.encode(encodedName, encodedSymbol, encodedDecimals); - } - function deployViaCreat2L2( bytes memory creationCode, bytes memory constructorargs, diff --git a/l1-contracts/test/foundry/l2/integration/WETH.t.sol b/l1-contracts/test/foundry/l2/integration/WETH.t.sol new file mode 100644 index 000000000..5a0b8e246 --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/WETH.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {L2WethTestAbstract} from "../../l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol"; + +import {SharedL2ContractL2DeployerUtils, SystemContractsArgs} from "./_SharedL2ContractL2DeployerUtils.sol"; + +contract WethTest is Test, L2WethTestAbstract, SharedL2ContractL2DeployerUtils { + function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal override(SharedL2ContractDeployer, SharedL2ContractL2DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override(DeployUtils, SharedL2ContractL2DeployerUtils) returns (address) { + return super.deployViaCreate2(creationCode, constructorArgs); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public override(SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol similarity index 81% rename from l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol rename to l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol index 31d69a7d4..61f51d0cd 100644 --- a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol @@ -18,10 +18,10 @@ import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; -import {L2Utils} from "../unit/utils/L2Utils.sol"; -import {L2ContractDummyDeployer, SystemContractsArgs} from "../../l1/integration/_SharedL2ContractDummyDeployer.sol"; +import {L2Utils} from "./L2Utils.sol"; +import {SharedL2ContractL1DeployerUtils, SystemContractsArgs} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; -contract L2ContractDeployer is DeployUtils, L2ContractDummyDeployer { +contract SharedL2ContractL2DeployerUtils is DeployUtils, SharedL2ContractL1DeployerUtils { using stdToml for string; function initSystemContracts(SystemContractsArgs memory _args) internal virtual override { @@ -37,5 +37,5 @@ contract L2ContractDeployer is DeployUtils, L2ContractDummyDeployer { } // add this to be excluded from coverage report - function test() internal virtual override(DeployUtils, L2ContractDummyDeployer) {} + function test() internal virtual override(DeployUtils, SharedL2ContractL1DeployerUtils) {} } diff --git a/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol b/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol index c54367295..3374e1acc 100644 --- a/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol +++ b/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol @@ -8,7 +8,7 @@ import {Test} from "forge-std/Test.sol"; import {ValidiumL2DAValidator} from "contracts/data-availability/ValidiumL2DAValidator.sol"; -contract L2Erc20BridgeTest is Test { +contract L2ValidiumDAValidatorTest is Test { function test_callValidiumDAValidator(address depositor, address receiver, uint256 amount) internal { ValidiumL2DAValidator validator = new ValidiumL2DAValidator(); From 8af590e5d8916d01020ca2bc0edf72f987afdcb4 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 20:56:39 +0100 Subject: [PATCH 19/42] fmt --- .../contracts/dev-contracts/test/L2NativeTokenVaultDev.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol index 0c26d8d14..7d4c01577 100644 --- a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol +++ b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol @@ -34,7 +34,7 @@ contract L2NativeTokenVaultDev is L2NativeTokenVault { _baseTokenAssetId ) {} - + /// @notice copied from L1NTV for L1 compilation function calculateCreate2TokenAddress( uint256 _originChainId, From 4c40c898fc44bc2541499d71abcb9a3aa5f38ed8 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 21:44:51 +0100 Subject: [PATCH 20/42] chain withdrawal test --- .../contracts/bridgehub/Bridgehub.sol | 16 +++++++--- .../contracts/bridgehub/IBridgehub.sol | 2 +- l1-contracts/contracts/common/Config.sol | 5 ++++ l1-contracts/deploy-scripts/DeployL1.s.sol | 2 +- .../deploy-scripts/GatewayPreparation.s.sol | 2 +- .../L2GatewayTestAbstract.t.sol | 30 +++++++++++++++---- 6 files changed, 45 insertions(+), 12 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index cd22a9cc7..283de8998 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.24; // solhint-disable reason-string, gas-custom-errors +import {console} from "forge-std/console.sol"; + import {EnumerableMap} from "@openzeppelin/contracts-v4/utils/structs/EnumerableMap.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; @@ -18,7 +20,7 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; import {IZKChain} from "../state-transition/chain-interfaces/IZKChain.sol"; -import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "../common/Config.sol"; +import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER, L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS} from "../common/Config.sol"; import {BridgehubL2TransactionRequest, L2Message, L2Log, TxStatus} from "../common/Messaging.sol"; import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IMessageRoot} from "./IMessageRoot.sol"; @@ -86,6 +88,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev asset info used to identify chains in the Shared Bridge mapping(bytes32 ctmAssetId => address ctmAddress) public ctmAssetIdToAddress; + /// @dev ctmAddress to ctmAssetId + mapping(address ctmAddress => bytes32 ctmAssetId) public ctmAssetIdFromAddress; + /// @dev used to indicate the currently active settlement layer for a given chainId mapping(uint256 chainId => uint256 activeSettlementLayerChainId) public settlementLayer; @@ -320,6 +325,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 assetInfo = keccak256(abi.encode(L1_CHAIN_ID, sender, _additionalData)); ctmAssetIdToAddress[assetInfo] = _assetAddress; + ctmAssetIdFromAddress[_assetAddress] = assetInfo; emit AssetRegistered(assetInfo, _assetAddress, _additionalData, msg.sender); } @@ -422,10 +428,10 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus if (ctmAddress == address(0)) { revert ChainIdNotRegistered(_chainId); } - return ctmAssetId(chainTypeManager[_chainId]); + return ctmAssetIdFromAddress[chainTypeManager[_chainId]]; } - function ctmAssetId(address _ctmAddress) public view override returns (bytes32) { + function calculateCtmAssetId(address _ctmAddress) internal view returns (bytes32) { return keccak256(abi.encode(L1_CHAIN_ID, address(l1CtmDeployer), bytes32(uint256(uint160(_ctmAddress))))); } @@ -700,7 +706,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bridgehubData.ctmData ); bytes memory chainMintData = IZKChain(zkChain).forwardedBridgeBurn( - zkChainMap.get(_settlementChainId), + _settlementChainId == L1_CHAIN_ID + ? L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS + : zkChainMap.get(_settlementChainId), _originalCaller, bridgehubData.chainData ); diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index 5e2be03fc..1eb9723ae 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -217,7 +217,7 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { function ctmAssetIdFromChainId(uint256 _chainId) external view returns (bytes32); - function ctmAssetId(address _ctmAddress) external view returns (bytes32); + function ctmAssetIdFromAddress(address _ctmAddress) external view returns (bytes32); function l1CtmDeployer() external view returns (ICTMDeploymentTracker); diff --git a/l1-contracts/contracts/common/Config.sol b/l1-contracts/contracts/common/Config.sol index beebcd00c..a1e58f464 100644 --- a/l1-contracts/contracts/common/Config.sol +++ b/l1-contracts/contracts/common/Config.sol @@ -120,6 +120,11 @@ address constant SETTLEMENT_LAYER_RELAY_SENDER = address(uint160(0x1111111111111 /// @dev The metadata version that is supported by the ZK Chains to prove that an L2->L1 log was included in a batch. uint256 constant SUPPORTED_PROOF_METADATA_VERSION = 1; +/// @dev The virtual address of the L1 settlement layer. +address constant L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS = address( + uint160(uint256(keccak256("L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS")) - 1) +); + struct PriorityTreeCommitment { uint256 nextLeafIndex; uint256 startIndex; diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 3f49947ff..5d12ba4c4 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -237,7 +237,7 @@ contract DeployL1Script is Script, DeployUtils { vm.stopBroadcast(); console.log("CTM registered in CTMDeploymentTracker"); - bytes32 assetId = bridgehub.ctmAssetId(addresses.stateTransition.chainTypeManagerProxy); + bytes32 assetId = bridgehub.ctmAssetIdFromAddress(addresses.stateTransition.chainTypeManagerProxy); // console.log(address(bridgehub.ctmDeployer()), addresses.bridgehub.ctmDeploymentTrackerProxy); // console.log(address(bridgehub.ctmDeployer().BRIDGE_HUB()), addresses.bridgehub.bridgehubProxy); console.log( diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index a3568f10c..808db90a4 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -188,7 +188,7 @@ contract GatewayPreparation is Script { function governanceSetCTMAssetHandler(bytes32 governanoceOperationSalt) public { initializeConfig(); - bytes32 assetId = IBridgehub(config.bridgehub).ctmAssetId(config.chainTypeManagerProxy); + bytes32 assetId = IBridgehub(config.bridgehub).ctmAssetIdFromAddress(config.chainTypeManagerProxy); // This should be equivalent to `config.chainTypeManagerProxy`, but we just double checking to ensure that // bridgehub was initialized correctly diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol index e2bf33d84..e97c2017b 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol @@ -14,11 +14,11 @@ import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol" import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; -import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR, L2_MESSENGER} from "contracts/common/L2ContractAddresses.sol"; import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; -import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {BridgehubMintCTMAssetData, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; @@ -35,21 +35,41 @@ import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; abstract contract L2GatewayTestAbstract is Test, SharedL2ContractDeployer { function test_gatewayShouldFinalizeDeposit() public { finalizeDeposit(); + require(l2Bridgehub.ctmAssetIdFromAddress(address(chainTypeManager)) == ctmAssetId, "ctmAssetId mismatch"); + require(l2Bridgehub.ctmAssetIdFromChainId(mintChainId) == ctmAssetId, "ctmAssetIdFromChainId mismatch"); } function test_forwardToL3OnGateway() public { // todo fix this test finalizeDeposit(); - IBridgehub bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); - bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); + l2Bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); + } + + function test_withdrawFromGateway() public { + // todo fix this test + finalizeDeposit(); + address newAdmin = address(0x1); + bytes memory newDiamondCut = abi.encode(); + BridgehubBurnCTMAssetData memory data = BridgehubBurnCTMAssetData({ + chainId: mintChainId, + ctmData: abi.encode(newAdmin, config.contracts.diamondCutData), + chainData: abi.encode(chainTypeManager.protocolVersion()) + }); + vm.prank(ownerWallet); + vm.mockCall( + address(L2_MESSENGER), + abi.encodeWithSelector(L2_MESSENGER.sendToL1.selector), + abi.encode(bytes("")) + ); + l2AssetRouter.withdraw(ctmAssetId, abi.encode(data)); } function finalizeDeposit() public { bytes memory chainData = exampleChainCommitment; bytes memory ctmData = abi.encode( baseTokenAssetId, - msg.sender, + ownerWallet, chainTypeManager.protocolVersion(), config.contracts.diamondCutData ); From 8ce690545a7da2928c193b30340fea5d901db10a Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 22:13:47 +0100 Subject: [PATCH 21/42] some bridge fixes --- .../contracts/bridge/L2SharedBridgeLegacy.sol | 4 +- .../bridge/asset-router/IL2AssetRouter.sol | 2 +- .../bridge/asset-router/L2AssetRouter.sol | 10 ++-- .../interfaces/IL2SharedBridgeLegacy.sol | 2 +- .../contracts/bridgehub/Bridgehub.sol | 2 - .../deploy-scripts/GatewayPreparation.s.sol | 58 ++++++++++--------- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol index 9ea29cf07..db46fef12 100644 --- a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol @@ -148,8 +148,8 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { proxy = abi.decode(returndata, (address)); } - function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter { + function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter returns (bytes32) { // slither-disable-next-line unused-return - L2ContractHelper.sendMessageToL1(_message); + return L2ContractHelper.sendMessageToL1(_message); } } diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 7cb2bd1ce..e1b6c5959 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -14,7 +14,7 @@ interface IL2AssetRouter is IAssetRouterBase { bytes assetData ); - function withdraw(bytes32 _assetId, bytes calldata _transferData) external; + function withdraw(bytes32 _assetId, bytes calldata _transferData) external returns (bytes32); function l1AssetRouter() external view returns (address); diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 187039464..7a7b72651 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -151,11 +151,11 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @dev do not rely on this function, it will be deprecated in the future /// @param _assetId The asset id of the withdrawn asset /// @param _assetData The data that is passed to the asset handler contract - function withdraw(bytes32 _assetId, bytes memory _assetData) public override { + function withdraw(bytes32 _assetId, bytes memory _assetData) public override returns (bytes32) { _withdrawSender(_assetId, _assetData, msg.sender, true); } - function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { + function withdrawToken(address _l2NativeToken, bytes memory _assetData) public returns (bytes32) { bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); _withdrawSender(assetId, _assetData, msg.sender, true); } @@ -171,7 +171,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { bytes memory _assetData, address _sender, bool _alwaysNewMessageFormat - ) internal { + ) internal returns (bytes32 txHash) { address assetHandler = assetHandlerAddress[_assetId]; bytes memory _l1bridgeMintData = IAssetHandler(assetHandler).bridgeBurn({ _chainId: L1_CHAIN_ID, @@ -185,7 +185,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { if (_alwaysNewMessageFormat || L2_LEGACY_SHARED_BRIDGE == address(0)) { message = _getAssetRouterWithdrawMessage(_assetId, _l1bridgeMintData); // slither-disable-next-line unused-return - L2ContractHelper.sendMessageToL1(message); + txHash = L2ContractHelper.sendMessageToL1(message); } else { address l1Token = IBridgedStandardToken( IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).tokenAddress(_assetId) @@ -195,7 +195,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { } (uint256 amount, address l1Receiver) = abi.decode(_assetData, (uint256, address)); message = _getSharedBridgeWithdrawMessage(l1Receiver, l1Token, amount); - IL2SharedBridgeLegacy(L2_LEGACY_SHARED_BRIDGE).sendMessageToL1(message); + txHash = IL2SharedBridgeLegacy(L2_LEGACY_SHARED_BRIDGE).sendMessageToL1(message); } emit WithdrawalInitiatedAssetRouter(L1_CHAIN_ID, _sender, _assetId, _assetData); diff --git a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol index 00a762447..a3f903139 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol @@ -17,5 +17,5 @@ interface IL2SharedBridgeLegacy { function deployBeaconProxy(bytes32 _salt) external returns (address); - function sendMessageToL1(bytes calldata _message) external; + function sendMessageToL1(bytes calldata _message) external returns (bytes32); } diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 283de8998..c753533ba 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -4,8 +4,6 @@ pragma solidity 0.8.24; // solhint-disable reason-string, gas-custom-errors -import {console} from "forge-std/console.sol"; - import {EnumerableMap} from "@openzeppelin/contracts-v4/utils/structs/EnumerableMap.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 808db90a4..d3e1827fd 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -45,6 +45,8 @@ struct Config { address gatewayChainProxyAdmin; address l1NullifierProxy; bytes gatewayDiamondCutData; + bytes l1DiamondCutData; + uint256 l1ChainId; } /// @notice Scripts that is responsible for preparing the chain to become a gateway @@ -97,10 +99,12 @@ contract GatewayPreparation is Script { gatewayChainId: toml.readUint("$.chain_chain_id"), governance: toml.readAddress("$.governance"), gatewayDiamondCutData: toml.readBytes("$.gateway_diamond_cut_data"), + l1DiamondCutData: toml.readBytes("$.l1_diamond_cut_data"), gatewayChainAdmin: toml.readAddress("$.chain_admin"), gatewayAccessControlRestriction: toml.readAddress("$.access_control_restriction"), gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin"), - l1NullifierProxy: toml.readAddress("$.l1_nullifier_proxy") + l1NullifierProxy: toml.readAddress("$.l1_nullifier_proxy"), + l1ChainId: toml.readUint("$.l1_chain_id") }); } @@ -294,33 +298,31 @@ contract GatewayPreparation is Script { bytes32 chainAssetId = IBridgehub(config.bridgehub).ctmAssetIdFromChainId(chainId); uint256 currentSettlementLayer = IBridgehub(config.bridgehub).settlementLayer(chainId); - // if (currentSettlementLayer == config.l1ChainId) { - // console.log("Chain already using L1 as its settlement layer"); - // saveOutput(bytes32(0)); - // return; - // } - - // if (currentSettlementLayer == address(0)) { - // console.log("Chain has never used Gateway as its settlement layer"); - // saveOutput(bytes32(0)); - // return; - // } - - // bytes memory bridgehubData = abi.encode( - // BridgehubBurnCTMAssetData({ - // chainId: chainId, - // ctmData: abi.encode(chainAdmin, config.diamondCutData), - // chainData: abi.encode(IZKChain(IBridgehub(config.bridgehub).getZKChain(chainId)).getProtocolVersion()) - // }) - // ); - - // L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); - // l2AssetRouter.withdraw( - // ctmAssetId, - // bridgehubBurnData - // ); - - // saveOutput(l2TxHash); + if (currentSettlementLayer == config.l1ChainId) { + console.log("Chain already using L1 as its settlement layer"); + saveOutput(bytes32(0)); + return; + } + + if (currentSettlementLayer == 0) { + console.log("Chain has never used Gateway as its settlement layer"); + saveOutput(bytes32(0)); + return; + } + + bytes memory bridgehubBurnData = abi.encode( + BridgehubBurnCTMAssetData({ + chainId: chainId, + ctmData: abi.encode(chainAdmin, config.l1DiamondCutData), + chainData: abi.encode(IChainTypeManager(config.chainTypeManagerProxy).getProtocolVersion(chainId)) + }) + ); + + L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); + bytes32 ctmAssetId = IBridgehub(config.bridgehub).ctmAssetIdFromAddress(config.chainTypeManagerProxy); + bytes32 l2TxHash = l2AssetRouter.withdraw(ctmAssetId, bridgehubBurnData); + + saveOutput(l2TxHash); } function finishMigrateChainFromGateway( From 183589c65ba66a7bd67d68c63d18cf82bbf85da0 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Sun, 29 Sep 2024 22:23:40 +0100 Subject: [PATCH 22/42] script --- .../deploy-scripts/GatewayPreparation.s.sol | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index d3e1827fd..72f130ef1 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -295,17 +295,11 @@ contract GatewayPreparation is Script { ) public { initializeConfig(); - bytes32 chainAssetId = IBridgehub(config.bridgehub).ctmAssetIdFromChainId(chainId); - - uint256 currentSettlementLayer = IBridgehub(config.bridgehub).settlementLayer(chainId); - if (currentSettlementLayer == config.l1ChainId) { - console.log("Chain already using L1 as its settlement layer"); - saveOutput(bytes32(0)); - return; - } + IBridgehub l2Bridgehub = IBridgehub(config.bridgehub); - if (currentSettlementLayer == 0) { - console.log("Chain has never used Gateway as its settlement layer"); + uint256 currentSettlementLayer = l2Bridgehub.settlementLayer(chainId); + if (currentSettlementLayer == config.l1ChainId || currentSettlementLayer == 0) { + console.log("Chain not using Gateway as settlement layer"); saveOutput(bytes32(0)); return; } @@ -318,8 +312,8 @@ contract GatewayPreparation is Script { }) ); + bytes32 ctmAssetId = l2Bridgehub.ctmAssetIdFromChainId(chainId); L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); - bytes32 ctmAssetId = IBridgehub(config.bridgehub).ctmAssetIdFromAddress(config.chainTypeManagerProxy); bytes32 l2TxHash = l2AssetRouter.withdraw(ctmAssetId, bridgehubBurnData); saveOutput(l2TxHash); From aa87952a6679472fd9ce7f0c41953d80b50502b7 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 30 Sep 2024 14:07:57 +0100 Subject: [PATCH 23/42] l1 test fixes --- l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol | 2 -- .../integration/l2-dummy-tests/_SharedL2ContractDeployer.sol | 1 - .../l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol | 3 +++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol index d9abe13da..77aad90a5 100644 --- a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol @@ -120,7 +120,6 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, address chain = _deployZkChain( chainId, baseTokenAssetId, - address(bridgeHub.sharedBridge()), owner, chainTypeManager.protocolVersion(), chainTypeManager.storedBatchZero(), @@ -152,7 +151,6 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, address chain = _deployZkChain( chainId, baseTokenAssetId, - address(bridgeHub.sharedBridge()), owner, chainTypeManager.protocolVersion(), chainTypeManager.storedBatchZero(), diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol index aa7e5676a..8892bd21f 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol @@ -141,7 +141,6 @@ abstract contract SharedL2ContractDeployer is Test, DeployUtils { address chainAddress = chainTypeManager.createNewChain( ERA_CHAIN_ID + 1, baseTokenAssetId, - L2_ASSET_ROUTER_ADDR, address(0x1), abi.encode(config.contracts.diamondCutData, generatedData.forceDeploymentsData), new bytes[](0) diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol index b48b7f0bc..8c0e7e099 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol @@ -76,7 +76,10 @@ contract SharedL2ContractL1DeployerUtils is DeployUtils { MessageRoot(L2_MESSAGE_ROOT_ADDR).initialize(); vm.etch(L2_BRIDGEHUB_ADDR, bridgehub.code); + uint256 prevChainId = block.chainid; + vm.chainId(_args.l1ChainId); Bridgehub(L2_BRIDGEHUB_ADDR).initialize(_args.aliasedOwner); + vm.chainId(prevChainId); vm.prank(_args.aliasedOwner); Bridgehub(L2_BRIDGEHUB_ADDR).setAddresses( L2_ASSET_ROUTER_ADDR, From f6e3538a21f67c61ab1aad3cac09e949636653ad Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 1 Oct 2024 18:28:35 +0100 Subject: [PATCH 24/42] gateway -> l1 migration fix --- .../bridge/asset-router/L2AssetRouter.sol | 4 +- .../deploy-scripts/GatewayPreparation.s.sol | 89 +++++++++---------- .../l1/integration/L1GatewayTests.t.sol | 33 ++++++- 3 files changed, 75 insertions(+), 51 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index d9b414c55..0562a05b4 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -207,9 +207,9 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { function _getAssetRouterWithdrawMessage( bytes32 _assetId, bytes memory _l1bridgeMintData - ) internal pure returns (bytes memory) { + ) internal view returns (bytes memory) { // solhint-disable-next-line func-named-parameters - return abi.encodePacked(IAssetRouterBase.finalizeDeposit.selector, _assetId, _l1bridgeMintData); + return abi.encodePacked(IAssetRouterBase.finalizeDeposit.selector, block.chainid, _assetId, _l1bridgeMintData); } /// @notice Encodes the message for l2ToL1log sent during withdraw initialization. diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 72f130ef1..cf6220d5b 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -22,7 +22,7 @@ import {GatewayTransactionFilterer} from "contracts/transactionFilterer/GatewayT import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION} from "contracts/bridgehub/CTMDeploymentTracker.sol"; -import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {L2AssetRouter, IL2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; @@ -46,7 +46,6 @@ struct Config { address l1NullifierProxy; bytes gatewayDiamondCutData; bytes l1DiamondCutData; - uint256 l1ChainId; } /// @notice Scripts that is responsible for preparing the chain to become a gateway @@ -103,8 +102,7 @@ contract GatewayPreparation is Script { gatewayChainAdmin: toml.readAddress("$.chain_admin"), gatewayAccessControlRestriction: toml.readAddress("$.access_control_restriction"), gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin"), - l1NullifierProxy: toml.readAddress("$.l1_nullifier_proxy"), - l1ChainId: toml.readUint("$.l1_chain_id") + l1NullifierProxy: toml.readAddress("$.l1_nullifier_proxy_addr") }); } @@ -294,11 +292,10 @@ contract GatewayPreparation is Script { uint256 chainId ) public { initializeConfig(); + IBridgehub bridgehub = IBridgehub(config.bridgehub); - IBridgehub l2Bridgehub = IBridgehub(config.bridgehub); - - uint256 currentSettlementLayer = l2Bridgehub.settlementLayer(chainId); - if (currentSettlementLayer == config.l1ChainId || currentSettlementLayer == 0) { + uint256 currentSettlementLayer = bridgehub.settlementLayer(chainId); + if (currentSettlementLayer != config.gatewayChainId) { console.log("Chain not using Gateway as settlement layer"); saveOutput(bytes32(0)); return; @@ -312,60 +309,56 @@ contract GatewayPreparation is Script { }) ); - bytes32 ctmAssetId = l2Bridgehub.ctmAssetIdFromChainId(chainId); + bytes32 ctmAssetId = bridgehub.ctmAssetIdFromChainId(chainId); L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); - bytes32 l2TxHash = l2AssetRouter.withdraw(ctmAssetId, bridgehubBurnData); + bytes memory l2Calldata = abi.encodeCall(IL2AssetRouter.withdraw, (ctmAssetId, bridgehubBurnData)); + // vm.startBroadcast(); + bytes32 l2TxHash = Utils.runAdminL1L2DirectTransaction( + _getL1GasPrice(), + chainAdmin, + accessControlRestriction, + l2Calldata, + Utils.MAX_PRIORITY_TX_GAS, + new bytes[](0), + L2_ASSET_ROUTER_ADDR, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy + ); saveOutput(l2TxHash); } function finishMigrateChainFromGateway( - IZKChain migratingChain, uint256 migratingChainId, - uint256 gatewayChainId + uint256 gatewayChainId, + uint256 l2BatchNumber, + uint256 l2MessageIndex, + uint16 l2TxNumberInBatch, + bytes memory message, + bytes32[] memory merkleProof ) public { initializeConfig(); // TODO(EVM-746): Use L2-based chain admin contract // address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); - IBridgehub bridgehub = IBridgehub(config.bridgehub); - bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); - bytes32 baseTokenAssetId = bridgehub.baseTokenAssetId(migratingChainId); - IChainTypeManager ctm = IChainTypeManager(config.chainTypeManagerProxy); - - bytes memory chainData = abi.encode(IAdmin(address(migratingChain)).prepareChainCommitment()); - bytes memory ctmData = abi.encode( - baseTokenAssetId, - msg.sender, - ctm.protocolVersion(), - config.gatewayDiamondCutData - ); - BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ - chainId: migratingChainId, - baseTokenAssetId: baseTokenAssetId, - ctmData: ctmData, - chainData: chainData - }); - bytes memory bridgehubMintData = abi.encode(data); L1Nullifier l1Nullifier = L1Nullifier(config.l1NullifierProxy); - - l1Nullifier.finalizeDeposit( - FinalizeL1DepositParams({ - chainId: migratingChainId, - l2BatchNumber: 1, - l2MessageIndex: 1, - l2Sender: L2_ASSET_ROUTER_ADDR, - l2TxNumberInBatch: 1, - message: abi.encodePacked( - IAssetRouterBase.finalizeDeposit.selector, - gatewayChainId, - assetId, - bridgehubMintData - ), - merkleProof: new bytes32[](0) - }) - ); + { + IBridgehub bridgehub = IBridgehub(config.bridgehub); + bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); + l1Nullifier.finalizeDeposit( + FinalizeL1DepositParams({ + chainId: gatewayChainId, + l2BatchNumber: l2BatchNumber, + l2MessageIndex: l2MessageIndex, + l2Sender: L2_ASSET_ROUTER_ADDR, + l2TxNumberInBatch: l2TxNumberInBatch, + message: message, + merkleProof: merkleProof + }) + ); + } } /// @dev Calling this function requires private key to the admin of the chain diff --git a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index 0d558b8db..5fd43c169 100644 --- a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -38,6 +38,7 @@ import {TxStatus} from "contracts/common/Messaging.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { uint256 constant TEST_USERS_COUNT = 10; @@ -281,7 +282,37 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L abi.encode(chainTypeManager.protocolVersion()) ); - gatewayScript.finishMigrateChainFromGateway(migratingChain, migratingChainId, gatewayChainId); + uint256 protocolVersion = chainTypeManager.getProtocolVersion(migratingChainId); + + bytes memory chainData = abi.encode(IAdmin(address(migratingChain)).prepareChainCommitment()); + bytes memory ctmData = abi.encode( + baseTokenAssetId, + msg.sender, + protocolVersion, + ecosystemConfig.contracts.diamondCutData + ); + BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + chainId: migratingChainId, + baseTokenAssetId: baseTokenAssetId, + ctmData: ctmData, + chainData: chainData + }); + bytes memory bridgehubMintData = abi.encode(data); + bytes memory message = abi.encodePacked( + IAssetRouterBase.finalizeDeposit.selector, + gatewayChainId, + assetId, + bridgehubMintData + ); + gatewayScript.finishMigrateChainFromGateway( + migratingChainId, + gatewayChainId, + 0, + 0, + 0, + message, + new bytes32[](0) + ); vm.chainId(currentChainId); From ffcc50fd05fb95a422270ca7bc92bcc4ce0cbcdc Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 1 Oct 2024 20:10:58 +0100 Subject: [PATCH 25/42] stas issue fixes --- .../contracts/bridge/BridgedStandardERC20.sol | 5 ++ l1-contracts/contracts/bridge/L1Nullifier.sol | 2 +- .../contracts/bridge/L2WrappedBaseToken.sol | 9 +++- .../bridge/asset-router/L1AssetRouter.sol | 2 +- .../bridge/asset-router/L2AssetRouter.sol | 4 +- .../bridge/ntv/INativeTokenVault.sol | 4 ++ .../l1/integration/AssetRouterTest.t.sol | 14 ++++-- .../integration/BridgeHubInvariantTests.t.sol | 16 +++--- .../l1/integration/BridgehubTests.t.sol | 16 +++--- .../l1/integration/DeploymentTest.t.sol | 50 +++++++++---------- .../l1/integration/L1GatewayTests.t.sol | 22 ++++---- .../_SharedL1ContractDeployer.t.sol | 20 ++++---- .../integration/_SharedZKChainDeployer.t.sol | 10 ++-- .../_SharedL2ContractDeployer.sol | 2 +- .../_SharedL2ContractL1DeployerUtils.sol | 3 +- .../l2/integration/L2GatewayTests.t.sol | 2 - 16 files changed, 101 insertions(+), 80 deletions(-) diff --git a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol index b6dddb4d9..d848dcbac 100644 --- a/l1-contracts/contracts/bridge/BridgedStandardERC20.sol +++ b/l1-contracts/contracts/bridge/BridgedStandardERC20.sol @@ -10,6 +10,7 @@ import {IBridgedStandardToken} from "./interfaces/IBridgedStandardToken.sol"; import {Unauthorized, NonSequentialVersion, ZeroAddress} from "../common/L1ContractErrors.sol"; import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; +import {INativeTokenVault} from "../bridge/ntv/INativeTokenVault.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -53,6 +54,10 @@ contract BridgedStandardERC20 is ERC20PermitUpgradeable, IBridgedStandardToken, if (ntv == address(0)) { ntv = L2_NATIVE_TOKEN_VAULT_ADDR; nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR; + assetId = DataEncoding.encodeNTVAssetId( + INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).L1_CHAIN_ID(), + originToken + ); } if (msg.sender != ntv) { revert Unauthorized(msg.sender); diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 2684bde55..5601bf344 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -702,7 +702,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, ) external override onlyLegacyBridge { bytes memory assetData = abi.encode(_amount, _depositSender); /// the legacy bridge can only be used with L1 native tokens. - bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Asset); + bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Asset); _verifyAndClearFailedTransfer({ _checkedInLegacyBridge: true, diff --git a/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol b/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol index cc825cbee..7af7b6cd3 100644 --- a/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol +++ b/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol @@ -33,7 +33,7 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri /// @dev Address of the native token vault. address public override nativeTokenVault; - /// @dev The assetId of the token. + /// @dev The assetId of the token. The wrapped token does not have its own assetId. bytes32 public override assetId; modifier onlyBridge() { @@ -66,7 +66,8 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri string calldata name_, string calldata symbol_, address _l2Bridge, - address _l1Address + address _l1Address, + bytes32 _baseTokenAssetId ) external reinitializer(2) { if (_l2Bridge == address(0)) { revert ZeroAddress(); @@ -75,9 +76,13 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri if (_l1Address == address(0)) { revert ZeroAddress(); } + if (_baseTokenAssetId == bytes32(0)) { + revert ZeroAddress(); + } l2Bridge = _l2Bridge; l1Address = _l1Address; nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR; + assetId = _baseTokenAssetId; // Set decoded values for name and symbol. __ERC20_init_unchained(name_, symbol_); diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index c61297a9a..1c5d18c8a 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -357,7 +357,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { /// @param _token The native token address which should be registered with native token vault. /// @return assetId The asset ID of the token provided. function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { - assetId = nativeTokenVault.calculateAssetId(block.chainid, _token); + assetId = DataEncoding.encodeNTVAssetId(block.chainid, _token); if (nativeTokenVault.tokenAddress(assetId) == address(0)) { nativeTokenVault.registerToken(_token); } diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 0562a05b4..b2919d5f8 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -152,12 +152,12 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @param _assetId The asset id of the withdrawn asset /// @param _assetData The data that is passed to the asset handler contract function withdraw(bytes32 _assetId, bytes memory _assetData) public override returns (bytes32) { - _withdrawSender(_assetId, _assetData, msg.sender, true); + return _withdrawSender(_assetId, _assetData, msg.sender, true); } function withdrawToken(address _l2NativeToken, bytes memory _assetData) public returns (bytes32) { bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); - _withdrawSender(assetId, _assetData, msg.sender, true); + return _withdrawSender(assetId, _assetData, msg.sender, true); } /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 diff --git a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol index f6887f6e6..05b641c43 100644 --- a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol @@ -16,6 +16,10 @@ interface INativeTokenVault { /// @notice The AssetRouter contract function ASSET_ROUTER() external view returns (IAssetRouterBase); + + /// @notice The chain ID of the L1 chain + function L1_CHAIN_ID() external view returns (uint256); + /// @notice Returns the chain ID of the origin chain for a given asset ID function originChainId(bytes32 assetId) external view returns (uint256); diff --git a/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol b/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol index 83913d2a4..7d122638d 100644 --- a/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/AssetRouterTest.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; +import {console2 as console} from "forge-std/console2.sol"; import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; @@ -24,6 +25,7 @@ import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {IL1Nullifier, FinalizeL1DepositParams} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IAssetRouterBase, LEGACY_ENCODING_VERSION, NEW_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; @@ -79,12 +81,12 @@ contract AssetRouterTest is L1ContractDeployer, ZKChainDeployer, TokenDeployer, function depositToL1(address _tokenAddress) public { vm.mockCall( - address(bridgeHub), + address(bridgehub), abi.encodeWithSelector(IBridgehub.proveL2MessageInclusion.selector), abi.encode(true) ); uint256 chainId = eraZKChainId; - l2TokenAssetId = DataEncoding.encodeNTVAssetId(chainId, address(1)); + l2TokenAssetId = DataEncoding.encodeNTVAssetId(chainId, _tokenAddress); bytes memory transferData = DataEncoding.encodeBridgeMintData({ _originalCaller: ETH_TOKEN_ADDRESS, _l2Receiver: address(this), @@ -152,7 +154,13 @@ contract AssetRouterTest is L1ContractDeployer, ZKChainDeployer, TokenDeployer, function test_BridgeTokenBurn() public { depositToL1(ETH_TOKEN_ADDRESS); BridgedStandardERC20 bridgedToken = BridgedStandardERC20(l1NativeTokenVault.tokenAddress(l2TokenAssetId)); + // setting nativeTokenVault to zero address. vm.store(address(bridgedToken), bytes32(uint256(207)), bytes32(0)); + vm.mockCall( + address(L2_NATIVE_TOKEN_VAULT_ADDR), + abi.encodeWithSelector(INativeTokenVault.L1_CHAIN_ID.selector), + abi.encode(block.chainid) + ); vm.broadcast(L2_NATIVE_TOKEN_VAULT_ADDR); // kl todo call ntv, or even assetRouter/bridgehub bridgedToken.bridgeBurn(address(this), 100); } @@ -164,7 +172,7 @@ contract AssetRouterTest is L1ContractDeployer, ZKChainDeployer, TokenDeployer, abi.encode(l2TokenAssetId, abi.encode(uint256(100), address(this))) ); IERC20(tokenL1Address).approve(address(l1NativeTokenVault), 100); - bridgeHub.requestL2TransactionTwoBridges{value: 250000000000100}( + bridgehub.requestL2TransactionTwoBridges{value: 250000000000100}( L2TransactionRequestTwoBridgesOuter({ chainId: eraZKChainId, mintValue: 250000000000100, diff --git a/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol b/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol index 5d23e1ad6..b4fec78de 100644 --- a/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol @@ -270,7 +270,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges{value: mintValue}(requestTx); + bytes32 resultantHash = bridgehub.requestL2TransactionTwoBridges{value: mintValue}(requestTx); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -320,7 +320,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges{value: l2Value}(requestTx); + bytes32 resultantHash = bridgehub.requestL2TransactionTwoBridges{value: l2Value}(requestTx); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -375,7 +375,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges(requestTx); + bytes32 resultantHash = bridgehub.requestL2TransactionTwoBridges(requestTx); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -420,7 +420,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionDirect{value: mintValue}(txRequest); + bytes32 resultantHash = bridgehub.requestL2TransactionDirect{value: mintValue}(txRequest); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -464,7 +464,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionDirect(txRequest); + bytes32 resultantHash = bridgehub.requestL2TransactionDirect(txRequest); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -509,7 +509,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgeHubProxyAddress, + bridgehubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -565,7 +565,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgeHubProxyAddress, + bridgehubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -643,7 +643,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe } function getAddressesToExclude() public returns (address[] memory) { - addressesToExclude.push(bridgeHubProxyAddress); + addressesToExclude.push(bridgehubProxyAddress); addressesToExclude.push(address(sharedBridge)); for (uint256 i = 0; i < users.length; i++) { diff --git a/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol b/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol index a9e54825a..a99790839 100644 --- a/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol @@ -270,7 +270,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges{value: mintValue}(requestTx); + bytes32 resultantHash = bridgehub.requestL2TransactionTwoBridges{value: mintValue}(requestTx); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -320,7 +320,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges{value: l2Value}(requestTx); + bytes32 resultantHash = bridgehub.requestL2TransactionTwoBridges{value: l2Value}(requestTx); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -375,7 +375,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionTwoBridges(requestTx); + bytes32 resultantHash = bridgehub.requestL2TransactionTwoBridges(requestTx); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -420,7 +420,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionDirect{value: mintValue}(txRequest); + bytes32 resultantHash = bridgehub.requestL2TransactionDirect{value: mintValue}(txRequest); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -464,7 +464,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.recordLogs(); - bytes32 resultantHash = bridgeHub.requestL2TransactionDirect(txRequest); + bytes32 resultantHash = bridgehub.requestL2TransactionDirect(txRequest); Vm.Log[] memory logs = vm.getRecordedLogs(); NewPriorityRequest memory request = _getNewPriorityQueueFromLogs(logs); @@ -509,7 +509,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgeHubProxyAddress, + bridgehubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -565,7 +565,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgeHubProxyAddress, + bridgehubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -643,7 +643,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe } function getAddressesToExclude() public returns (address[] memory) { - addressesToExclude.push(bridgeHubProxyAddress); + addressesToExclude.push(bridgehubProxyAddress); addressesToExclude.push(address(sharedBridge)); for (uint256 i = 0; i < users.length; i++) { diff --git a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol index 77aad90a5..b4cca3bb1 100644 --- a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol @@ -73,17 +73,17 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, // equals the balance of L1Shared bridge. function test_initialDeployment() public { uint256 chainId = zkChainIds[0]; - address newChainAddress = bridgeHub.getZKChain(chainId); - address admin = IZKChain(bridgeHub.getZKChain(chainId)).getAdmin(); + address newChainAddress = bridgehub.getZKChain(chainId); + address admin = IZKChain(bridgehub.getZKChain(chainId)).getAdmin(); assertNotEq(admin, address(0)); assertNotEq(newChainAddress, address(0)); - address[] memory chainAddresses = bridgeHub.getAllZKChains(); + address[] memory chainAddresses = bridgehub.getAllZKChains(); assertEq(chainAddresses.length, 1); assertEq(chainAddresses[0], newChainAddress); - uint256[] memory chainIds = bridgeHub.getAllZKChainChainIDs(); + uint256[] memory chainIds = bridgehub.getAllZKChainChainIDs(); assertEq(chainIds.length, 1); assertEq(chainIds[0], chainId); @@ -91,7 +91,7 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, assertEq(protocolVersion, 25); } - function test_bridgeHubSetter() public { + function test_bridgehubSetter() public { uint256 chainId = zkChainIds[0]; uint256 randomChainId = 123456; @@ -100,18 +100,18 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, abi.encodeWithSelector(IChainTypeManager.getZKChainLegacy.selector, randomChainId), abi.encode(address(0x01)) ); - vm.store(address(bridgeHub), keccak256(abi.encode(randomChainId, 205)), bytes32(uint256(uint160(1)))); + vm.store(address(bridgehub), keccak256(abi.encode(randomChainId, 205)), bytes32(uint256(uint160(1)))); vm.store( - address(bridgeHub), + address(bridgehub), keccak256(abi.encode(randomChainId, 204)), bytes32(uint256(uint160(address(chainTypeManager)))) ); - bridgeHub.setLegacyBaseTokenAssetId(randomChainId); - bridgeHub.setLegacyChainAddress(randomChainId); + bridgehub.setLegacyBaseTokenAssetId(randomChainId); + bridgehub.setLegacyChainAddress(randomChainId); } function test_registerAlreadyDeployedZKChain() public { - address owner = Ownable(address(bridgeHub)).owner(); + address owner = Ownable(address(bridgehub)).owner(); { uint256 chainId = currentZKChainId++; @@ -123,26 +123,26 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, owner, chainTypeManager.protocolVersion(), chainTypeManager.storedBatchZero(), - address(bridgeHub) + address(bridgehub) ); address stmAddr = IZKChain(chain).getChainTypeManager(); vm.startBroadcast(owner); - bridgeHub.addChainTypeManager(stmAddr); - bridgeHub.addTokenAssetId(baseTokenAssetId); - bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); + bridgehub.addChainTypeManager(stmAddr); + bridgehub.addTokenAssetId(baseTokenAssetId); + bridgehub.registerAlreadyDeployedZKChain(chainId, chain); vm.stopBroadcast(); - address bridgeHubStmForChain = bridgeHub.chainTypeManager(chainId); - bytes32 bridgeHubBaseAssetIdForChain = bridgeHub.baseTokenAssetId(chainId); - address bridgeHubChainAddressdForChain = bridgeHub.getZKChain(chainId); + address bridgehubStmForChain = bridgehub.chainTypeManager(chainId); + bytes32 bridgehubBaseAssetIdForChain = bridgehub.baseTokenAssetId(chainId); + address bridgehubChainAddressdForChain = bridgehub.getZKChain(chainId); address bhAddr = IZKChain(chain).getBridgehub(); - assertEq(bridgeHubStmForChain, stmAddr); - assertEq(bridgeHubBaseAssetIdForChain, baseTokenAssetId); - assertEq(bridgeHubChainAddressdForChain, chain); - assertEq(bhAddr, address(bridgeHub)); + assertEq(bridgehubStmForChain, stmAddr); + assertEq(bridgehubBaseAssetIdForChain, baseTokenAssetId); + assertEq(bridgehubChainAddressdForChain, chain); + assertEq(bhAddr, address(bridgehub)); } { @@ -154,17 +154,17 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, owner, chainTypeManager.protocolVersion(), chainTypeManager.storedBatchZero(), - address(bridgeHub.sharedBridge()) + address(bridgehub.sharedBridge()) ); address stmAddr = IZKChain(chain).getChainTypeManager(); vm.startBroadcast(owner); - bridgeHub.addTokenAssetId(baseTokenAssetId); + bridgehub.addTokenAssetId(baseTokenAssetId); vm.expectRevert( - abi.encodeWithSelector(IncorrectBridgeHubAddress.selector, address(bridgeHub.sharedBridge())) + abi.encodeWithSelector(IncorrectBridgeHubAddress.selector, address(bridgehub.sharedBridge())) ); - bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); + bridgehub.registerAlreadyDeployedZKChain(chainId, chain); vm.stopBroadcast(); } } diff --git a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index 5fd43c169..9f67d7973 100644 --- a/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -91,13 +91,13 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L _initializeGatewayScript(); vm.deal(ecosystemConfig.ownerAddress, 100000000000000000000000000000000000); - migratingChain = IZKChain(IBridgehub(bridgeHub).getZKChain(migratingChainId)); - gatewayChain = IZKChain(IBridgehub(bridgeHub).getZKChain(gatewayChainId)); + migratingChain = IZKChain(IBridgehub(bridgehub).getZKChain(migratingChainId)); + gatewayChain = IZKChain(IBridgehub(bridgehub).getZKChain(gatewayChainId)); vm.deal(migratingChain.getAdmin(), 100000000000000000000000000000000000); vm.deal(gatewayChain.getAdmin(), 100000000000000000000000000000000000); // vm.deal(msg.sender, 100000000000000000000000000000000000); - // vm.deal(bridgeHub, 100000000000000000000000000000000000); + // vm.deal(bridgehub, 100000000000000000000000000000000000); } // This is a method to simplify porting the tests for now. @@ -128,7 +128,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L _extractAccessControlRestriction(migratingChain.getAdmin()), migratingChainId ); - require(bridgeHub.settlementLayer(migratingChainId) == gatewayChainId, "Migration failed"); + require(bridgehub.settlementLayer(migratingChainId) == gatewayChainId, "Migration failed"); } function test_l2Registration() public { @@ -149,7 +149,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L _extractAccessControlRestriction(migratingChain.getAdmin()), migratingChainId ); - IBridgehub bridgehub = IBridgehub(bridgeHub); + IBridgehub bridgehub = IBridgehub(bridgehub); uint256 expectedValue = 1000000000000000000000; L2TransactionRequestDirect memory request = _createL2TransactionRequestDirect( @@ -172,7 +172,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L ); // Setup - IBridgehub bridgehub = IBridgehub(bridgeHub); + IBridgehub bridgehub = IBridgehub(bridgehub); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); bytes memory transferData; @@ -219,7 +219,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L ); // Set Deposit Happened - vm.startBroadcast(address(bridgeHub)); + vm.startBroadcast(address(bridgehub)); assetRouter.bridgehubConfirmL2Transaction({ _chainId: migratingChainId, _txDataHash: txDataHash, @@ -242,7 +242,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L vm.stopBroadcast(); } - function test_migrateBackChain() public { + function test_finishMigrateBackChain() public { _setUpGatewayWithFilterer(); gatewayScript.migrateChainToGateway( migratingChain.getAdmin(), @@ -253,7 +253,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L } function migrateBackChain() public { - IBridgehub bridgehub = IBridgehub(bridgeHub); + IBridgehub bridgehub = IBridgehub(bridgehub); IZKChain migratingChain = IZKChain(bridgehub.getZKChain(migratingChainId)); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); @@ -267,7 +267,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L // we are already on L1, so we have to set another chain id, it cannot be GW or mintChainId. vm.chainId(migratingChainId); vm.mockCall( - address(bridgeHub), + address(bridgehub), abi.encodeWithSelector(IBridgehub.proveL2MessageInclusion.selector), abi.encode(true) ); @@ -326,7 +326,7 @@ contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L _setUpGatewayWithFilterer(); vm.chainId(12345); vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); - bridgeHub.forwardTransactionOnGateway(migratingChainId, bytes32(0), 0); + bridgehub.forwardTransactionOnGateway(migratingChainId, bytes32(0), 0); vm.stopBroadcast(); } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index d7d7866d9..e4cb7e690 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -21,9 +21,9 @@ contract L1ContractDeployer is Test { DeployedAddresses public ecosystemAddresses; Config public ecosystemConfig; - address bridgeHubProxyAddress; - address bridgeHubOwnerAddress; - Bridgehub bridgeHub; + address bridgehubProxyAddress; + address bridgehubOwnerAddress; + Bridgehub bridgehub; CTMDeploymentTracker ctmDeploymentTracker; @@ -55,7 +55,7 @@ contract L1ContractDeployer is Test { ecosystemAddresses = l1Script.getAddresses(); ecosystemConfig = l1Script.getConfig(); - bridgeHub = Bridgehub(ecosystemAddresses.bridgehub.bridgehubProxy); + bridgehub = Bridgehub(ecosystemAddresses.bridgehub.bridgehubProxy); chainTypeManager = IChainTypeManager(ecosystemAddresses.stateTransition.chainTypeManagerProxy); ctmDeploymentTracker = CTMDeploymentTracker(ecosystemAddresses.bridgehub.ctmDeploymentTrackerProxy); @@ -66,12 +66,12 @@ contract L1ContractDeployer is Test { _acceptOwnership(); _setEraBatch(); - bridgeHubOwnerAddress = bridgeHub.owner(); + bridgehubOwnerAddress = bridgehub.owner(); } function _acceptOwnership() private { - vm.startPrank(bridgeHub.pendingOwner()); - bridgeHub.acceptOwnership(); + vm.startPrank(bridgehub.pendingOwner()); + bridgehub.acceptOwnership(); sharedBridge.acceptOwnership(); ctmDeploymentTracker.acceptOwnership(); vm.stopPrank(); @@ -86,9 +86,9 @@ contract L1ContractDeployer is Test { function _registerNewToken(address _tokenAddress) internal { bytes32 tokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _tokenAddress); - if (!bridgeHub.assetIdIsRegistered(tokenAssetId)) { - vm.prank(bridgeHubOwnerAddress); - bridgeHub.addTokenAssetId(tokenAssetId); + if (!bridgehub.assetIdIsRegistered(tokenAssetId)) { + vm.prank(bridgehubOwnerAddress); + bridgehub.addTokenAssetId(tokenAssetId); } } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol index d9b47ab06..8836fec99 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol @@ -139,15 +139,15 @@ contract ZKChainDeployer is L1ContractDeployer { } function getZKChainAddress(uint256 _chainId) public view returns (address) { - return bridgeHub.getZKChain(_chainId); + return bridgehub.getZKChain(_chainId); } function getZKChainBaseToken(uint256 _chainId) public view returns (address) { - return bridgeHub.baseToken(_chainId); + return bridgehub.baseToken(_chainId); } function acceptPendingAdmin() public { - IZKChain chain = IZKChain(bridgeHub.getZKChain(currentZKChainId - 1)); + IZKChain chain = IZKChain(bridgehub.getZKChain(currentZKChainId - 1)); address admin = chain.getPendingAdmin(); vm.startBroadcast(admin); chain.acceptAdmin(); @@ -164,7 +164,7 @@ contract ZKChainDeployer is L1ContractDeployer { address _admin, uint256 _protocolVersion, bytes32 _storedBatchZero, - address _bridgeHub + address _bridgehub ) internal returns (address) { Diamond.DiamondCutData memory diamondCut = abi.decode( ecosystemConfig.contracts.diamondCutData, @@ -176,7 +176,7 @@ contract ZKChainDeployer is L1ContractDeployer { initData = bytes.concat( IDiamondInit.initialize.selector, bytes32(_chainId), - bytes32(uint256(uint160(address(_bridgeHub)))), + bytes32(uint256(uint160(address(_bridgehub)))), bytes32(uint256(uint160(address(this)))), bytes32(_protocolVersion), bytes32(uint256(uint160(_admin))), diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol index 8892bd21f..3a7611c6e 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol @@ -194,7 +194,7 @@ abstract contract SharedL2ContractDeployer is Test, DeployUtils { L2WrappedBaseToken wethImpl = new L2WrappedBaseToken(); TransparentUpgradeableProxy wethProxy = new TransparentUpgradeableProxy(address(wethImpl), ownerWallet, ""); weth = L2WrappedBaseToken(payable(wethProxy)); - weth.initializeV2("Wrapped Ether", "WETH", L2_ASSET_ROUTER_ADDR, l1WethAddress); + weth.initializeV2("Wrapped Ether", "WETH", L2_ASSET_ROUTER_ADDR, l1WethAddress, baseTokenAssetId); return weth; } diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol index 8c0e7e099..c3ee33a06 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol @@ -43,10 +43,11 @@ contract SharedL2ContractL1DeployerUtils is DeployUtils { using stdToml for string; using stdStorage for StdStorage; + /// @dev We provide a fast form of debugging the L2 contracts using L1 foundry. We also test using zk foundry. function initSystemContracts(SystemContractsArgs memory _args) internal virtual { bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(_args.l1ChainId, ETH_TOKEN_ADDRESS); address wethToken = address(0x1); - + // we deploy the code to get the contract code with immutables which we then etch address messageRoot = address(new MessageRoot(IBridgehub(L2_BRIDGEHUB_ADDR))); address bridgehub = address(new Bridgehub(_args.l1ChainId, _args.aliasedOwner, 100)); address assetRouter = address( diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index f9518d213..9c4f5da05 100644 --- a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -7,12 +7,10 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import "forge-std/console.sol"; -// import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; -// import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; From 40a556a75f119f5ae19c4f78581a97a05612709e Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 1 Oct 2024 20:51:15 +0100 Subject: [PATCH 26/42] rename l2 dummy tests --- .../L2Erc20L1Test.t.sol | 0 .../L2Erc20TestAbstract.t.sol | 0 .../L2GatewayL1Test.t.sol | 0 .../L2GatewayTestAbstract.t.sol | 0 .../L2WethTestAbstract.t.sol | 0 .../_SharedL2ContractDeployer.sol | 1 - .../_SharedL2ContractL1DeployerUtils.sol | 2 +- 7 files changed, 1 insertion(+), 2 deletions(-) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/L2Erc20L1Test.t.sol (100%) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/L2Erc20TestAbstract.t.sol (100%) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/L2GatewayL1Test.t.sol (100%) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/L2GatewayTestAbstract.t.sol (100%) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/L2WethTestAbstract.t.sol (100%) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/_SharedL2ContractDeployer.sol (99%) rename l1-contracts/test/foundry/l1/integration/{l2-dummy-tests => l2-tests-in-l1-context}/_SharedL2ContractL1DeployerUtils.sol (99%) diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2Erc20L1Test.t.sol similarity index 100% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2Erc20L1Test.t.sol diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2Erc20TestAbstract.t.sol similarity index 100% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2Erc20TestAbstract.t.sol diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2GatewayL1Test.t.sol similarity index 100% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2GatewayL1Test.t.sol diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2GatewayTestAbstract.t.sol similarity index 100% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2GatewayTestAbstract.t.sol diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2WethTestAbstract.t.sol similarity index 100% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/L2WethTestAbstract.t.sol diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol similarity index 99% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol index 3a7611c6e..83d8def98 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol @@ -30,7 +30,6 @@ import {L2WrappedBaseToken} from "contracts/bridge/L2WrappedBaseToken.sol"; import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; -// import {L2ContractL1Deployer} from "./_SharedL2ContractL1Deployer.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol similarity index 99% rename from l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol rename to l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol index c3ee33a06..9dd125055 100644 --- a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol @@ -47,7 +47,7 @@ contract SharedL2ContractL1DeployerUtils is DeployUtils { function initSystemContracts(SystemContractsArgs memory _args) internal virtual { bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(_args.l1ChainId, ETH_TOKEN_ADDRESS); address wethToken = address(0x1); - // we deploy the code to get the contract code with immutables which we then etch + // we deploy the code to get the contract code with immutables which we then vm.etch address messageRoot = address(new MessageRoot(IBridgehub(L2_BRIDGEHUB_ADDR))); address bridgehub = address(new Bridgehub(_args.l1ChainId, _args.aliasedOwner, 100)); address assetRouter = address( From b88b1b6ddb3b99334904f14e7c3be0f9cc84bab1 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Tue, 1 Oct 2024 21:05:22 +0100 Subject: [PATCH 27/42] renamed the dummy-tests folder --- .../test/foundry/l2/integration/L2ERC20BridgeTest.t.sol | 6 +++--- .../test/foundry/l2/integration/L2GatewayTests.t.sol | 6 +++--- l1-contracts/test/foundry/l2/integration/L2Utils.sol | 2 +- l1-contracts/test/foundry/l2/integration/WETH.t.sol | 6 +++--- .../l2/integration/_SharedL2ContractL2DeployerUtils.sol | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol b/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol index 9b308fd9e..8fe0ffcc8 100644 --- a/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol @@ -19,11 +19,11 @@ import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol"; import {L2Utils, SystemContractsArgs} from "./L2Utils.sol"; import {SharedL2ContractL2DeployerUtils} from "./_SharedL2ContractL2DeployerUtils.sol"; -import {L2Erc20TestAbstract} from "../../l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol"; -import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; +import {L2Erc20TestAbstract} from "../../l1/integration/l2-tests-in-l1-context/L2Erc20TestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol"; contract L2Erc20Test is Test, L2Erc20TestAbstract, SharedL2ContractL2DeployerUtils { function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol index 9c4f5da05..b1ae7bb5f 100644 --- a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -30,9 +30,9 @@ import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.so import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; -import {L2GatewayTestAbstract} from "../../l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol"; -import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol"; +import {L2GatewayTestAbstract} from "../../l1/integration/l2-tests-in-l1-context/L2GatewayTestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol"; contract L2GatewayTests is Test, L2GatewayTestAbstract, SharedL2ContractL2DeployerUtils { // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. diff --git a/l1-contracts/test/foundry/l2/integration/L2Utils.sol b/l1-contracts/test/foundry/l2/integration/L2Utils.sol index 9a0afb563..d10105734 100644 --- a/l1-contracts/test/foundry/l2/integration/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/integration/L2Utils.sol @@ -26,7 +26,7 @@ import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {SystemContractsCaller} from "contracts/common/libraries/SystemContractsCaller.sol"; import {DeployFailed} from "contracts/common/L1ContractErrors.sol"; -import {SystemContractsArgs} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; +import {SystemContractsArgs} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol"; library L2Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); diff --git a/l1-contracts/test/foundry/l2/integration/WETH.t.sol b/l1-contracts/test/foundry/l2/integration/WETH.t.sol index 5a0b8e246..f7932b4eb 100644 --- a/l1-contracts/test/foundry/l2/integration/WETH.t.sol +++ b/l1-contracts/test/foundry/l2/integration/WETH.t.sol @@ -6,9 +6,9 @@ import {Test} from "forge-std/Test.sol"; import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; -import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; -import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; -import {L2WethTestAbstract} from "../../l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractDeployer.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol"; +import {L2WethTestAbstract} from "../../l1/integration/l2-tests-in-l1-context/L2WethTestAbstract.t.sol"; import {SharedL2ContractL2DeployerUtils, SystemContractsArgs} from "./_SharedL2ContractL2DeployerUtils.sol"; diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol index 61f51d0cd..0b42255b5 100644 --- a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol @@ -19,7 +19,7 @@ import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {L2Utils} from "./L2Utils.sol"; -import {SharedL2ContractL1DeployerUtils, SystemContractsArgs} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {SharedL2ContractL1DeployerUtils, SystemContractsArgs} from "../../l1/integration/l2-tests-in-l1-context/_SharedL2ContractL1DeployerUtils.sol"; contract SharedL2ContractL2DeployerUtils is DeployUtils, SharedL2ContractL1DeployerUtils { using stdToml for string; From 5e55954313367f8ddaba5e5fca556a451ae0657c Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 2 Oct 2024 16:08:25 +0400 Subject: [PATCH 28/42] (fix): support in Nullifier L2 native tokens, correct recovery of failed deposits for bridged tokens, add forwarding function to L1AR for SDK --- l1-contracts/contracts/bridge/L1Nullifier.sol | 5 +++- .../bridge/asset-router/IL1AssetRouter.sol | 23 +++++++++++++++ .../bridge/asset-router/L1AssetRouter.sol | 29 +++++++++++++++++++ .../bridge/ntv/L1NativeTokenVault.sol | 7 ++++- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 3c3d3d539..6eef7616c 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -642,7 +642,10 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, uint16 _l2TxNumberInBatch, bytes32[] calldata _merkleProof ) external override { - bytes32 assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Token); + bytes32 assetId = l1NativeTokenVault.assetId(_l1Token); + if (assetId == bytes32(0)) { + assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Token); + } // For legacy deposits, the l2 receiver is not required to check tx data hash // bytes memory transferData = abi.encode(_amount, _depositSender); bytes memory assetData = abi.encode(_amount, address(0)); diff --git a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol index 5e04fd2ea..c35e0f6f6 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol @@ -94,6 +94,29 @@ interface IL1AssetRouter is IAssetRouterBase { bytes calldata _assetData ) external; + /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2. + /// @param _chainId The ZK chain id to which deposit was initiated. + /// @param _depositSender The address of the entity that initiated the deposit. + /// @param _assetId The unique identifier of the deposited L1 token. + /// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information. + /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization. + /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed. + /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message. + /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent. + /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization. + /// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system. + function bridgeRecoverFailedTransfer( + uint256 _chainId, + address _depositSender, + bytes32 _assetId, + bytes memory _assetData, + bytes32 _l2TxHash, + uint256 _l2BatchNumber, + uint256 _l2MessageIndex, + uint16 _l2TxNumberInBatch, + bytes32[] calldata _merkleProof + ) external; + /// @notice Transfers funds to Native Token Vault, if the asset is registered with it. Does nothing for ETH or non-registered tokens. /// @dev assetId is not the padded address, but the correct encoded id (NTV stores respective format for IDs) /// @param _amount The asset amount to be transferred to native token vault. diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 458bc20b8..256b0d18d 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -335,6 +335,31 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { emit ClaimedFailedDepositAssetRouter(_chainId, _assetId, _assetData); } + + function bridgeRecoverFailedTransfer( + uint256 _chainId, + address _depositSender, + bytes32 _assetId, + bytes memory _assetData, + bytes32 _l2TxHash, + uint256 _l2BatchNumber, + uint256 _l2MessageIndex, + uint16 _l2TxNumberInBatch, + bytes32[] calldata _merkleProof + ) external { + L1_NULLIFIER.bridgeRecoverFailedTransfer( + _chainId, + _depositSender, + _assetId, + _assetData, + _l2TxHash, + _l2BatchNumber, + _l2MessageIndex, + _l2TxNumberInBatch, + _merkleProof + ); + } + /*////////////////////////////////////////////////////////////// Internal & Helpers //////////////////////////////////////////////////////////////*/ @@ -357,6 +382,10 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { /// @param _token The native token address which should be registered with native token vault. /// @return assetId The asset ID of the token provided. function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { + assetId = nativeTokenVault.assetId(_token); + if (assetId != bytes32(0)) { + return assetId; + } assetId = nativeTokenVault.calculateAssetId(block.chainid, _token); if (nativeTokenVault.tokenAddress(assetId) == address(0)) { nativeTokenVault.registerToken(_token); diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index f1d14834d..b94f946ab 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -17,6 +17,7 @@ import {NativeTokenVault} from "./NativeTokenVault.sol"; import {IL1AssetHandler} from "../interfaces/IL1AssetHandler.sol"; import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol"; +import {IBridgedStandardToken} from "../interfaces/IBridgedStandardToken.sol"; import {IL1AssetRouter} from "../asset-router/IL1AssetRouter.sol"; import {ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; @@ -178,7 +179,11 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken } require(callSuccess, "NTV: claimFailedDeposit failed, no funds or cannot transfer to receiver"); } else { - IERC20(l1Token).safeTransfer(_depositSender, _amount); + if (originChainId[_assetId] != block.chainid) { + IBridgedStandardToken(l1Token).bridgeMint(_depositSender, _amount); + } else { + IERC20(l1Token).safeTransfer(_depositSender, _amount); + } // Note we don't allow weth deposits anymore, but there might be legacy weth deposits. // until we add Weth bridging capabilities, we don't wrap/unwrap weth to ether. } From 0199a621f904bcd858416d57d2c26d6406b8bfda Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 2 Oct 2024 17:07:25 +0400 Subject: [PATCH 29/42] (merge): fix --- .gitignore | 1 + l1-contracts/.env | 1 + l1-contracts/contracts/bridge/L1Nullifier.sol | 6 + .../contracts/bridge/L2SharedBridgeLegacy.sol | 48 +- .../bridge/asset-router/IL1AssetRouter.sol | 3 +- .../bridge/asset-router/IL2AssetRouter.sol | 10 +- .../bridge/asset-router/L1AssetRouter.sol | 6 + .../bridge/asset-router/L2AssetRouter.sol | 42 +- .../interfaces/IL1SharedBridgeLegacy.sol | 9 - .../interfaces/IL2SharedBridgeLegacy.sol | 13 +- .../bridge/ntv/IL1NativeTokenVault.sol | 3 +- .../bridge/ntv/L1NativeTokenVault.sol | 12 + .../bridge/ntv/L2NativeTokenVault.sol | 4 +- .../contracts/bridge/ntv/NativeTokenVault.sol | 2 +- .../contracts/bridgehub/Bridgehub.sol | 29 +- .../contracts/bridgehub/IBridgehub.sol | 6 +- l1-contracts/contracts/common/Config.sol | 5 + .../contracts/common/L1ContractErrors.sol | 8 +- .../dev-contracts/test/DummyBridgehub.sol | 6 + .../test/L2NativeTokenVaultDev.sol | 76 + .../governance/IPermanentRestriction.sol | 3 + .../contracts/governance/L2AdminFactory.sol | 42 + .../governance/L2ProxyAdminDeployer.sol | 22 + .../governance/PermanentRestriction.sol | 137 +- .../governance/TransitionaryOwner.sol | 25 + .../state-transition/ChainTypeManager.sol | 8 +- .../state-transition/IChainTypeManager.sol | 1 - .../chain-deps/DiamondInit.sol | 4 - .../chain-deps/ZKChainStorage.sol | 2 +- .../chain-deps/facets/Getters.sol | 5 - .../chain-deps/facets/Mailbox.sol | 6 +- .../chain-deps/facets/ZKChainBase.sol | 7 - .../chain-interfaces/IAdmin.sol | 3 + .../chain-interfaces/IDiamondInit.sol | 2 - .../chain-interfaces/IGetters.sol | 3 - .../l2-deps/IL2GatewayUpgrade.sol | 13 + .../contracts/upgrades/GatewayHelper.sol | 24 + .../contracts/upgrades/GatewayUpgrade.sol | 56 +- .../contracts/upgrades/L1GenesisUpgrade.sol | 13 +- l1-contracts/deploy-scripts/DeployL1.s.sol | 554 +------ l1-contracts/deploy-scripts/DeployUtils.s.sol | 483 ++++++ .../deploy-scripts/GatewayPreparation.s.sol | 125 +- .../L2ContractsBytecodesLib.sol | 43 + .../deploy-scripts/RegisterZKChain.s.sol | 56 +- l1-contracts/deploy-scripts/Utils.sol | 83 +- .../deploy-scripts/upgrade/ChainUpgrade.s.sol | 212 +++ .../upgrade/EcosystemUpgrade.s.sol | 1348 +++++++++++++++++ l1-contracts/foundry.toml | 4 +- l1-contracts/package.json | 7 +- l1-contracts/src.ts/deploy.ts | 4 + l1-contracts/src.ts/utils.ts | 5 +- .../integration/BridgeHubInvariantTests.t.sol | 6 +- .../l1/integration/BridgehubTests.t.sol | 6 +- .../l1/integration/DeploymentTest.t.sol | 89 +- ...atewayTests.t.sol => L1GatewayTests.t.sol} | 204 ++- .../foundry/l1/integration/UpgradeTest.t.sol | 105 ++ ...ts.sol => _GatewayPreparationForTests.sol} | 5 +- .../integration/_SharedGatewayDeployer.t.sol | 6 +- .../_SharedL1ContractDeployer.t.sol | 39 +- .../integration/_SharedZKChainDeployer.t.sol | 9 +- .../script-config/config-deploy-l1.toml | 2 +- .../l2-dummy-tests/L2Erc20L1Test.t.sol | 51 + .../l2-dummy-tests/L2Erc20TestAbstract.t.sol} | 70 +- .../l2-dummy-tests/L2GatewayL1Test.t.sol | 51 + .../L2GatewayTestAbstract.t.sol | 85 ++ .../l2-dummy-tests/L2WethTestAbstract.t.sol} | 45 +- .../_SharedL2ContractDeployer.sol | 203 +++ .../_SharedL2ContractL1DeployerUtils.sol | 128 ++ .../script-config/mainnet-era.toml | 7 + .../upgrade-envs/script-config/mainnet.toml | 37 + .../upgrade-envs/script-out/.gitkeep | 0 .../Bridgehub/experimental_bridge.t.sol | 4 +- .../L1SharedBridge/L1SharedBridgeFails.t.sol | 133 +- .../concrete/DiamondCut/UpgradeLogic.t.sol | 1 - .../concrete/Executor/ExecutorProof.t.sol | 103 +- .../concrete/Executor/_Executor_Shared.t.sol | 3 +- .../Governance/PermanentRestriction.t.sol | 184 ++- .../foundry/l1/unit/concrete/Utils/Utils.sol | 111 +- .../l1/unit/concrete/Utils/UtilsFacet.sol | 8 - .../ChainTypeManager/CreateNewChain.t.sol | 11 +- .../ChainTypeManager/FreezeChain.t.sol | 41 +- .../ChainTypeManager/RevertBatches.t.sol | 345 +++-- .../_ChainTypeManager_Shared.t.sol | 33 +- .../chain-deps/DiamondInit/Initialize.t.sol | 1 - .../facets/Getters/GetBaseTokenBridge.t.sol | 16 - .../facets/Getters/_Getters_Shared.t.sol | 4 - .../facets/Mailbox/FinalizeWithdrawal.t.sol | 5 +- .../facets/Mailbox/RequestL2Transaction.t.sol | 4 +- .../facets/Mailbox/_Mailbox_Shared.t.sol | 4 + .../l2/integration/L2ERC20BridgeTest.t.sol | 49 + .../l2/integration/L2GatewayTests.t.sol | 62 + .../{unit/utils => integration}/L2Utils.sol | 159 +- .../test/foundry/l2/integration/WETH.t.sol | 36 + .../_SharedL2ContractL2DeployerUtils.sol | 41 + .../unit/L2AdminFactory/L2AdminFactory.t.sol | 42 + .../ValidiumL2DAValidator.t.sol | 2 +- system-contracts/SystemContractsHashes.json | 48 +- .../contracts/L2GatewayUpgrade.sol | 78 + .../contracts/L2GatewayUpgradeHelper.sol | 117 ++ .../contracts/L2GenesisUpgrade.sol | 108 +- .../interfaces/IL2GenesisUpgrade.sol | 5 + .../interfaces/IL2SharedBridgeLegacy.sol | 32 + .../libraries/SystemContractHelper.sol | 15 + system-contracts/package.json | 4 +- .../test/L2GenesisUpgrade.spec.ts | 7 +- 105 files changed, 5163 insertions(+), 1391 deletions(-) create mode 100644 l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol create mode 100644 l1-contracts/contracts/governance/L2AdminFactory.sol create mode 100644 l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol create mode 100644 l1-contracts/contracts/governance/TransitionaryOwner.sol create mode 100644 l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol create mode 100644 l1-contracts/contracts/upgrades/GatewayHelper.sol create mode 100644 l1-contracts/deploy-scripts/DeployUtils.s.sol create mode 100644 l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol create mode 100644 l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol rename l1-contracts/test/foundry/l1/integration/{GatewayTests.t.sol => L1GatewayTests.t.sol} (65%) create mode 100644 l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol rename l1-contracts/test/foundry/l1/integration/{GatewayPreparationForTests.sol => _GatewayPreparationForTests.sol} (87%) create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol rename l1-contracts/test/foundry/{l2/unit/erc20/L2Erc20BridgeTest.t.sol => l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol} (58%) create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol rename l1-contracts/test/foundry/{l2/unit/weth/WETH.t.sol => l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol} (61%) create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol create mode 100644 l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol create mode 100644 l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml create mode 100644 l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml create mode 100644 l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/.gitkeep delete mode 100644 l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol create mode 100644 l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol create mode 100644 l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol rename l1-contracts/test/foundry/l2/{unit/utils => integration}/L2Utils.sol (55%) create mode 100644 l1-contracts/test/foundry/l2/integration/WETH.t.sol create mode 100644 l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol create mode 100644 l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol create mode 100644 system-contracts/contracts/L2GatewayUpgrade.sol create mode 100644 system-contracts/contracts/L2GatewayUpgradeHelper.sol create mode 100644 system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol diff --git a/.gitignore b/.gitignore index a2a7a4d05..2128686e0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ l1-contracts/test/foundry/l1/integration/deploy-scripts/script-out/*.toml l1-contracts/test/foundry/l1/integration/deploy-scripts/script-out/* l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-*.toml l1-contracts/test/foundry/integration/deploy-scripts/script-out/* +l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/*.toml l1-contracts/zkout/* diff --git a/l1-contracts/.env b/l1-contracts/.env index 25ec2b87f..0cbe2dbd1 100644 --- a/l1-contracts/.env +++ b/l1-contracts/.env @@ -44,3 +44,4 @@ TOKENS_CONFIG=/script-config/config-deploy-erc20.toml ZK_CHAIN_CONFIG=/script-config/register-zk-chain.toml ZK_CHAIN_OUTPUT=/script-out/output-deploy-zk-chain-era.toml FORCE_DEPLOYMENTS_CONFIG=/script-config/generate-force-deployments-data.toml +GATEWAY_PREPARATION_L1_CONFIG=/script-config/gateway-preparation-l1.toml diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 6eef7616c..63a940f9c 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -100,6 +100,12 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, /// NOTE: this function may be removed in the future, don't rely on it! mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public __DEPRECATED_chainBalance; + /// @dev Admin has the ability to register new chains within the shared bridge. + address public __DEPRECATED_admin; + + /// @dev The pending admin, i.e. the candidate to the admin role. + address public __DEPRECATED_pendingAdmin; + /// @dev Address of L1 asset router. IL1AssetRouter public l1AssetRouter; diff --git a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol index 9ea29cf07..4ae901593 100644 --- a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol @@ -10,17 +10,20 @@ import {BridgedStandardERC20} from "./BridgedStandardERC20.sol"; import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol"; import {SystemContractsCaller} from "../common/libraries/SystemContractsCaller.sol"; import {L2ContractHelper, IContractDeployer} from "../common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IL2AssetRouter} from "./asset-router/IL2AssetRouter.sol"; import {IL2NativeTokenVault} from "./ntv/IL2NativeTokenVault.sol"; import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol"; -import {ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, DeployFailed} from "../common/L1ContractErrors.sol"; +import {InvalidCaller, ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, DeployFailed} from "../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The "default" bridge implementation for the ERC20 tokens. Note, that it does not /// support any custom token logic, i.e. rebase tokens' functionality is not supported. +/// @dev Note, that this contract should be compatible with its previous version as it will be +/// the primary bridge to be used during migration. contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { /// @dev The address of the L1 shared bridge counterpart. address public override l1SharedBridge; @@ -37,6 +40,7 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { /// @dev The address of the legacy L1 erc20 bridge counterpart. /// This is non-zero only on Era, and should not be renamed for backward compatibility with the SDKs. + // slither-disable-next-line uninitialized-state address public override l1Bridge; modifier onlyNTV() { @@ -103,6 +107,44 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { IL2AssetRouter(L2_ASSET_ROUTER_ADDR).withdrawLegacyBridge(_l1Receiver, _l2Token, _amount, msg.sender); } + /// @notice Finalize the deposit and mint funds + /// @param _l1Sender The account address that initiated the deposit on L1 + /// @param _l2Receiver The account address that would receive minted ether + /// @param _l1Token The address of the token that was locked on the L1 + /// @param _amount Total amount of tokens deposited from L1 + /// @param _data The additional data that user can pass with the deposit + function finalizeDeposit( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external { + // Only the L1 bridge counterpart can initiate and finalize the deposit. + if ( + AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1Bridge && + AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1SharedBridge + ) { + revert InvalidCaller(msg.sender); + } + + IL2AssetRouter(L2_ASSET_ROUTER_ADDR).finalizeDepositLegacyBridge({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + + address l2Token = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).l2TokenAddress(_l1Token); + + if (l1TokenAddress[l2Token] == address(0)) { + l1TokenAddress[l2Token] = _l1Token; + } + + emit FinalizeDeposit(_l1Sender, _l2Receiver, l2Token, _amount); + } + /// @return Address of an L2 token counterpart function l2TokenAddress(address _l1Token) public view override returns (address) { address token = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).l2TokenAddress(_l1Token); @@ -148,8 +190,8 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { proxy = abi.decode(returndata, (address)); } - function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter { + function sendMessageToL1(bytes calldata _message) external override onlyAssetRouter returns (bytes32) { // slither-disable-next-line unused-return - L2ContractHelper.sendMessageToL1(_message); + return L2ContractHelper.sendMessageToL1(_message); } } diff --git a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol index c35e0f6f6..5d4c4fc5f 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol @@ -6,11 +6,12 @@ import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol"; import {INativeTokenVault} from "../ntv/INativeTokenVault.sol"; import {IAssetRouterBase} from "./IAssetRouterBase.sol"; import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol"; +import {IL1SharedBridgeLegacy} from "../interfaces/IL1SharedBridgeLegacy.sol"; /// @title L1 Bridge contract interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -interface IL1AssetRouter is IAssetRouterBase { +interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy { event BridgehubMintData(bytes bridgeMintData); event BridgehubDepositFinalized( diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 7cb2bd1ce..81b1bc995 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -14,12 +14,20 @@ interface IL2AssetRouter is IAssetRouterBase { bytes assetData ); - function withdraw(bytes32 _assetId, bytes calldata _transferData) external; + function withdraw(bytes32 _assetId, bytes calldata _transferData) external returns (bytes32); function l1AssetRouter() external view returns (address); function withdrawLegacyBridge(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) external; + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external; + /// @dev Used to set the assedAddress for a given assetId. /// @dev Will be used by ZK Gateway function setAssetHandlerAddress(uint256 _originChainId, bytes32 _assetId, address _assetAddress) external; diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 256b0d18d..7846ff5b8 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -623,4 +623,10 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { ) external view returns (bool) { return L1_NULLIFIER.isWithdrawalFinalized(_chainId, _l2BatchNumber, _l2MessageIndex); } + /// @notice Legacy function to get the L2 shared bridge address for a chain. + /// @dev In case the chain has been deployed after the gateway release, + /// the returned value is 0. + function l2BridgeAddress(uint256 _chainId) external view override returns (address) { + return L1_NULLIFIER.l2BridgeAddress(_chainId); + } } diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 0e19b79b7..0562a05b4 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -151,11 +151,11 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @dev do not rely on this function, it will be deprecated in the future /// @param _assetId The asset id of the withdrawn asset /// @param _assetData The data that is passed to the asset handler contract - function withdraw(bytes32 _assetId, bytes memory _assetData) public override { + function withdraw(bytes32 _assetId, bytes memory _assetData) public override returns (bytes32) { _withdrawSender(_assetId, _assetData, msg.sender, true); } - function withdrawToken(address _l2NativeToken, bytes memory _assetData) public { + function withdrawToken(address _l2NativeToken, bytes memory _assetData) public returns (bytes32) { bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); _withdrawSender(assetId, _assetData, msg.sender, true); } @@ -171,7 +171,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { bytes memory _assetData, address _sender, bool _alwaysNewMessageFormat - ) internal { + ) internal returns (bytes32 txHash) { address assetHandler = assetHandlerAddress[_assetId]; bytes memory _l1bridgeMintData = IAssetHandler(assetHandler).bridgeBurn({ _chainId: L1_CHAIN_ID, @@ -185,7 +185,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { if (_alwaysNewMessageFormat || L2_LEGACY_SHARED_BRIDGE == address(0)) { message = _getAssetRouterWithdrawMessage(_assetId, _l1bridgeMintData); // slither-disable-next-line unused-return - L2ContractHelper.sendMessageToL1(message); + txHash = L2ContractHelper.sendMessageToL1(message); } else { address l1Token = IBridgedStandardToken( IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).tokenAddress(_assetId) @@ -195,7 +195,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { } (uint256 amount, address l1Receiver) = abi.decode(_assetData, (uint256, address)); message = _getSharedBridgeWithdrawMessage(l1Receiver, l1Token, amount); - IL2SharedBridgeLegacy(L2_LEGACY_SHARED_BRIDGE).sendMessageToL1(message); + txHash = IL2SharedBridgeLegacy(L2_LEGACY_SHARED_BRIDGE).sendMessageToL1(message); } emit WithdrawalInitiatedAssetRouter(L1_CHAIN_ID, _sender, _assetId, _assetData); @@ -236,6 +236,38 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { uint256 _amount, bytes calldata _data ) external onlyAssetRouterCounterpart(L1_CHAIN_ID) { + _translateLegacyFinalizeDeposit({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + } + + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external onlyLegacyBridge { + _translateLegacyFinalizeDeposit({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + } + + function _translateLegacyFinalizeDeposit( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) internal { bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, _l1Token); // solhint-disable-next-line func-named-parameters bytes memory data = DataEncoding.encodeBridgeMintData(_l1Sender, _l2Receiver, _l1Token, _amount, _data); diff --git a/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol index 627048f75..43fca83a3 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol @@ -7,13 +7,4 @@ pragma solidity 0.8.24; /// @custom:security-contact security@matterlabs.dev interface IL1SharedBridgeLegacy { function l2BridgeAddress(uint256 _chainId) external view returns (address); - - event LegacyDepositInitiated( - uint256 indexed chainId, - bytes32 indexed l2DepositTxHash, - address indexed from, - address to, - address l1Asset, - uint256 amount - ); } diff --git a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol index 00a762447..71c7a46c5 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol @@ -2,9 +2,20 @@ pragma solidity ^0.8.20; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev interface IL2SharedBridgeLegacy { + event FinalizeDeposit( + address indexed l1Sender, + address indexed l2Receiver, + address indexed l2Token, + uint256 amount + ); + + function l2TokenBeacon() external returns (UpgradeableBeacon); + function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; function l1TokenAddress(address _l2Token) external view returns (address); @@ -17,5 +28,5 @@ interface IL2SharedBridgeLegacy { function deployBeaconProxy(bytes32 _salt) external returns (address); - function sendMessageToL1(bytes calldata _message) external; + function sendMessageToL1(bytes calldata _message) external returns (bytes32); } diff --git a/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol index 1d16f48fb..a3fcbe917 100644 --- a/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol @@ -4,13 +4,14 @@ pragma solidity 0.8.24; import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol"; import {INativeTokenVault} from "./INativeTokenVault.sol"; +import {IL1AssetDeploymentTracker} from "../interfaces/IL1AssetDeploymentTracker.sol"; /// @title L1 Native token vault contract interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens // is IL1AssetHandler, IL1BaseTokenAssetHandler { -interface IL1NativeTokenVault is INativeTokenVault { +interface IL1NativeTokenVault is INativeTokenVault, IL1AssetDeploymentTracker { /// @notice The L1Nullifier contract function L1_NULLIFIER() external view returns (IL1Nullifier); diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index b94f946ab..65b2e8e39 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -21,6 +21,7 @@ import {IBridgedStandardToken} from "../interfaces/IBridgedStandardToken.sol"; import {IL1AssetRouter} from "../asset-router/IL1AssetRouter.sol"; import {ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/L2ContractAddresses.sol"; import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; import {Unauthorized, ZeroAddress, NoFundsTransferred, InsufficientChainBalance, WithdrawFailed} from "../../common/L1ContractErrors.sol"; @@ -124,6 +125,17 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken L1_NULLIFIER.nullifyChainBalanceByNTV(_targetChainId, _token); } + /// @notice Used to register the Asset Handler asset in L2 AssetRouter. + /// @param _assetHandlerAddressOnCounterpart the address of the asset handler on the counterpart chain. + function bridgeCheckCounterpartAddress( + uint256, + bytes32, + address, + address _assetHandlerAddressOnCounterpart + ) external view override onlyAssetRouter { + require(_assetHandlerAddressOnCounterpart == L2_NATIVE_TOKEN_VAULT_ADDR, "NTV: wrong counterpart"); + } + /*////////////////////////////////////////////////////////////// Start transaction Functions //////////////////////////////////////////////////////////////*/ diff --git a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol index a6a0ff62d..4b83037a7 100644 --- a/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L2NativeTokenVault.sol @@ -132,7 +132,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { /// for the code of the proxy. /// @param _salt The salt used for beacon proxy deployment of L2 bridged token. /// @return proxy The beacon proxy, i.e. L2 bridged token. - function _deployBeaconProxy(bytes32 _salt) internal override returns (BeaconProxy proxy) { + function _deployBeaconProxy(bytes32 _salt) internal virtual override returns (BeaconProxy proxy) { if (address(L2_LEGACY_SHARED_BRIDGE) == address(0)) { // Deploy the beacon proxy for the L2 token @@ -177,7 +177,7 @@ contract L2NativeTokenVault is IL2NativeTokenVault, NativeTokenVault { function calculateCreate2TokenAddress( uint256 _originChainId, address _l1Token - ) public view override(INativeTokenVault, NativeTokenVault) returns (address) { + ) public view virtual override(INativeTokenVault, NativeTokenVault) returns (address) { bytes32 constructorInputHash = keccak256(abi.encode(address(bridgedTokenBeacon), "")); bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); if (address(L2_LEGACY_SHARED_BRIDGE) != address(0)) { diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 5fb6394f5..644669f18 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -255,7 +255,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 } _handleChainBalanceIncrease(_chainId, _assetId, amount, true); if (_depositAmount != amount) { - revert ValueMismatch(amount, msg.value); + revert ValueMismatch(_depositAmount, amount); } } else { // The Bridgehub also checks this, but we want to be sure diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..e084bc3d1 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -18,7 +18,7 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; import {IZKChain} from "../state-transition/chain-interfaces/IZKChain.sol"; -import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "../common/Config.sol"; +import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER, L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS} from "../common/Config.sol"; import {BridgehubL2TransactionRequest, L2Message, L2Log, TxStatus} from "../common/Messaging.sol"; import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IMessageRoot} from "./IMessageRoot.sol"; @@ -86,6 +86,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @dev asset info used to identify chains in the Shared Bridge mapping(bytes32 ctmAssetId => address ctmAddress) public ctmAssetIdToAddress; + /// @dev ctmAddress to ctmAssetId + mapping(address ctmAddress => bytes32 ctmAssetId) public ctmAssetIdFromAddress; + /// @dev used to indicate the currently active settlement layer for a given chainId mapping(uint256 chainId => uint256 activeSettlementLayerChainId) public settlementLayer; @@ -154,14 +157,24 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus // We will change this with interop. ETH_TOKEN_ASSET_ID = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, ETH_TOKEN_ADDRESS); _transferOwnership(_owner); - whitelistedSettlementLayers[_l1ChainId] = true; + _initializeInner(); } /// @notice used to initialize the contract /// @notice this contract is also deployed on L2 as a system contract there the owner and the related functions will not be used /// @param _owner the owner of the contract - function initialize(address _owner) external reentrancyGuardInitializer { + function initialize(address _owner) external reentrancyGuardInitializer onlyL1 { _transferOwnership(_owner); + _initializeInner(); + } + + /// @notice Used to initialize the contract on L1 + function initializeV2() external initializer onlyL1 { + _initializeInner(); + } + + /// @notice Initializes the contract + function _initializeInner() internal { assetIdIsRegistered[ETH_TOKEN_ASSET_ID] = true; whitelistedSettlementLayers[L1_CHAIN_ID] = true; } @@ -320,6 +333,7 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 assetInfo = keccak256(abi.encode(L1_CHAIN_ID, sender, _additionalData)); ctmAssetIdToAddress[assetInfo] = _assetAddress; + ctmAssetIdFromAddress[_assetAddress] = assetInfo; emit AssetRegistered(assetInfo, _assetAddress, _additionalData, msg.sender); } @@ -356,7 +370,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus address chainAddress = IChainTypeManager(_chainTypeManager).createNewChain({ _chainId: _chainId, _baseTokenAssetId: _baseTokenAssetId, - _assetRouter: assetRouter, _admin: _admin, _initData: _initData, _factoryDeps: _factoryDeps @@ -422,10 +435,10 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus if (ctmAddress == address(0)) { revert ChainIdNotRegistered(_chainId); } - return ctmAssetId(chainTypeManager[_chainId]); + return ctmAssetIdFromAddress[chainTypeManager[_chainId]]; } - function ctmAssetId(address _ctmAddress) public view override returns (bytes32) { + function calculateCtmAssetId(address _ctmAddress) internal view returns (bytes32) { return keccak256(abi.encode(L1_CHAIN_ID, address(l1CtmDeployer), bytes32(uint256(uint160(_ctmAddress))))); } @@ -700,7 +713,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bridgehubData.ctmData ); bytes memory chainMintData = IZKChain(zkChain).forwardedBridgeBurn( - zkChainMap.get(_settlementChainId), + _settlementChainId == L1_CHAIN_ID + ? L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS + : zkChainMap.get(_settlementChainId), _originalCaller, bridgehubData.chainData ); diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index 5e2be03fc..3f05bba35 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -217,7 +217,7 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { function ctmAssetIdFromChainId(uint256 _chainId) external view returns (bytes32); - function ctmAssetId(address _ctmAddress) external view returns (bytes32); + function ctmAssetIdFromAddress(address _ctmAddress) external view returns (bytes32); function l1CtmDeployer() external view returns (ICTMDeploymentTracker); @@ -232,4 +232,8 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { function registerAlreadyDeployedZKChain(uint256 _chainId, address _hyperchain) external; function setLegacyChainAddress(uint256 _chainId) external; + + /// @notice return the ZK chain contract for a chainId + /// @dev It is a legacy method. Do not use! + function getHyperchain(uint256 _chainId) external view returns (address); } diff --git a/l1-contracts/contracts/common/Config.sol b/l1-contracts/contracts/common/Config.sol index beebcd00c..a1e58f464 100644 --- a/l1-contracts/contracts/common/Config.sol +++ b/l1-contracts/contracts/common/Config.sol @@ -120,6 +120,11 @@ address constant SETTLEMENT_LAYER_RELAY_SENDER = address(uint160(0x1111111111111 /// @dev The metadata version that is supported by the ZK Chains to prove that an L2->L1 log was included in a batch. uint256 constant SUPPORTED_PROOF_METADATA_VERSION = 1; +/// @dev The virtual address of the L1 settlement layer. +address constant L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS = address( + uint160(uint256(keccak256("L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS")) - 1) +); + struct PriorityTreeCommitment { uint256 nextLeafIndex; uint256 startIndex; diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 18766497f..00812e606 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -95,8 +95,6 @@ error DepositDoesNotExist(); error DepositExists(); // 0x79cacff1 error DepositFailed(); -// 0xae08e4af -error DepositIncorrectAmount(uint256 expectedAmt, uint256 providedAmt); // 0x0e7ee319 error DiamondAlreadyFrozen(); // 0x682dabb4 @@ -407,6 +405,12 @@ error IncorrectBatchBounds( ); // 0x64107968 error AssetHandlerNotRegistered(bytes32 assetId); +// 0x10f30e75 +error NotBridgehub(address addr); +// 0x2554babc +error InvalidAddress(address expected, address actual); +// 0xfa5cd00f +error NotAllowed(address addr); enum SharedBridgeKey { PostUpgradeFirstBatch, diff --git a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol index 5038f5f66..f178fc0ed 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol @@ -15,6 +15,8 @@ contract DummyBridgehub { address public zkChain; + address public sharedBridge; + // add this to be excluded from coverage report function test() internal virtual {} @@ -41,4 +43,8 @@ contract DummyBridgehub { function getZKChain(uint256) external view returns (address) { return address(0); } + + function setSharedBridge(address addr) external { + sharedBridge = addr; + } } diff --git a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol new file mode 100644 index 000000000..7d4c01577 --- /dev/null +++ b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; +import {Create2} from "@openzeppelin/contracts-v4/utils/Create2.sol"; +import {IBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/IBeacon.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; +import {NativeTokenVault} from "contracts/bridge/ntv/NativeTokenVault.sol"; +import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; + +contract L2NativeTokenVaultDev is L2NativeTokenVault { + constructor( + uint256 _l1ChainId, + address _aliasedOwner, + bytes32 _l2TokenProxyBytecodeHash, + address _legacySharedBridge, + address _bridgedTokenBeacon, + bool _contractsDeployedAlready, + address _wethToken, + bytes32 _baseTokenAssetId + ) + L2NativeTokenVault( + _l1ChainId, + _aliasedOwner, + _l2TokenProxyBytecodeHash, + _legacySharedBridge, + _bridgedTokenBeacon, + _contractsDeployedAlready, + _wethToken, + _baseTokenAssetId + ) + {} + + /// @notice copied from L1NTV for L1 compilation + function calculateCreate2TokenAddress( + uint256 _originChainId, + address _l1Token + ) public view override(L2NativeTokenVault) returns (address) { + bytes32 salt = _getCreate2Salt(_originChainId, _l1Token); + return + Create2.computeAddress( + salt, + keccak256(abi.encodePacked(type(BeaconProxy).creationCode, abi.encode(bridgedTokenBeacon, ""))) + ); + } + + function deployBridgedStandardERC20(address _owner) external { + _transferOwnership(_owner); + + address l2StandardToken = address(new BridgedStandardERC20{salt: bytes32(0)}()); + + UpgradeableBeacon tokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); + + tokenBeacon.transferOwnership(owner()); + bridgedTokenBeacon = IBeacon(address(tokenBeacon)); + emit L2TokenBeaconUpdated(address(bridgedTokenBeacon), l2TokenProxyBytecodeHash); + } + + function test() external pure { + // test + } + + function _deployBeaconProxy(bytes32 _salt) internal virtual override returns (BeaconProxy proxy) { + // Use CREATE2 to deploy the BeaconProxy + address proxyAddress = Create2.deploy( + 0, + _salt, + abi.encodePacked(type(BeaconProxy).creationCode, abi.encode(bridgedTokenBeacon, "")) + ); + return BeaconProxy(payable(proxyAddress)); + } +} diff --git a/l1-contracts/contracts/governance/IPermanentRestriction.sol b/l1-contracts/contracts/governance/IPermanentRestriction.sol index 548866b9f..5fb015e33 100644 --- a/l1-contracts/contracts/governance/IPermanentRestriction.sol +++ b/l1-contracts/contracts/governance/IPermanentRestriction.sol @@ -14,4 +14,7 @@ interface IPermanentRestriction { /// @notice Emitted when the selector is labeled as validated or not. event SelectorValidationChanged(bytes4 indexed selector, bool isValidated); + + /// @notice Emitted when the L2 admin is whitelisted or not. + event AllowL2Admin(address indexed adminAddress); } diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol new file mode 100644 index 000000000..d4fe4637c --- /dev/null +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {ChainAdmin} from "./ChainAdmin.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @dev Contract used to deploy ChainAdmin contracts on L2. +/// @dev It can be used to ensure that certain L2 admins are deployed with +/// predefined restrictions. E.g. it can be used to deploy admins that ensure that +/// a chain is a permanent rollup. +/// @dev This contract is expected to be deployed in zkEVM (L2) environment. +/// @dev The contract is immutable, in case the restrictions need to be changed, +/// a new contract should be deployed. +contract L2AdminFactory { + event AdminDeployed(address admin); + + /// @dev We use storage instead of immutable variables due to the + /// specifics of the zkEVM environment, where storage is actually cheaper. + address[] public requiredRestrictions; + + constructor(address[] memory _requiredRestrictions) { + requiredRestrictions = _requiredRestrictions; + } + + /// @notice Deploys a new L2 admin contract. + /// @return admin The address of the deployed admin contract. + function deployAdmin(address[] calldata _additionalRestrictions, bytes32 _salt) external returns (address admin) { + address[] memory restrictions = new address[](requiredRestrictions.length + _additionalRestrictions.length); + uint256 cachedRequired = requiredRestrictions.length; + for (uint256 i = 0; i < cachedRequired; ++i) { + restrictions[i] = requiredRestrictions[i]; + } + uint256 cachedAdditional = _additionalRestrictions.length; + for (uint256 i = 0; i < cachedAdditional; ++i) { + restrictions[requiredRestrictions.length + i] = _additionalRestrictions[i]; + } + + admin = address(new ChainAdmin{salt: _salt}(restrictions)); + } +} diff --git a/l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol b/l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol new file mode 100644 index 000000000..144f951bf --- /dev/null +++ b/l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +// solhint-disable gas-length-in-loops + +import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice The contract that deterministically deploys a ProxyAdmin, while +/// ensuring that its owner is the aliased governance contract +contract L2ProxyAdminDeployer { + address public immutable PROXY_ADMIN_ADDRESS; + + constructor(address _aliasedGovernance) { + ProxyAdmin admin = new ProxyAdmin{salt: bytes32(0)}(); + admin.transferOwnership(_aliasedGovernance); + + PROXY_ADMIN_ADDRESS = address(admin); + } +} diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index d013a4de6..153ce369e 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -2,19 +2,28 @@ pragma solidity 0.8.24; -import {CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation} from "../common/L1ContractErrors.sol"; +import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotAllowed, NotBridgehub, InvalidSelector, InvalidAddress, NotEnoughGas} from "../common/L1ContractErrors.sol"; -import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; +import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; +import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; +import {NEW_ENCODING_VERSION} from "../bridge/asset-router/IAssetRouterBase.sol"; import {Call} from "./Common.sol"; import {IRestriction} from "./IRestriction.sol"; import {IChainAdmin} from "./IChainAdmin.sol"; import {IBridgehub} from "../bridgehub/IBridgehub.sol"; import {IZKChain} from "../state-transition/chain-interfaces/IZKChain.sol"; +import {IGetters} from "../state-transition/chain-interfaces/IGetters.sol"; import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; +/// @dev We use try-catch to test whether some of the conditions should be checked. +/// To avoid attacks based on the 63/64 gas limitations, we ensure that each such call +/// has at least this amount. +uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; + /// @title PermanentRestriction contract /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -22,10 +31,16 @@ import {IPermanentRestriction} from "./IPermanentRestriction.sol"; /// properties are preserved forever. /// @dev To be deployed as a transparent upgradable proxy, owned by a trusted decentralized governance. /// @dev Once of the instances of such contract is to ensure that a ZkSyncHyperchain is a rollup forever. -contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2Step { +contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2StepUpgradeable { /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; + /// @notice The address of the L2 admin factory that should be used to deploy the chain admins + /// for chains that migrated on top of an L2 settlement layer. + /// @dev If this contract is deployed on L2, this address is 0. + /// @dev This address is expected to be the same on all L2 chains. + address public immutable L2_ADMIN_FACTORY; + /// @notice The mapping of the allowed admin implementations. mapping(bytes32 implementationCodeHash => bool isAllowed) public allowedAdminImplementations; @@ -35,9 +50,15 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice The mapping of the validated selectors. mapping(bytes4 selector => bool isValidated) public validatedSelectors; - constructor(address _initialOwner, IBridgehub _bridgehub) { + /// @notice The mapping of whitelisted L2 admins. + mapping(address adminAddress => bool isWhitelisted) public allowedL2Admins; + + constructor(IBridgehub _bridgehub, address _l2AdminFactory) { BRIDGE_HUB = _bridgehub; + L2_ADMIN_FACTORY = _l2AdminFactory; + } + function initialize(address _initialOwner) external initializer { // solhint-disable-next-line gas-custom-errors, reason-string if (_initialOwner == address(0)) { revert ZeroAddress(); @@ -72,15 +93,53 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St emit SelectorValidationChanged(_selector, _isValidated); } + /// @notice Whitelists a certain L2 admin. + /// @param deploymentSalt The salt for the deployment. + /// @param l2BytecodeHash The hash of the L2 bytecode. + /// @param constructorInputHash The hash of the constructor data for the deployment. + function allowL2Admin(bytes32 deploymentSalt, bytes32 l2BytecodeHash, bytes32 constructorInputHash) external { + // We do not do any additional validations for constructor data or the bytecode, + // we expect that only admins of the allowed format are to be deployed. + address expectedAddress = L2ContractHelper.computeCreate2Address( + L2_ADMIN_FACTORY, + deploymentSalt, + l2BytecodeHash, + constructorInputHash + ); + + if (allowedL2Admins[expectedAddress]) { + revert AlreadyWhitelisted(expectedAddress); + } + + allowedL2Admins[expectedAddress] = true; + emit AllowL2Admin(expectedAddress); + } + /// @inheritdoc IRestriction function validateCall( Call calldata _call, address // _invoker ) external view override { _validateAsChainAdmin(_call); + _validateMigrationToL2(_call); _validateRemoveRestriction(_call); } + /// @notice Validates the migration to an L2 settlement layer. + /// @param _call The call data. + /// @dev Note that we do not need to validate the migration to the L1 layer as the admin + /// is not changed in this case. + function _validateMigrationToL2(Call calldata _call) internal view { + _ensureEnoughGas(); + try this.tryGetNewAdminFromMigration(_call) returns (address admin) { + if (!allowedL2Admins[admin]) { + revert NotAllowed(admin); + } + } catch { + // It was not the migration call, so we do nothing + } + } + /// @notice Validates the call as the chain admin /// @param _call The call data. function _validateAsChainAdmin(Call calldata _call) internal view { @@ -153,6 +212,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Checks if the `msg.sender` is an admin of a certain ZkSyncHyperchain. /// @param _chain The address of the chain. function _isAdminOfAChain(address _chain) internal view returns (bool) { + _ensureEnoughGas(); (bool success, ) = address(this).staticcall(abi.encodeCall(this.tryCompareAdminOfAChain, (_chain, msg.sender))); return success; } @@ -172,8 +232,20 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St // - Query it for `chainId`. If it reverts, it is not a ZkSyncHyperchain. // - Query the Bridgehub for the Hyperchain with the given `chainId`. // - We compare the corresponding addresses - uint256 chainId = IZKChain(_chain).getChainId(); - if (BRIDGE_HUB.getZKChain(chainId) != _chain) { + + // Note, that we do not use an explicit call here to ensure that the function does not panic in case of + // incorrect `_chain` address. + (bool success, bytes memory data) = _chain.staticcall(abi.encodeWithSelector(IGetters.getChainId.selector)); + if (!success || data.length < 32) { + revert NotAHyperchain(_chain); + } + + // Can not fail + uint256 chainId = abi.decode(data, (uint256)); + + // Note, that here it is important to use the legacy `getHyperchain` function, so that the contract + // is compatible with the legacy ones. + if (BRIDGE_HUB.getHyperchain(chainId) != _chain) { revert NotAHyperchain(_chain); } @@ -183,4 +255,57 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St revert NotAnAdmin(admin, _potentialAdmin); } } + + /// @notice Tries to get the new admin from the migration. + /// @param _call The call data. + /// @dev This function reverts if the provided call was not a migration call. + function tryGetNewAdminFromMigration(Call calldata _call) external view returns (address) { + if (_call.target != address(BRIDGE_HUB)) { + revert NotBridgehub(_call.target); + } + + if (bytes4(_call.data[:4]) != IBridgehub.requestL2TransactionTwoBridges.selector) { + revert InvalidSelector(bytes4(_call.data[:4])); + } + + address sharedBridge = BRIDGE_HUB.sharedBridge(); + + L2TransactionRequestTwoBridgesOuter memory request = abi.decode( + _call.data[4:], + (L2TransactionRequestTwoBridgesOuter) + ); + + if (request.secondBridgeAddress != sharedBridge) { + revert InvalidAddress(sharedBridge, request.secondBridgeAddress); + } + + bytes memory secondBridgeData = request.secondBridgeCalldata; + if (secondBridgeData[0] != NEW_ENCODING_VERSION) { + revert UnsupportedEncodingVersion(); + } + bytes memory encodedData = new bytes(secondBridgeData.length - 1); + assembly { + mcopy(add(encodedData, 0x20), add(secondBridgeData, 0x21), mload(encodedData)) + } + + (bytes32 chainAssetId, bytes memory bridgehubData) = abi.decode(encodedData, (bytes32, bytes)); + // We will just check that the chainAssetId is a valid chainAssetId. + // For now, for simplicity, we do not check that the admin is exactly the admin + // of this chain. + address ctmAddress = BRIDGE_HUB.ctmAssetIdToAddress(chainAssetId); + if (ctmAddress == address(0)) { + revert ZeroAddress(); + } + + BridgehubBurnCTMAssetData memory burnData = abi.decode(bridgehubData, (BridgehubBurnCTMAssetData)); + (address l2Admin, ) = abi.decode(burnData.ctmData, (address, bytes)); + + return l2Admin; + } + + function _ensureEnoughGas() internal view { + if (gasleft() < MIN_GAS_FOR_FALLABLE_CALL) { + revert NotEnoughGas(); + } + } } diff --git a/l1-contracts/contracts/governance/TransitionaryOwner.sol b/l1-contracts/contracts/governance/TransitionaryOwner.sol new file mode 100644 index 000000000..9248204bf --- /dev/null +++ b/l1-contracts/contracts/governance/TransitionaryOwner.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +// solhint-disable gas-length-in-loops + +import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice The contract that is used a temporary owner for Ownable2Step contracts until the +/// governance can accept the ownership +contract TransitionaryOwner { + address public immutable GOVERNANCE_ADDRESS; + + constructor(address _governanceAddress) { + GOVERNANCE_ADDRESS = _governanceAddress; + } + + /// @notice Claims that ownership of a contract and transfers it to the governance + function claimOwnershipAndGiveToGovernance(address target) external { + Ownable2Step(target).acceptOwnership(); + Ownable2Step(target).transferOwnership(GOVERNANCE_ADDRESS); + } +} diff --git a/l1-contracts/contracts/state-transition/ChainTypeManager.sol b/l1-contracts/contracts/state-transition/ChainTypeManager.sol index 62df92419..2760e36c3 100644 --- a/l1-contracts/contracts/state-transition/ChainTypeManager.sol +++ b/l1-contracts/contracts/state-transition/ChainTypeManager.sol @@ -344,13 +344,11 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg /// @notice deploys a full set of chains contracts /// @param _chainId the chain's id /// @param _baseTokenAssetId the base token asset id used to pay for gas fees - /// @param _sharedBridge the shared bridge address, used as base token bridge /// @param _admin the chain's admin address /// @param _diamondCut the diamond cut data that initializes the chains Diamond Proxy function _deployNewChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _sharedBridge, address _admin, bytes memory _diamondCut ) internal returns (address zkChainAddress) { @@ -383,7 +381,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg bytes32(uint256(uint160(_admin))), bytes32(uint256(uint160(validatorTimelock))), _baseTokenAssetId, - bytes32(uint256(uint160(_sharedBridge))), storedBatchZero, diamondCut.initCalldata ); @@ -400,7 +397,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg /// @notice called by Bridgehub when a chain registers /// @param _chainId the chain's id /// @param _baseTokenAssetId the base token asset id used to pay for gas fees - /// @param _assetRouter the shared bridge address, used as base token bridge /// @param _admin the chain's admin address /// @param _initData the diamond cut data, force deployments and factoryDeps encoded /// @param _factoryDeps the factory dependencies used for the genesis upgrade @@ -408,7 +404,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg function createNewChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _assetRouter, address _admin, bytes calldata _initData, bytes[] calldata _factoryDeps @@ -416,7 +411,7 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg (bytes memory _diamondCut, bytes memory _forceDeploymentData) = abi.decode(_initData, (bytes, bytes)); // solhint-disable-next-line func-named-parameters - zkChainAddress = _deployNewChain(_chainId, _baseTokenAssetId, _assetRouter, _admin, _diamondCut); + zkChainAddress = _deployNewChain(_chainId, _baseTokenAssetId, _admin, _diamondCut); { // check input @@ -492,7 +487,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg chainAddress = _deployNewChain({ _chainId: _chainId, _baseTokenAssetId: _baseTokenAssetId, - _sharedBridge: address(IBridgehub(BRIDGE_HUB).sharedBridge()), _admin: _admin, _diamondCut: _diamondCut }); diff --git a/l1-contracts/contracts/state-transition/IChainTypeManager.sol b/l1-contracts/contracts/state-transition/IChainTypeManager.sol index b5202e975..90b500b28 100644 --- a/l1-contracts/contracts/state-transition/IChainTypeManager.sol +++ b/l1-contracts/contracts/state-transition/IChainTypeManager.sol @@ -115,7 +115,6 @@ interface IChainTypeManager { function createNewChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _assetRouter, address _admin, bytes calldata _initData, bytes[] calldata _factoryDeps diff --git a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol index 3be7dc2b1..69a646308 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol @@ -45,9 +45,6 @@ contract DiamondInit is ZKChainBase, IDiamondInit { if (_initializeData.baseTokenAssetId == bytes32(0)) { revert ZeroAddress(); } - if (_initializeData.baseTokenBridge == address(0)) { - revert ZeroAddress(); - } if (_initializeData.blobVersionedHashRetriever == address(0)) { revert ZeroAddress(); } @@ -56,7 +53,6 @@ contract DiamondInit is ZKChainBase, IDiamondInit { s.bridgehub = _initializeData.bridgehub; s.chainTypeManager = _initializeData.chainTypeManager; s.baseTokenAssetId = _initializeData.baseTokenAssetId; - s.baseTokenBridge = _initializeData.baseTokenBridge; s.protocolVersion = _initializeData.protocolVersion; s.verifier = _initializeData.verifier; diff --git a/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol b/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol index 3205a229e..5f19aecd4 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol @@ -149,7 +149,7 @@ struct ZKChainStorage { /// @dev The address of the baseToken contract. Eth is address(1) address __DEPRECATED_baseToken; /// @dev The address of the baseTokenbridge. Eth also uses the shared bridge - address baseTokenBridge; + address __DEPRECATED_baseTokenBridge; /// @notice gasPriceMultiplier for each baseToken, so that each L1->L2 transaction pays for its transaction on the destination /// we multiply by the nominator, and divide by the denominator uint128 baseTokenGasPriceMultiplierNominator; diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol index 1ffdb5b0c..b5eda6d19 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol @@ -75,11 +75,6 @@ contract GettersFacet is ZKChainBase, IGetters, ILegacyGetters { return s.baseTokenAssetId; } - /// @inheritdoc IGetters - function getBaseTokenBridge() external view returns (address) { - return s.baseTokenBridge; - } - /// @inheritdoc IGetters function baseTokenGasPriceMultiplierNominator() external view returns (uint128) { return s.baseTokenGasPriceMultiplierNominator; 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 48b6dd76d..d230a04b9 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol @@ -610,7 +610,8 @@ contract MailboxFacet is ZKChainBase, IMailbox { if (s.chainId != ERA_CHAIN_ID) { revert OnlyEraSupported(); } - IL1AssetRouter(s.baseTokenBridge).finalizeWithdrawal({ + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + IL1AssetRouter(sharedBridge).finalizeWithdrawal({ _chainId: ERA_CHAIN_ID, _l2BatchNumber: _l2BatchNumber, _l2MessageIndex: _l2MessageIndex, @@ -646,7 +647,8 @@ contract MailboxFacet is ZKChainBase, IMailbox { refundRecipient: _refundRecipient }) ); - IL1AssetRouter(s.baseTokenBridge).bridgehubDepositBaseToken{value: msg.value}( + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + IL1AssetRouter(sharedBridge).bridgehubDepositBaseToken{value: msg.value}( s.chainId, s.baseTokenAssetId, msg.sender, diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol index 45c360197..6c8a08657 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol @@ -62,13 +62,6 @@ contract ZKChainBase is ReentrancyGuard { _; } - modifier onlyBaseTokenBridge() { - if (msg.sender != s.baseTokenBridge) { - revert Unauthorized(msg.sender); - } - _; - } - function _getTotalPriorityTxs() internal view returns (uint256) { if (s.priorityQueue.getFirstUnprocessedPriorityTx() >= s.priorityTree.startIndex) { return s.priorityTree.getTotalPriorityTxs(); diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol index 4a2ad7170..daf155e3a 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IAdmin.sol @@ -6,6 +6,7 @@ import {IZKChainBase} from "../chain-interfaces/IZKChainBase.sol"; import {Diamond} from "../libraries/Diamond.sol"; import {FeeParams, PubdataPricingMode} from "../chain-deps/ZKChainStorage.sol"; +import {ZKChainCommitment} from "../../common/Config.sol"; /// @title The interface of the Admin Contract that controls access rights for contract management. /// @author Matter Labs @@ -149,4 +150,6 @@ interface IAdmin is IZKChainBase { /// @dev Similar to IL1AssetHandler interface, used to receive chains. function forwardedBridgeMint(bytes calldata _data, bool _contractAlreadyDeployed) external payable; + + function prepareChainCommitment() external view returns (ZKChainCommitment memory commitment); } diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol index c5f2bbc90..e175ac91f 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol @@ -12,7 +12,6 @@ import {FeeParams} from "../chain-deps/ZKChainStorage.sol"; /// @param validatorTimelock address of the validator timelock that delays execution /// @param admin address who can manage the contract /// @param baseTokenAssetId asset id of the base token of the chain -/// @param baseTokenBridge address of the L1 shared bridge contract /// @param storedBatchZero hash of the initial genesis batch /// @param verifier address of Verifier contract /// @param verifierParams Verifier config parameters that describes the circuit to be verified @@ -30,7 +29,6 @@ struct InitializeData { address admin; address validatorTimelock; bytes32 baseTokenAssetId; - address baseTokenBridge; bytes32 storedBatchZero; IVerifier verifier; VerifierParams verifierParams; diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol index 5dfd600ca..d2ee2b3d0 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol @@ -41,9 +41,6 @@ interface IGetters is IZKChainBase { /// @return The address of the base token function getBaseTokenAssetId() external view returns (bytes32); - /// @return The address of the base token bridge - function getBaseTokenBridge() external view returns (address); - /// @return The total number of batches that were committed function getTotalBatchesCommitted() external view returns (uint256); diff --git a/l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol b/l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol new file mode 100644 index 000000000..fdafe2807 --- /dev/null +++ b/l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {IL2ContractDeployer} from "../../common/interfaces/IL2ContractDeployer.sol"; + +interface IL2GatewayUpgrade { + function upgrade( + IL2ContractDeployer.ForceDeployment[] calldata _forceDeployments, + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) external payable; +} diff --git a/l1-contracts/contracts/upgrades/GatewayHelper.sol b/l1-contracts/contracts/upgrades/GatewayHelper.sol new file mode 100644 index 000000000..5ae02b9a0 --- /dev/null +++ b/l1-contracts/contracts/upgrades/GatewayHelper.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {IL1SharedBridgeLegacy} from "../bridge/interfaces/IL1SharedBridgeLegacy.sol"; +import {IBridgehub} from "../bridgehub/IBridgehub.sol"; + +import {ZKChainSpecificForceDeploymentsData} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; + +import {ZKChainStorage} from "../state-transition/chain-deps/ZKChainStorage.sol"; + +library GatewayHelper { + function getZKChainSpecificForceDeploymentsData(ZKChainStorage storage s) internal view returns (bytes memory) { + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + address legacySharedBridge = IL1SharedBridgeLegacy(sharedBridge).l2BridgeAddress(s.chainId); + ZKChainSpecificForceDeploymentsData + memory additionalForceDeploymentsData = ZKChainSpecificForceDeploymentsData({ + baseTokenAssetId: s.baseTokenAssetId, + l2LegacySharedBridge: legacySharedBridge, + l2Weth: address(0) // kl todo + }); + return abi.encode(additionalForceDeploymentsData); + } +} diff --git a/l1-contracts/contracts/upgrades/GatewayUpgrade.sol b/l1-contracts/contracts/upgrades/GatewayUpgrade.sol index 08d05989e..3420d81ae 100644 --- a/l1-contracts/contracts/upgrades/GatewayUpgrade.sol +++ b/l1-contracts/contracts/upgrades/GatewayUpgrade.sol @@ -2,8 +2,6 @@ pragma solidity 0.8.24; -import {Initializable} from "@openzeppelin/contracts-upgradeable-v4/proxy/utils/Initializable.sol"; - import {BaseZkSyncUpgrade, ProposedUpgrade} from "./BaseZkSyncUpgrade.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; @@ -13,14 +11,27 @@ import {PriorityQueue} from "../state-transition/libraries/PriorityQueue.sol"; import {PriorityTree} from "../state-transition/libraries/PriorityTree.sol"; import {IGatewayUpgrade} from "./IGatewayUpgrade.sol"; -import {IL1SharedBridgeLegacy} from "../bridge/interfaces/IL1SharedBridgeLegacy.sol"; +import {IComplexUpgrader} from "../state-transition/l2-deps/IComplexUpgrader.sol"; +import {IL2GatewayUpgrade} from "../state-transition/l2-deps/IL2GatewayUpgrade.sol"; + +import {IL2ContractDeployer} from "../common/interfaces/IL2ContractDeployer.sol"; -import {IBridgehub} from "../bridgehub/IBridgehub.sol"; +import {GatewayHelper} from "./GatewayHelper.sol"; + +// solhint-disable-next-line gas-struct-packing +struct GatewayUpgradeEncodedInput { + IL2ContractDeployer.ForceDeployment[] baseForceDeployments; + bytes fixedForceDeploymentsData; + address ctmDeployer; + address l2GatewayUpgrade; + address oldValidatorTimelock; + address newValidatorTimelock; +} /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice This upgrade will be used to migrate Era to be part of the ZK chain ecosystem contracts. -contract GatewayUpgrade is BaseZkSyncUpgrade, Initializable { +contract GatewayUpgrade is BaseZkSyncUpgrade { using PriorityQueue for PriorityQueue.Queue; using PriorityTree for PriorityTree.Tree; @@ -33,23 +44,34 @@ contract GatewayUpgrade is BaseZkSyncUpgrade, Initializable { /// @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) { - (bytes memory l2TxDataStart, bytes memory l2TxDataFinish) = abi.decode( + GatewayUpgradeEncodedInput memory encodedInput = abi.decode( _proposedUpgrade.postUpgradeCalldata, - (bytes, bytes) + (GatewayUpgradeEncodedInput) ); - s.baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, s.__DEPRECATED_baseToken); + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, s.__DEPRECATED_baseToken); + + s.baseTokenAssetId = baseTokenAssetId; s.priorityTree.setup(s.priorityQueue.getTotalPriorityTxs()); - IBridgehub bridgehub = IBridgehub(s.bridgehub); - s.baseTokenBridge = bridgehub.sharedBridge(); // we change the assetRouter - bridgehub.setLegacyBaseTokenAssetId(s.chainId); + s.validators[encodedInput.oldValidatorTimelock] = false; + s.validators[encodedInput.newValidatorTimelock] = true; ProposedUpgrade memory proposedUpgrade = _proposedUpgrade; - address l2LegacyBridge = IL1SharedBridgeLegacy(s.baseTokenBridge).l2BridgeAddress(s.chainId); - proposedUpgrade.l2ProtocolUpgradeTx.data = bytes.concat( - l2TxDataStart, - bytes32(uint256(uint160(l2LegacyBridge))), - l2TxDataFinish + + bytes memory gatewayUpgradeCalldata = abi.encodeCall( + IL2GatewayUpgrade.upgrade, + ( + encodedInput.baseForceDeployments, + encodedInput.ctmDeployer, + encodedInput.fixedForceDeploymentsData, + GatewayHelper.getZKChainSpecificForceDeploymentsData(s) + ) + ); + + proposedUpgrade.l2ProtocolUpgradeTx.data = abi.encodeCall( + IComplexUpgrader.upgrade, + (encodedInput.l2GatewayUpgrade, gatewayUpgradeCalldata) ); + // slither-disable-next-line controlled-delegatecall (bool success, ) = THIS_ADDRESS.delegatecall( abi.encodeWithSelector(IGatewayUpgrade.upgradeExternal.selector, proposedUpgrade) @@ -61,8 +83,6 @@ contract GatewayUpgrade is BaseZkSyncUpgrade, Initializable { /// @notice The function that will be called from this same contract, we need an external call to be able to modify _proposedUpgrade (memory/calldata). function upgradeExternal(ProposedUpgrade calldata _proposedUpgrade) external { - // solhint-disable-next-line gas-custom-errors - require(msg.sender == address(this), "GatewayUpgrade: upgradeExternal"); super.upgrade(_proposedUpgrade); } } diff --git a/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol b/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol index d6cb769c0..a6475f59a 100644 --- a/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol +++ b/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol @@ -8,15 +8,18 @@ import {Diamond} from "../state-transition/libraries/Diamond.sol"; import {BaseZkSyncUpgradeGenesis} from "./BaseZkSyncUpgradeGenesis.sol"; import {ProposedUpgrade} from "./IDefaultUpgrade.sol"; import {L2CanonicalTransaction} from "../common/Messaging.sol"; -import {IL2GenesisUpgrade, ZKChainSpecificForceDeploymentsData} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; +import {IL2GenesisUpgrade} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; import {IL1GenesisUpgrade} from "./IL1GenesisUpgrade.sol"; -import {IL1Nullifier} from "../bridge/interfaces/IL1Nullifier.sol"; -import {IL1AssetRouter} from "../bridge/asset-router/IL1AssetRouter.sol"; import {IComplexUpgrader} from "../state-transition/l2-deps/IComplexUpgrader.sol"; import {L2_FORCE_DEPLOYER_ADDR, L2_COMPLEX_UPGRADER_ADDR, L2_GENESIS_UPGRADE_ADDR} from "../common/L2ContractAddresses.sol"; //, COMPLEX_UPGRADER_ADDR, GENESIS_UPGRADE_ADDR import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, SYSTEM_UPGRADE_L2_TX_TYPE, PRIORITY_TX_MAX_GAS_LIMIT} from "../common/Config.sol"; import {SemVer} from "../common/libraries/SemVer.sol"; +import {IL1SharedBridgeLegacy} from "../bridge/interfaces/IL1SharedBridgeLegacy.sol"; +import {IBridgehub} from "../bridgehub/IBridgehub.sol"; + +import {ZKChainSpecificForceDeploymentsData} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; + import {VerifierParams} from "../state-transition/chain-interfaces/IVerifier.sol"; import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; @@ -106,8 +109,8 @@ contract L1GenesisUpgrade is IL1GenesisUpgrade, BaseZkSyncUpgradeGenesis { } function _getZKChainSpecificForceDeploymentsData() internal view returns (bytes memory) { - IL1Nullifier l1Nullifier = IL1AssetRouter(s.baseTokenBridge).L1_NULLIFIER(); - address legacySharedBridge = l1Nullifier.l2BridgeAddress(s.chainId); + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + address legacySharedBridge = IL1SharedBridgeLegacy(sharedBridge).l2BridgeAddress(s.chainId); ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = ZKChainSpecificForceDeploymentsData({ baseTokenAssetId: s.baseTokenAssetId, diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 1112d917e..3916cd7f3 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -56,125 +56,12 @@ import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.s import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; import {ValidiumL1DAValidator} from "contracts/state-transition/data-availability/ValidiumL1DAValidator.sol"; -struct FixedForceDeploymentsData { - uint256 l1ChainId; - uint256 eraChainId; - address l1AssetRouter; - bytes32 l2TokenProxyBytecodeHash; - address aliasedL1Governance; - uint256 maxNumberOfZKChains; - bytes32 bridgehubBytecodeHash; - bytes32 l2AssetRouterBytecodeHash; - bytes32 l2NtvBytecodeHash; - bytes32 messageRootBytecodeHash; -} +import {DeployUtils, GeneratedData, Config, DeployedAddresses, FixedForceDeploymentsData} from "./DeployUtils.s.sol"; -contract DeployL1Script is Script { +contract DeployL1Script is Script, DeployUtils { using stdToml for string; address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; - address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; - - // solhint-disable-next-line gas-struct-packing - struct DeployedAddresses { - BridgehubDeployedAddresses bridgehub; - StateTransitionDeployedAddresses stateTransition; - BridgesDeployedAddresses bridges; - L1NativeTokenVaultAddresses vaults; - DataAvailabilityDeployedAddresses daAddresses; - address transparentProxyAdmin; - address governance; - address chainAdmin; - address accessControlRestrictionAddress; - address blobVersionedHashRetriever; - address validatorTimelock; - address create2Factory; - } - - // solhint-disable-next-line gas-struct-packing - struct L1NativeTokenVaultAddresses { - address l1NativeTokenVaultImplementation; - address l1NativeTokenVaultProxy; - } - - struct DataAvailabilityDeployedAddresses { - address l1RollupDAValidator; - address l1ValidiumDAValidator; - } - - // solhint-disable-next-line gas-struct-packing - struct BridgehubDeployedAddresses { - address bridgehubImplementation; - address bridgehubProxy; - address ctmDeploymentTrackerImplementation; - address ctmDeploymentTrackerProxy; - address messageRootImplementation; - address messageRootProxy; - } - - // solhint-disable-next-line gas-struct-packing - struct BridgesDeployedAddresses { - address erc20BridgeImplementation; - address erc20BridgeProxy; - address sharedBridgeImplementation; - address sharedBridgeProxy; - address l1NullifierImplementation; - address l1NullifierProxy; - address bridgedStandardERC20Implementation; - address bridgedTokenBeacon; - } - - // solhint-disable-next-line gas-struct-packing - struct Config { - uint256 l1ChainId; - address deployerAddress; - uint256 eraChainId; - address ownerAddress; - bool testnetVerifier; - ContractsConfig contracts; - TokensConfig tokens; - } - - // solhint-disable-next-line gas-struct-packing - struct GeneratedData { - bytes forceDeploymentsData; - } - - // solhint-disable-next-line gas-struct-packing - struct ContractsConfig { - bytes32 create2FactorySalt; - address create2FactoryAddr; - address multicall3Addr; - uint256 validatorTimelockExecutionDelay; - bytes32 genesisRoot; - uint256 genesisRollupLeafIndex; - bytes32 genesisBatchCommitment; - uint256 latestProtocolVersion; - bytes32 recursionNodeLevelVkHash; - bytes32 recursionLeafLevelVkHash; - bytes32 recursionCircuitsSetVksHash; - uint256 priorityTxMaxGasLimit; - PubdataPricingMode diamondInitPubdataPricingMode; - uint256 diamondInitBatchOverheadL1Gas; - uint256 diamondInitMaxPubdataPerBatch; - uint256 diamondInitMaxL2GasPerBatch; - uint256 diamondInitPriorityTxMaxPubdata; - uint256 diamondInitMinimalL2GasPrice; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - uint256 maxNumberOfChains; - bytes diamondCutData; - bytes32 bootloaderHash; - bytes32 defaultAAHash; - } - - struct TokensConfig { - address tokenWethAddress; - } - - Config internal config; - GeneratedData internal generatedData; - DeployedAddresses internal addresses; function run() public { console.log("Deploying L1 contracts"); @@ -186,11 +73,20 @@ contract DeployL1Script is Script { runInner(vm.envString("L1_CONFIG"), vm.envString("L1_OUTPUT")); } + function getAddresses() public view returns (DeployedAddresses memory) { + return addresses; + } + + function getConfig() public view returns (Config memory) { + return config; + } + function runInner(string memory inputPath, string memory outputPath) internal { string memory root = vm.projectRoot(); inputPath = string.concat(root, inputPath); outputPath = string.concat(root, outputPath); + saveDiamondSelectors(); initializeConfig(inputPath); instantiateCreate2Factory(); @@ -225,6 +121,7 @@ contract DeployL1Script is Script { deployBlobVersionedHashRetriever(); deployChainTypeManagerContract(); + registerChainTypeManager(); setChainTypeManagerInValidatorTimelock(); updateOwners(); @@ -232,226 +129,38 @@ contract DeployL1Script is Script { saveOutput(outputPath); } - function getBridgehubProxyAddress() public view returns (address) { - return addresses.bridgehub.bridgehubProxy; - } - - function getSharedBridgeProxyAddress() public view returns (address) { - return addresses.bridges.sharedBridgeProxy; - } - - function getNativeTokenVaultProxyAddress() public view returns (address) { - return addresses.vaults.l1NativeTokenVaultProxy; - } - - function getL1NullifierProxyAddress() public view returns (address) { - return addresses.bridges.l1NullifierProxy; - } - - function getOwnerAddress() public view returns (address) { - return config.ownerAddress; - } - - function getCTM() public view returns (address) { - return addresses.stateTransition.chainTypeManagerProxy; - } - - function getInitialDiamondCutData() public view returns (bytes memory) { - return config.contracts.diamondCutData; - } - - function getCTMDeploymentTrackerAddress() public view returns (address) { - return addresses.bridgehub.ctmDeploymentTrackerProxy; - } - - function initializeConfig(string memory configPath) internal { - string memory toml = vm.readFile(configPath); - - config.l1ChainId = block.chainid; - config.deployerAddress = msg.sender; - - // Config file must be parsed key by key, otherwise values returned - // are parsed alfabetically and not by key. - // https://book.getfoundry.sh/cheatcodes/parse-toml - config.eraChainId = toml.readUint("$.era_chain_id"); - config.ownerAddress = toml.readAddress("$.owner_address"); - config.testnetVerifier = toml.readBool("$.testnet_verifier"); - - config.contracts.governanceSecurityCouncilAddress = toml.readAddress( - "$.contracts.governance_security_council_address" - ); - config.contracts.governanceMinDelay = toml.readUint("$.contracts.governance_min_delay"); - config.contracts.maxNumberOfChains = toml.readUint("$.contracts.max_number_of_chains"); - config.contracts.create2FactorySalt = toml.readBytes32("$.contracts.create2_factory_salt"); - if (vm.keyExistsToml(toml, "$.contracts.create2_factory_addr")) { - config.contracts.create2FactoryAddr = toml.readAddress("$.contracts.create2_factory_addr"); - } - config.contracts.validatorTimelockExecutionDelay = toml.readUint( - "$.contracts.validator_timelock_execution_delay" - ); - config.contracts.genesisRoot = toml.readBytes32("$.contracts.genesis_root"); - config.contracts.genesisRollupLeafIndex = toml.readUint("$.contracts.genesis_rollup_leaf_index"); - config.contracts.genesisBatchCommitment = toml.readBytes32("$.contracts.genesis_batch_commitment"); - config.contracts.latestProtocolVersion = toml.readUint("$.contracts.latest_protocol_version"); - config.contracts.recursionNodeLevelVkHash = toml.readBytes32("$.contracts.recursion_node_level_vk_hash"); - config.contracts.recursionLeafLevelVkHash = toml.readBytes32("$.contracts.recursion_leaf_level_vk_hash"); - config.contracts.recursionCircuitsSetVksHash = toml.readBytes32("$.contracts.recursion_circuits_set_vks_hash"); - config.contracts.priorityTxMaxGasLimit = toml.readUint("$.contracts.priority_tx_max_gas_limit"); - config.contracts.diamondInitPubdataPricingMode = PubdataPricingMode( - toml.readUint("$.contracts.diamond_init_pubdata_pricing_mode") - ); - config.contracts.diamondInitBatchOverheadL1Gas = toml.readUint( - "$.contracts.diamond_init_batch_overhead_l1_gas" - ); - config.contracts.diamondInitMaxPubdataPerBatch = toml.readUint( - "$.contracts.diamond_init_max_pubdata_per_batch" - ); - config.contracts.diamondInitMaxL2GasPerBatch = toml.readUint("$.contracts.diamond_init_max_l2_gas_per_batch"); - config.contracts.diamondInitPriorityTxMaxPubdata = toml.readUint( - "$.contracts.diamond_init_priority_tx_max_pubdata" - ); - config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); - config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); - config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); - - config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); - } - function initializeGeneratedData() internal { generatedData.forceDeploymentsData = prepareForceDeploymentsData(); } - function instantiateCreate2Factory() internal { - address contractAddress; - - bool isDeterministicDeployed = DETERMINISTIC_CREATE2_ADDRESS.code.length > 0; - bool isConfigured = config.contracts.create2FactoryAddr != address(0); - - if (isConfigured) { - if (config.contracts.create2FactoryAddr.code.length == 0) { - revert AddressHasNoCode(config.contracts.create2FactoryAddr); - } - contractAddress = config.contracts.create2FactoryAddr; - console.log("Using configured Create2Factory address:", contractAddress); - } else if (isDeterministicDeployed) { - contractAddress = DETERMINISTIC_CREATE2_ADDRESS; - console.log("Using deterministic Create2Factory address:", contractAddress); - } else { - contractAddress = Utils.deployCreate2Factory(); - console.log("Create2Factory deployed at:", contractAddress); - } - - addresses.create2Factory = contractAddress; - } - function deployIfNeededMulticall3() internal { // Multicall3 is already deployed on public networks if (MULTICALL3_ADDRESS.code.length == 0) { - address contractAddress = deployViaCreate2(type(Multicall3).creationCode); + address contractAddress = deployViaCreate2(type(Multicall3).creationCode, ""); console.log("Multicall3 deployed at:", contractAddress); config.contracts.multicall3Addr = contractAddress; } else { config.contracts.multicall3Addr = MULTICALL3_ADDRESS; } } - - function deployVerifier() internal { - bytes memory code; - if (config.testnetVerifier) { - code = type(TestnetVerifier).creationCode; - } else { - code = type(Verifier).creationCode; - } - address contractAddress = deployViaCreate2(code); - console.log("Verifier deployed at:", contractAddress); - addresses.stateTransition.verifier = contractAddress; - } - - function deployDefaultUpgrade() internal { - address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode); - console.log("DefaultUpgrade deployed at:", contractAddress); - addresses.stateTransition.defaultUpgrade = contractAddress; - } - - function deployGenesisUpgrade() internal { - bytes memory bytecode = abi.encodePacked(type(L1GenesisUpgrade).creationCode); - address contractAddress = deployViaCreate2(bytecode); - console.log("GenesisUpgrade deployed at:", contractAddress); - addresses.stateTransition.genesisUpgrade = contractAddress; - } - function deployDAValidators() internal { - address contractAddress = deployViaCreate2(Utils.readRollupDAValidatorBytecode()); + address contractAddress = deployViaCreate2(Utils.readRollupDAValidatorBytecode(), ""); console.log("L1RollupDAValidator deployed at:", contractAddress); addresses.daAddresses.l1RollupDAValidator = contractAddress; - contractAddress = deployViaCreate2(type(ValidiumL1DAValidator).creationCode); + contractAddress = deployViaCreate2(type(ValidiumL1DAValidator).creationCode, ""); console.log("L1ValidiumDAValidator deployed at:", contractAddress); addresses.daAddresses.l1ValidiumDAValidator = contractAddress; } - - function deployValidatorTimelock() internal { - uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); - bytes memory bytecode = abi.encodePacked( - type(ValidatorTimelock).creationCode, - abi.encode(config.deployerAddress, executionDelay, config.eraChainId) - ); - address contractAddress = deployViaCreate2(bytecode); - console.log("ValidatorTimelock deployed at:", contractAddress); - addresses.validatorTimelock = contractAddress; - } - - function deployGovernance() internal { - bytes memory bytecode = abi.encodePacked( - type(Governance).creationCode, - abi.encode( - config.ownerAddress, - config.contracts.governanceSecurityCouncilAddress, - config.contracts.governanceMinDelay - ) - ); - address contractAddress = deployViaCreate2(bytecode); - console.log("Governance deployed at:", contractAddress); - addresses.governance = contractAddress; - } - - function deployChainAdmin() internal { - bytes memory accessControlRestrictionBytecode = abi.encodePacked( - type(AccessControlRestriction).creationCode, - abi.encode(uint256(0), config.ownerAddress) - ); - - address accessControlRestriction = deployViaCreate2(accessControlRestrictionBytecode); - console.log("Access control restriction deployed at:", accessControlRestriction); - address[] memory restrictions = new address[](1); - restrictions[0] = accessControlRestriction; - addresses.accessControlRestrictionAddress = accessControlRestriction; - - bytes memory bytecode = abi.encodePacked(type(ChainAdmin).creationCode, abi.encode(restrictions)); - address contractAddress = deployViaCreate2(bytecode); - console.log("ChainAdmin deployed at:", contractAddress); - addresses.chainAdmin = contractAddress; - } - - function deployTransparentProxyAdmin() internal { - vm.startBroadcast(); - ProxyAdmin proxyAdmin = new ProxyAdmin(); - proxyAdmin.transferOwnership(addresses.governance); - vm.stopBroadcast(); - console.log("Transparent Proxy Admin deployed at:", address(proxyAdmin)); - addresses.transparentProxyAdmin = address(proxyAdmin); - } - function deployBridgehubContract() internal { - bytes memory bridgeHubBytecode = abi.encodePacked( + address bridgehubImplementation = deployViaCreate2( type(Bridgehub).creationCode, abi.encode(config.l1ChainId, config.ownerAddress, (config.contracts.maxNumberOfChains)) ); - address bridgehubImplementation = deployViaCreate2(bridgeHubBytecode); console.log("Bridgehub Implementation deployed at:", bridgehubImplementation); addresses.bridgehub.bridgehubImplementation = bridgehubImplementation; - bytes memory bytecode = abi.encodePacked( + address bridgehubProxy = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode( bridgehubImplementation, @@ -459,21 +168,19 @@ contract DeployL1Script is Script { abi.encodeCall(Bridgehub.initialize, (config.deployerAddress)) ) ); - address bridgehubProxy = deployViaCreate2(bytecode); console.log("Bridgehub Proxy deployed at:", bridgehubProxy); addresses.bridgehub.bridgehubProxy = bridgehubProxy; } function deployMessageRootContract() internal { - bytes memory messageRootBytecode = abi.encodePacked( + address messageRootImplementation = deployViaCreate2( type(MessageRoot).creationCode, abi.encode(addresses.bridgehub.bridgehubProxy) ); - address messageRootImplementation = deployViaCreate2(messageRootBytecode); console.log("MessageRoot Implementation deployed at:", messageRootImplementation); addresses.bridgehub.messageRootImplementation = messageRootImplementation; - bytes memory bytecode = abi.encodePacked( + address messageRootProxy = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode( messageRootImplementation, @@ -481,21 +188,19 @@ contract DeployL1Script is Script { abi.encodeCall(MessageRoot.initialize, ()) ) ); - address messageRootProxy = deployViaCreate2(bytecode); console.log("Message Root Proxy deployed at:", messageRootProxy); addresses.bridgehub.messageRootProxy = messageRootProxy; } function deployCTMDeploymentTracker() internal { - bytes memory ctmDTBytecode = abi.encodePacked( + address ctmDTImplementation = deployViaCreate2( type(CTMDeploymentTracker).creationCode, abi.encode(addresses.bridgehub.bridgehubProxy, addresses.bridges.sharedBridgeProxy) ); - address ctmDTImplementation = deployViaCreate2(ctmDTBytecode); console.log("CTM Deployment Tracker Implementation deployed at:", ctmDTImplementation); addresses.bridgehub.ctmDeploymentTrackerImplementation = ctmDTImplementation; - bytes memory bytecode = abi.encodePacked( + address ctmDTProxy = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode( ctmDTImplementation, @@ -503,7 +208,6 @@ contract DeployL1Script is Script { abi.encodeCall(CTMDeploymentTracker.initialize, (config.deployerAddress)) ) ); - address ctmDTProxy = deployViaCreate2(bytecode); console.log("CTM Deployment Tracker Proxy deployed at:", ctmDTProxy); addresses.bridgehub.ctmDeploymentTrackerProxy = ctmDTProxy; } @@ -511,144 +215,10 @@ contract DeployL1Script is Script { function deployBlobVersionedHashRetriever() internal { // solc contracts/state-transition/utils/blobVersionedHashRetriever.yul --strict-assembly --bin bytes memory bytecode = hex"600b600b5f39600b5ff3fe5f358049805f5260205ff3"; - address contractAddress = deployViaCreate2(bytecode); + address contractAddress = deployViaCreate2(bytecode, ""); console.log("BlobVersionedHashRetriever deployed at:", contractAddress); addresses.blobVersionedHashRetriever = contractAddress; } - - function deployChainTypeManagerContract() internal { - deployStateTransitionDiamondFacets(); - deployChainTypeManagerImplementation(); - deployChainTypeManagerProxy(); - registerChainTypeManager(); - } - - function deployStateTransitionDiamondFacets() internal { - address executorFacet = deployViaCreate2(type(ExecutorFacet).creationCode); - console.log("ExecutorFacet deployed at:", executorFacet); - addresses.stateTransition.executorFacet = executorFacet; - - 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, config.l1ChainId)) - ); - console.log("MailboxFacet deployed at:", mailboxFacet); - addresses.stateTransition.mailboxFacet = mailboxFacet; - - address gettersFacet = deployViaCreate2(type(GettersFacet).creationCode); - console.log("GettersFacet deployed at:", gettersFacet); - addresses.stateTransition.gettersFacet = gettersFacet; - - address diamondInit = deployViaCreate2(type(DiamondInit).creationCode); - console.log("DiamondInit deployed at:", diamondInit); - addresses.stateTransition.diamondInit = diamondInit; - } - - function deployChainTypeManagerImplementation() internal { - bytes memory bytecode = abi.encodePacked( - type(ChainTypeManager).creationCode, - abi.encode(addresses.bridgehub.bridgehubProxy) - ); - address contractAddress = deployViaCreate2(bytecode); - console.log("ChainTypeManagerImplementation deployed at:", contractAddress); - addresses.stateTransition.chainTypeManagerImplementation = contractAddress; - } - - function deployChainTypeManagerProxy() internal { - Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); - facetCuts[0] = Diamond.FacetCut({ - facet: addresses.stateTransition.adminFacet, - action: Diamond.Action.Add, - isFreezable: false, - selectors: Utils.getAllSelectors(addresses.stateTransition.adminFacet.code) - }); - facetCuts[1] = Diamond.FacetCut({ - facet: addresses.stateTransition.gettersFacet, - action: Diamond.Action.Add, - isFreezable: false, - selectors: Utils.getAllSelectors(addresses.stateTransition.gettersFacet.code) - }); - facetCuts[2] = Diamond.FacetCut({ - facet: addresses.stateTransition.mailboxFacet, - action: Diamond.Action.Add, - isFreezable: true, - selectors: Utils.getAllSelectors(addresses.stateTransition.mailboxFacet.code) - }); - facetCuts[3] = Diamond.FacetCut({ - facet: addresses.stateTransition.executorFacet, - action: Diamond.Action.Add, - isFreezable: true, - selectors: Utils.getAllSelectors(addresses.stateTransition.executorFacet.code) - }); - - VerifierParams memory verifierParams = VerifierParams({ - recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, - recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, - recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash - }); - - FeeParams memory feeParams = FeeParams({ - pubdataPricingMode: config.contracts.diamondInitPubdataPricingMode, - batchOverheadL1Gas: uint32(config.contracts.diamondInitBatchOverheadL1Gas), - maxPubdataPerBatch: uint32(config.contracts.diamondInitMaxPubdataPerBatch), - maxL2GasPerBatch: uint32(config.contracts.diamondInitMaxL2GasPerBatch), - priorityTxMaxPubdata: uint32(config.contracts.diamondInitPriorityTxMaxPubdata), - minimalL2GasPrice: uint64(config.contracts.diamondInitMinimalL2GasPrice) - }); - - DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ - verifier: IVerifier(addresses.stateTransition.verifier), - verifierParams: verifierParams, - l2BootloaderBytecodeHash: config.contracts.bootloaderHash, - l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, - priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, - feeParams: feeParams, - blobVersionedHashRetriever: addresses.blobVersionedHashRetriever - }); - - Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ - facetCuts: facetCuts, - initAddress: addresses.stateTransition.diamondInit, - initCalldata: abi.encode(initializeData) - }); - - config.contracts.diamondCutData = abi.encode(diamondCut); - - ChainCreationParams memory chainCreationParams = ChainCreationParams({ - genesisUpgrade: addresses.stateTransition.genesisUpgrade, - genesisBatchHash: config.contracts.genesisRoot, - genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), - genesisBatchCommitment: config.contracts.genesisBatchCommitment, - diamondCut: diamondCut, - forceDeploymentsData: generatedData.forceDeploymentsData - }); - - ChainTypeManagerInitializeData memory diamondInitData = ChainTypeManagerInitializeData({ - owner: msg.sender, - validatorTimelock: addresses.validatorTimelock, - chainCreationParams: chainCreationParams, - protocolVersion: config.contracts.latestProtocolVersion - }); - - address contractAddress = deployViaCreate2( - abi.encodePacked( - type(TransparentUpgradeableProxy).creationCode, - abi.encode( - addresses.stateTransition.chainTypeManagerImplementation, - addresses.transparentProxyAdmin, - abi.encodeCall(ChainTypeManager.initialize, (diamondInitData)) - ) - ) - ); - console.log("ChainTypeManagerProxy deployed at:", contractAddress); - addresses.stateTransition.chainTypeManagerProxy = contractAddress; - } - function registerChainTypeManager() internal { Bridgehub bridgehub = Bridgehub(addresses.bridgehub.bridgehubProxy); vm.startBroadcast(msg.sender); @@ -667,7 +237,7 @@ contract DeployL1Script is Script { vm.stopBroadcast(); console.log("CTM registered in CTMDeploymentTracker"); - bytes32 assetId = bridgehub.ctmAssetId(addresses.stateTransition.chainTypeManagerProxy); + bytes32 assetId = bridgehub.ctmAssetIdFromAddress(addresses.stateTransition.chainTypeManagerProxy); // console.log(address(bridgehub.ctmDeployer()), addresses.bridgehub.ctmDeploymentTrackerProxy); // console.log(address(bridgehub.ctmDeployer().BRIDGE_HUB()), addresses.bridgehub.bridgehubProxy); console.log( @@ -697,11 +267,10 @@ contract DeployL1Script is Script { initAddress: address(0), initCalldata: "" }); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(DiamondProxy).creationCode, abi.encode(config.l1ChainId, diamondCut) ); - address contractAddress = deployViaCreate2(bytecode); console.log("DiamondProxy deployed at:", contractAddress); addresses.stateTransition.diamondProxy = contractAddress; } @@ -718,29 +287,27 @@ contract DeployL1Script is Script { function deployL1NullifierImplementation() internal { // TODO(EVM-743): allow non-dev nullifier in the local deployment - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1NullifierDev).creationCode, // solhint-disable-next-line func-named-parameters abi.encode(addresses.bridgehub.bridgehubProxy, config.eraChainId, addresses.stateTransition.diamondProxy) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NullifierImplementation deployed at:", contractAddress); addresses.bridges.l1NullifierImplementation = contractAddress; } function deployL1NullifierProxy() internal { bytes memory initCalldata = abi.encodeCall(L1Nullifier.initialize, (config.deployerAddress, 1, 1, 1, 0)); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.bridges.l1NullifierImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NullifierProxy deployed at:", contractAddress); addresses.bridges.l1NullifierProxy = contractAddress; } function deploySharedBridgeImplementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1AssetRouter).creationCode, // solhint-disable-next-line func-named-parameters abi.encode( @@ -751,18 +318,16 @@ contract DeployL1Script is Script { addresses.stateTransition.diamondProxy ) ); - address contractAddress = deployViaCreate2(bytecode); console.log("SharedBridgeImplementation deployed at:", contractAddress); addresses.bridges.sharedBridgeImplementation = contractAddress; } function deploySharedBridgeProxy() internal { bytes memory initCalldata = abi.encodeCall(L1AssetRouter.initialize, (config.deployerAddress)); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.bridges.sharedBridgeImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("SharedBridgeProxy deployed at:", contractAddress); addresses.bridges.sharedBridgeProxy = contractAddress; } @@ -782,7 +347,7 @@ contract DeployL1Script is Script { } function deployErc20BridgeImplementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1ERC20Bridge).creationCode, abi.encode( addresses.bridges.l1NullifierProxy, @@ -791,18 +356,16 @@ contract DeployL1Script is Script { config.eraChainId ) ); - address contractAddress = deployViaCreate2(bytecode); console.log("Erc20BridgeImplementation deployed at:", contractAddress); addresses.bridges.erc20BridgeImplementation = contractAddress; } function deployErc20BridgeProxy() internal { bytes memory initCalldata = abi.encodeCall(L1ERC20Bridge.initialize, ()); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.bridges.erc20BridgeImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("Erc20BridgeProxy deployed at:", contractAddress); addresses.bridges.erc20BridgeProxy = contractAddress; } @@ -815,22 +378,16 @@ contract DeployL1Script is Script { } function deployBridgedStandardERC20Implementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(BridgedStandardERC20).creationCode, // solhint-disable-next-line func-named-parameters abi.encode() ); - address contractAddress = deployViaCreate2(bytecode); console.log("BridgedStandardERC20Implementation deployed at:", contractAddress); addresses.bridges.bridgedStandardERC20Implementation = contractAddress; } function deployBridgedTokenBeacon() internal { - bytes memory bytecode = abi.encodePacked( - type(UpgradeableBeacon).creationCode, - // solhint-disable-next-line func-named-parameters - abi.encode(addresses.bridges.bridgedStandardERC20Implementation) - ); UpgradeableBeacon beacon = new UpgradeableBeacon(addresses.bridges.bridgedStandardERC20Implementation); address contractAddress = address(beacon); beacon.transferOwnership(config.ownerAddress); @@ -839,7 +396,7 @@ contract DeployL1Script is Script { } function deployL1NativeTokenVaultImplementation() internal { - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(L1NativeTokenVault).creationCode, // solhint-disable-next-line func-named-parameters abi.encode( @@ -849,7 +406,6 @@ contract DeployL1Script is Script { addresses.bridges.l1NullifierProxy ) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NativeTokenVaultImplementation deployed at:", contractAddress); addresses.vaults.l1NativeTokenVaultImplementation = contractAddress; } @@ -859,11 +415,10 @@ contract DeployL1Script is Script { L1NativeTokenVault.initialize, (config.ownerAddress, addresses.bridges.bridgedTokenBeacon) ); - bytes memory bytecode = abi.encodePacked( + address contractAddress = deployViaCreate2( type(TransparentUpgradeableProxy).creationCode, abi.encode(addresses.vaults.l1NativeTokenVaultImplementation, addresses.transparentProxyAdmin, initCalldata) ); - address contractAddress = deployViaCreate2(bytecode); console.log("L1NativeTokenVaultProxy deployed at:", contractAddress); addresses.vaults.l1NativeTokenVaultProxy = contractAddress; @@ -916,6 +471,36 @@ contract DeployL1Script is Script { console.log("Owners updated"); } + function saveDiamondSelectors() public { + AdminFacet adminFacet = new AdminFacet(1); + GettersFacet gettersFacet = new GettersFacet(); + MailboxFacet mailboxFacet = new MailboxFacet(1, 1); + ExecutorFacet executorFacet = new ExecutorFacet(); + bytes4[] memory adminFacetSelectors = Utils.getAllSelectors(address(adminFacet).code); + bytes4[] memory gettersFacetSelectors = Utils.getAllSelectors(address(gettersFacet).code); + bytes4[] memory mailboxFacetSelectors = Utils.getAllSelectors(address(mailboxFacet).code); + bytes4[] memory executorFacetSelectors = Utils.getAllSelectors(address(executorFacet).code); + + string memory root = vm.projectRoot(); + string memory outputPath = string.concat(root, "/script-out/diamond-selectors.toml"); + + bytes memory adminFacetSelectorsBytes = abi.encode(adminFacetSelectors); + bytes memory gettersFacetSelectorsBytes = abi.encode(gettersFacetSelectors); + bytes memory mailboxFacetSelectorsBytes = abi.encode(mailboxFacetSelectors); + bytes memory executorFacetSelectorsBytes = abi.encode(executorFacetSelectors); + + vm.serializeBytes("diamond_selectors", "admin_facet_selectors", adminFacetSelectorsBytes); + vm.serializeBytes("diamond_selectors", "getters_facet_selectors", gettersFacetSelectorsBytes); + vm.serializeBytes("diamond_selectors", "mailbox_facet_selectors", mailboxFacetSelectorsBytes); + string memory toml = vm.serializeBytes( + "diamond_selectors", + "executor_facet_selectors", + executorFacetSelectorsBytes + ); + + vm.writeToml(toml, outputPath); + } + function saveOutput(string memory outputPath) internal { vm.serializeAddress("bridgehub", "bridgehub_proxy_addr", addresses.bridgehub.bridgehubProxy); vm.serializeAddress("bridgehub", "bridgehub_implementation_addr", addresses.bridgehub.bridgehubImplementation); @@ -1079,10 +664,6 @@ contract DeployL1Script is Script { vm.writeToml(toml, outputPath); } - function deployViaCreate2(bytes memory _bytecode) internal returns (address) { - return Utils.deployViaCreate2(_bytecode, config.contracts.create2FactorySalt, addresses.create2Factory); - } - function prepareForceDeploymentsData() internal view returns (bytes memory) { require(addresses.governance != address(0), "Governance address is not set"); @@ -1102,12 +683,17 @@ contract DeployL1Script is Script { l2NtvBytecodeHash: L2ContractHelper.hashL2Bytecode( L2ContractsBytecodesLib.readL2NativeTokenVaultBytecode() ), - messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()) + messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()), + // For newly created chains it it is expected that the following bridges are not present + l2SharedBridgeLegacyImpl: address(0), + l2BridgedStandardERC20Impl: address(0), + l2BridgeProxyOwnerAddress: address(0), + l2BridgedStandardERC20ProxyOwnerAddress: address(0) }); return abi.encode(data); } // add this to be excluded from coverage report - function test() internal {} + function test() internal virtual override {} } diff --git a/l1-contracts/deploy-scripts/DeployUtils.s.sol b/l1-contracts/deploy-scripts/DeployUtils.s.sol new file mode 100644 index 000000000..33f386cf9 --- /dev/null +++ b/l1-contracts/deploy-scripts/DeployUtils.s.sol @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {StateTransitionDeployedAddresses, Utils, L2_BRIDGEHUB_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT_ADDRESS} from "./Utils.sol"; +import {Multicall3} from "contracts/dev-contracts/Multicall3.sol"; +import {Verifier} from "contracts/state-transition/Verifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; +import {Governance} from "contracts/governance/Governance.sol"; +import {L1GenesisUpgrade} from "contracts/upgrades/L1GenesisUpgrade.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; +import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; +import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; +import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; +import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; +import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; +import {ChainTypeManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IChainTypeManager.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; +import {InitializeDataNewChain as DiamondInitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; +import {FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1ERC20Bridge} from "contracts/bridge/L1ERC20Bridge.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {AddressHasNoCode} from "./ZkSyncScriptErrors.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IL2ContractDeployer} from "contracts/common/interfaces/IL2ContractDeployer.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {IL1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; +import {L1NullifierDev} from "contracts/dev-contracts/L1NullifierDev.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; + +struct FixedForceDeploymentsData { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + bytes32 l2TokenProxyBytecodeHash; + address aliasedL1Governance; + uint256 maxNumberOfZKChains; + bytes32 bridgehubBytecodeHash; + bytes32 l2AssetRouterBytecodeHash; + bytes32 l2NtvBytecodeHash; + bytes32 messageRootBytecodeHash; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; +} + +// solhint-disable-next-line gas-struct-packing +struct DeployedAddresses { + BridgehubDeployedAddresses bridgehub; + StateTransitionDeployedAddresses stateTransition; + BridgesDeployedAddresses bridges; + L1NativeTokenVaultAddresses vaults; + DataAvailabilityDeployedAddresses daAddresses; + address transparentProxyAdmin; + address governance; + address chainAdmin; + address accessControlRestrictionAddress; + address blobVersionedHashRetriever; + address validatorTimelock; + address create2Factory; +} + +// solhint-disable-next-line gas-struct-packing +struct L1NativeTokenVaultAddresses { + address l1NativeTokenVaultImplementation; + address l1NativeTokenVaultProxy; +} + +struct DataAvailabilityDeployedAddresses { + address l1RollupDAValidator; + address l1ValidiumDAValidator; +} + +// solhint-disable-next-line gas-struct-packing +struct BridgehubDeployedAddresses { + address bridgehubImplementation; + address bridgehubProxy; + address ctmDeploymentTrackerImplementation; + address ctmDeploymentTrackerProxy; + address messageRootImplementation; + address messageRootProxy; +} + +// solhint-disable-next-line gas-struct-packing +struct BridgesDeployedAddresses { + address erc20BridgeImplementation; + address erc20BridgeProxy; + address sharedBridgeImplementation; + address sharedBridgeProxy; + address l1NullifierImplementation; + address l1NullifierProxy; + address bridgedStandardERC20Implementation; + address bridgedTokenBeacon; +} + +// solhint-disable-next-line gas-struct-packing +struct Config { + uint256 l1ChainId; + address deployerAddress; + uint256 eraChainId; + address ownerAddress; + bool testnetVerifier; + ContractsConfig contracts; + TokensConfig tokens; +} + +// solhint-disable-next-line gas-struct-packing +struct ContractsConfig { + bytes32 create2FactorySalt; + address create2FactoryAddr; + address multicall3Addr; + uint256 validatorTimelockExecutionDelay; + bytes32 genesisRoot; + uint256 genesisRollupLeafIndex; + bytes32 genesisBatchCommitment; + uint256 latestProtocolVersion; + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; + uint256 priorityTxMaxGasLimit; + PubdataPricingMode diamondInitPubdataPricingMode; + uint256 diamondInitBatchOverheadL1Gas; + uint256 diamondInitMaxPubdataPerBatch; + uint256 diamondInitMaxL2GasPerBatch; + uint256 diamondInitPriorityTxMaxPubdata; + uint256 diamondInitMinimalL2GasPrice; + address governanceSecurityCouncilAddress; + uint256 governanceMinDelay; + uint256 maxNumberOfChains; + bytes diamondCutData; + bytes32 bootloaderHash; + bytes32 defaultAAHash; +} + +struct TokensConfig { + address tokenWethAddress; +} + +// solhint-disable-next-line gas-struct-packing +struct GeneratedData { + bytes forceDeploymentsData; +} + +contract DeployUtils is Script { + using stdToml for string; + + address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + + Config public config; + GeneratedData internal generatedData; + DeployedAddresses internal addresses; + + function initializeConfig(string memory configPath) internal { + string memory toml = vm.readFile(configPath); + + config.l1ChainId = block.chainid; + config.deployerAddress = msg.sender; + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + config.eraChainId = toml.readUint("$.era_chain_id"); + config.ownerAddress = toml.readAddress("$.owner_address"); + config.testnetVerifier = toml.readBool("$.testnet_verifier"); + + config.contracts.governanceSecurityCouncilAddress = toml.readAddress( + "$.contracts.governance_security_council_address" + ); + config.contracts.governanceMinDelay = toml.readUint("$.contracts.governance_min_delay"); + config.contracts.maxNumberOfChains = toml.readUint("$.contracts.max_number_of_chains"); + config.contracts.create2FactorySalt = toml.readBytes32("$.contracts.create2_factory_salt"); + if (vm.keyExistsToml(toml, "$.contracts.create2_factory_addr")) { + config.contracts.create2FactoryAddr = toml.readAddress("$.contracts.create2_factory_addr"); + } + config.contracts.validatorTimelockExecutionDelay = toml.readUint( + "$.contracts.validator_timelock_execution_delay" + ); + config.contracts.genesisRoot = toml.readBytes32("$.contracts.genesis_root"); + config.contracts.genesisRollupLeafIndex = toml.readUint("$.contracts.genesis_rollup_leaf_index"); + config.contracts.genesisBatchCommitment = toml.readBytes32("$.contracts.genesis_batch_commitment"); + config.contracts.latestProtocolVersion = toml.readUint("$.contracts.latest_protocol_version"); + config.contracts.recursionNodeLevelVkHash = toml.readBytes32("$.contracts.recursion_node_level_vk_hash"); + config.contracts.recursionLeafLevelVkHash = toml.readBytes32("$.contracts.recursion_leaf_level_vk_hash"); + config.contracts.recursionCircuitsSetVksHash = toml.readBytes32("$.contracts.recursion_circuits_set_vks_hash"); + config.contracts.priorityTxMaxGasLimit = toml.readUint("$.contracts.priority_tx_max_gas_limit"); + config.contracts.diamondInitPubdataPricingMode = PubdataPricingMode( + toml.readUint("$.contracts.diamond_init_pubdata_pricing_mode") + ); + config.contracts.diamondInitBatchOverheadL1Gas = toml.readUint( + "$.contracts.diamond_init_batch_overhead_l1_gas" + ); + config.contracts.diamondInitMaxPubdataPerBatch = toml.readUint( + "$.contracts.diamond_init_max_pubdata_per_batch" + ); + config.contracts.diamondInitMaxL2GasPerBatch = toml.readUint("$.contracts.diamond_init_max_l2_gas_per_batch"); + config.contracts.diamondInitPriorityTxMaxPubdata = toml.readUint( + "$.contracts.diamond_init_priority_tx_max_pubdata" + ); + config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); + config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); + config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); + + config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); + } + + function instantiateCreate2Factory() internal { + address contractAddress; + + bool isDeterministicDeployed = DETERMINISTIC_CREATE2_ADDRESS.code.length > 0; + bool isConfigured = config.contracts.create2FactoryAddr != address(0); + + if (isConfigured) { + if (config.contracts.create2FactoryAddr.code.length == 0) { + revert AddressHasNoCode(config.contracts.create2FactoryAddr); + } + contractAddress = config.contracts.create2FactoryAddr; + console.log("Using configured Create2Factory address:", contractAddress); + } else if (isDeterministicDeployed) { + contractAddress = DETERMINISTIC_CREATE2_ADDRESS; + console.log("Using deterministic Create2Factory address:", contractAddress); + } else { + contractAddress = Utils.deployCreate2Factory(); + console.log("Create2Factory deployed at:", contractAddress); + } + + addresses.create2Factory = contractAddress; + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal virtual returns (address) { + return + Utils.deployViaCreate2( + abi.encodePacked(creationCode, constructorArgs), + config.contracts.create2FactorySalt, + addresses.create2Factory + ); + } + + function deployVerifier() internal { + bytes memory code; + if (config.testnetVerifier) { + code = type(TestnetVerifier).creationCode; + } else { + code = type(Verifier).creationCode; + } + address contractAddress = deployViaCreate2(code, ""); + console.log("Verifier deployed at:", contractAddress); + addresses.stateTransition.verifier = contractAddress; + } + + function deployDefaultUpgrade() internal { + address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode, ""); + console.log("DefaultUpgrade deployed at:", contractAddress); + addresses.stateTransition.defaultUpgrade = contractAddress; + } + + function deployGenesisUpgrade() internal { + address contractAddress = deployViaCreate2(type(L1GenesisUpgrade).creationCode, ""); + console.log("GenesisUpgrade deployed at:", contractAddress); + addresses.stateTransition.genesisUpgrade = contractAddress; + } + + function deployValidatorTimelock() internal { + uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); + address contractAddress = deployViaCreate2( + type(ValidatorTimelock).creationCode, + abi.encode(config.deployerAddress, executionDelay, config.eraChainId) + ); + console.log("ValidatorTimelock deployed at:", contractAddress); + addresses.validatorTimelock = contractAddress; + } + + function deployGovernance() internal { + address contractAddress = deployViaCreate2( + type(Governance).creationCode, + abi.encode( + config.ownerAddress, + config.contracts.governanceSecurityCouncilAddress, + config.contracts.governanceMinDelay + ) + ); + console.log("Governance deployed at:", contractAddress); + addresses.governance = contractAddress; + } + + function deployChainAdmin() internal { + address accessControlRestriction = deployViaCreate2( + type(AccessControlRestriction).creationCode, + abi.encode(uint256(0), config.ownerAddress) + ); + + console.log("Access control restriction deployed at:", accessControlRestriction); + address[] memory restrictions = new address[](1); + restrictions[0] = accessControlRestriction; + addresses.accessControlRestrictionAddress = accessControlRestriction; + + address contractAddress = deployViaCreate2(type(ChainAdmin).creationCode, abi.encode(restrictions)); + console.log("ChainAdmin deployed at:", contractAddress); + addresses.chainAdmin = contractAddress; + } + + function deployTransparentProxyAdmin() internal { + vm.startBroadcast(); + ProxyAdmin proxyAdmin = new ProxyAdmin(); + proxyAdmin.transferOwnership(addresses.governance); + vm.stopBroadcast(); + console.log("Transparent Proxy Admin deployed at:", address(proxyAdmin)); + addresses.transparentProxyAdmin = address(proxyAdmin); + } + + function deployChainTypeManagerContract() internal { + deployStateTransitionDiamondFacets(); + deployChainTypeManagerImplementation(); + deployChainTypeManagerProxy(); + } + + function deployStateTransitionDiamondFacets() internal { + address executorFacet = deployViaCreate2(type(ExecutorFacet).creationCode, abi.encode()); + console.log("ExecutorFacet deployed at:", executorFacet); + addresses.stateTransition.executorFacet = executorFacet; + + address adminFacet = deployViaCreate2(type(AdminFacet).creationCode, abi.encode(config.l1ChainId)); + console.log("AdminFacet deployed at:", adminFacet); + addresses.stateTransition.adminFacet = adminFacet; + + address mailboxFacet = deployViaCreate2( + type(MailboxFacet).creationCode, + abi.encode(config.eraChainId, config.l1ChainId) + ); + console.log("MailboxFacet deployed at:", mailboxFacet); + addresses.stateTransition.mailboxFacet = mailboxFacet; + + address gettersFacet = deployViaCreate2(type(GettersFacet).creationCode, ""); + console.log("GettersFacet deployed at:", gettersFacet); + addresses.stateTransition.gettersFacet = gettersFacet; + + address diamondInit = deployViaCreate2(type(DiamondInit).creationCode, ""); + console.log("DiamondInit deployed at:", diamondInit); + addresses.stateTransition.diamondInit = diamondInit; + } + + function deployChainTypeManagerImplementation() internal { + bytes memory bytecode = type(ChainTypeManager).creationCode; + bytes memory constructorArgs = abi.encode(addresses.bridgehub.bridgehubProxy); + address contractAddress = deployViaCreate2(bytecode, constructorArgs); + console.log("ChainTypeManagerImplementation deployed at:", contractAddress); + addresses.stateTransition.chainTypeManagerImplementation = contractAddress; + } + + function deployChainTypeManagerProxy() internal { + string memory root = vm.projectRoot(); + string memory inputPath = string.concat(root, "/script-out/diamond-selectors.toml"); + string memory toml = vm.readFile(inputPath); + + bytes memory adminFacetSelectors = toml.readBytes("$.admin_facet_selectors"); + bytes memory gettersFacetSelectors = toml.readBytes("$.getters_facet_selectors"); + bytes memory mailboxFacetSelectors = toml.readBytes("$.mailbox_facet_selectors"); + bytes memory executorFacetSelectors = toml.readBytes("$.executor_facet_selectors"); + + bytes4[] memory adminFacetSelectorsArray = abi.decode(adminFacetSelectors, (bytes4[])); + bytes4[] memory gettersFacetSelectorsArray = abi.decode(gettersFacetSelectors, (bytes4[])); + bytes4[] memory mailboxFacetSelectorsArray = abi.decode(mailboxFacetSelectors, (bytes4[])); + bytes4[] memory executorFacetSelectorsArray = abi.decode(executorFacetSelectors, (bytes4[])); + + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); + facetCuts[0] = Diamond.FacetCut({ + facet: addresses.stateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: adminFacetSelectorsArray + }); + facetCuts[1] = Diamond.FacetCut({ + facet: addresses.stateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: gettersFacetSelectorsArray + }); + facetCuts[2] = Diamond.FacetCut({ + facet: addresses.stateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: mailboxFacetSelectorsArray + }); + facetCuts[3] = Diamond.FacetCut({ + facet: addresses.stateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: executorFacetSelectorsArray + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash + }); + + FeeParams memory feeParams = FeeParams({ + pubdataPricingMode: config.contracts.diamondInitPubdataPricingMode, + batchOverheadL1Gas: uint32(config.contracts.diamondInitBatchOverheadL1Gas), + maxPubdataPerBatch: uint32(config.contracts.diamondInitMaxPubdataPerBatch), + maxL2GasPerBatch: uint32(config.contracts.diamondInitMaxL2GasPerBatch), + priorityTxMaxPubdata: uint32(config.contracts.diamondInitPriorityTxMaxPubdata), + minimalL2GasPrice: uint64(config.contracts.diamondInitMinimalL2GasPrice) + }); + + DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ + verifier: IVerifier(addresses.stateTransition.verifier), + verifierParams: verifierParams, + l2BootloaderBytecodeHash: config.contracts.bootloaderHash, + l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, + priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, + feeParams: feeParams, + blobVersionedHashRetriever: addresses.blobVersionedHashRetriever + }); + + Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: addresses.stateTransition.diamondInit, + initCalldata: abi.encode(initializeData) + }); + + config.contracts.diamondCutData = abi.encode(diamondCut); + + ChainCreationParams memory chainCreationParams = ChainCreationParams({ + genesisUpgrade: addresses.stateTransition.genesisUpgrade, + genesisBatchHash: config.contracts.genesisRoot, + genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), + genesisBatchCommitment: config.contracts.genesisBatchCommitment, + diamondCut: diamondCut, + forceDeploymentsData: generatedData.forceDeploymentsData + }); + + ChainTypeManagerInitializeData memory diamondInitData = ChainTypeManagerInitializeData({ + owner: msg.sender, + validatorTimelock: addresses.validatorTimelock, + chainCreationParams: chainCreationParams, + protocolVersion: config.contracts.latestProtocolVersion + }); + + address contractAddress = deployViaCreate2( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + addresses.stateTransition.chainTypeManagerImplementation, + addresses.transparentProxyAdmin, + abi.encodeCall(ChainTypeManager.initialize, (diamondInitData)) + ) + ); + console.log("ChainTypeManagerProxy deployed at:", contractAddress); + addresses.stateTransition.chainTypeManagerProxy = contractAddress; + } + + function test() internal virtual {} +} diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 91601d9bd..cf6220d5b 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -22,6 +22,31 @@ import {GatewayTransactionFilterer} from "contracts/transactionFilterer/GatewayT import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {L2AssetRouter, IL2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {FinalizeL1DepositParams} from "contracts/bridge/interfaces/IL1Nullifier.sol"; + +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; + +// solhint-disable-next-line gas-struct-packing +struct Config { + address bridgehub; + address ctmDeploymentTracker; + address chainTypeManagerProxy; + address sharedBridgeProxy; + address governance; + uint256 gatewayChainId; + address gatewayChainAdmin; + address gatewayAccessControlRestriction; + address gatewayChainProxyAdmin; + address l1NullifierProxy; + bytes gatewayDiamondCutData; + bytes l1DiamondCutData; +} /// @notice Scripts that is responsible for preparing the chain to become a gateway contract GatewayPreparation is Script { @@ -33,20 +58,6 @@ contract GatewayPreparation is Script { address deployerAddress; uint256 l1ChainId; - // solhint-disable-next-line gas-struct-packing - struct Config { - address bridgehub; - address ctmDeploymentTracker; - address chainTypeManagerProxy; - address sharedBridgeProxy; - address governance; - uint256 gatewayChainId; - address gatewayChainAdmin; - address gatewayAccessControlRestriction; - address gatewayChainProxyAdmin; - bytes gatewayDiamondCutData; - } - struct Output { bytes32 governanceL2TxHash; address gatewayTransactionFiltererImplementation; @@ -70,7 +81,7 @@ contract GatewayPreparation is Script { l1ChainId = block.chainid; string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-config/gateway-preparation-l1.toml"); + string memory path = string.concat(root, vm.envString("GATEWAY_PREPARATION_L1_CONFIG")); string memory toml = vm.readFile(path); // Config file must be parsed key by key, otherwise values returned @@ -87,9 +98,11 @@ contract GatewayPreparation is Script { gatewayChainId: toml.readUint("$.chain_chain_id"), governance: toml.readAddress("$.governance"), gatewayDiamondCutData: toml.readBytes("$.gateway_diamond_cut_data"), + l1DiamondCutData: toml.readBytes("$.l1_diamond_cut_data"), gatewayChainAdmin: toml.readAddress("$.chain_admin"), gatewayAccessControlRestriction: toml.readAddress("$.access_control_restriction"), - gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin") + gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin"), + l1NullifierProxy: toml.readAddress("$.l1_nullifier_proxy_addr") }); } @@ -177,7 +190,7 @@ contract GatewayPreparation is Script { function governanceSetCTMAssetHandler(bytes32 governanoceOperationSalt) public { initializeConfig(); - bytes32 assetId = IBridgehub(config.bridgehub).ctmAssetId(config.chainTypeManagerProxy); + bytes32 assetId = IBridgehub(config.bridgehub).ctmAssetIdFromAddress(config.chainTypeManagerProxy); // This should be equivalent to `config.chainTypeManagerProxy`, but we just double checking to ensure that // bridgehub was initialized correctly @@ -240,7 +253,7 @@ contract GatewayPreparation is Script { uint256 currentSettlementLayer = IBridgehub(config.bridgehub).settlementLayer(chainId); if (currentSettlementLayer == config.gatewayChainId) { - console.log("Chain already whitelisted as settlement layer"); + console.log("Chain already using gateway as its settlement layer"); saveOutput(bytes32(0)); return; } @@ -272,6 +285,82 @@ contract GatewayPreparation is Script { saveOutput(l2TxHash); } + /// @dev Calling this function requires private key to the admin of the chain + function startMigrateChainFromGateway( + address chainAdmin, + address accessControlRestriction, + uint256 chainId + ) public { + initializeConfig(); + IBridgehub bridgehub = IBridgehub(config.bridgehub); + + uint256 currentSettlementLayer = bridgehub.settlementLayer(chainId); + if (currentSettlementLayer != config.gatewayChainId) { + console.log("Chain not using Gateway as settlement layer"); + saveOutput(bytes32(0)); + return; + } + + bytes memory bridgehubBurnData = abi.encode( + BridgehubBurnCTMAssetData({ + chainId: chainId, + ctmData: abi.encode(chainAdmin, config.l1DiamondCutData), + chainData: abi.encode(IChainTypeManager(config.chainTypeManagerProxy).getProtocolVersion(chainId)) + }) + ); + + bytes32 ctmAssetId = bridgehub.ctmAssetIdFromChainId(chainId); + L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); + bytes memory l2Calldata = abi.encodeCall(IL2AssetRouter.withdraw, (ctmAssetId, bridgehubBurnData)); + // vm.startBroadcast(); + bytes32 l2TxHash = Utils.runAdminL1L2DirectTransaction( + _getL1GasPrice(), + chainAdmin, + accessControlRestriction, + l2Calldata, + Utils.MAX_PRIORITY_TX_GAS, + new bytes[](0), + L2_ASSET_ROUTER_ADDR, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy + ); + + saveOutput(l2TxHash); + } + + function finishMigrateChainFromGateway( + uint256 migratingChainId, + uint256 gatewayChainId, + uint256 l2BatchNumber, + uint256 l2MessageIndex, + uint16 l2TxNumberInBatch, + bytes memory message, + bytes32[] memory merkleProof + ) public { + initializeConfig(); + + // TODO(EVM-746): Use L2-based chain admin contract + // address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); + + L1Nullifier l1Nullifier = L1Nullifier(config.l1NullifierProxy); + { + IBridgehub bridgehub = IBridgehub(config.bridgehub); + bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); + l1Nullifier.finalizeDeposit( + FinalizeL1DepositParams({ + chainId: gatewayChainId, + l2BatchNumber: l2BatchNumber, + l2MessageIndex: l2MessageIndex, + l2Sender: L2_ASSET_ROUTER_ADDR, + l2TxNumberInBatch: l2TxNumberInBatch, + message: message, + merkleProof: merkleProof + }) + ); + } + } + /// @dev Calling this function requires private key to the admin of the chain function setDAValidatorPair( address chainAdmin, diff --git a/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol b/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol index b9481c578..9b672deb2 100644 --- a/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol +++ b/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol @@ -259,4 +259,47 @@ library L2ContractsBytecodesLib { "/../l1-contracts/artifacts-zk/contracts/bridge/L2SharedBridgeLegacy.sol/L2SharedBridgeLegacy.json" ); } + + /// @notice Reads the bytecode of the L2GatewayUpgrade contract. + /// @return The bytecode of the L2GatewayUpgrade contract. + function readGatewayUpgradeBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../system-contracts/artifacts-zk/contracts-preprocessed/L2GatewayUpgrade.sol/L2GatewayUpgrade.json" + ); + } + + /// @notice Reads the bytecode of the L2GatewayUpgrade contract. + /// @return The bytecode of the L2GatewayUpgrade contract. + function readL2AdminFactoryBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/governance/L2AdminFactory.sol/L2AdminFactory.json" + ); + } + + function readProxyAdminBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json" + ); + } + + /// @notice Reads the bytecode of the L2GatewayUpgrade contract. + /// @return The bytecode of the L2GatewayUpgrade contract. + function readPermanentRestrictionBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/governance/PermanentRestriction.sol/PermanentRestriction.json" + ); + } + + /// @notice Reads the bytecode of the L2ProxyAdminDeployer contract. + /// @return The bytecode of the L2ProxyAdminDeployer contract. + function readProxyAdminDeployerBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/governance/L2ProxyAdminDeployer.sol/L2ProxyAdminDeployer.json" + ); + } } diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index 3a374e674..b1c8ae292 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -28,38 +28,38 @@ import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; +// solhint-disable-next-line gas-struct-packing +struct Config { + address deployerAddress; + address ownerAddress; + uint256 chainChainId; + bool validiumMode; + uint256 bridgehubCreateNewChainSalt; + address validatorSenderOperatorCommitEth; + address validatorSenderOperatorBlobsEth; + address baseToken; + bytes32 baseTokenAssetId; + uint128 baseTokenGasPriceMultiplierNominator; + uint128 baseTokenGasPriceMultiplierDenominator; + address bridgehub; + // TODO(EVM-744): maybe rename to asset router + address sharedBridgeProxy; + address nativeTokenVault; + address chainTypeManagerProxy; + address validatorTimelock; + bytes diamondCutData; + bytes forceDeployments; + address governanceSecurityCouncilAddress; + uint256 governanceMinDelay; + address l1Nullifier; +} + contract RegisterZKChainScript is Script { using stdToml for string; address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; bytes32 internal constant STATE_TRANSITION_NEW_CHAIN_HASH = keccak256("NewZKChain(uint256,address)"); - // solhint-disable-next-line gas-struct-packing - struct Config { - address deployerAddress; - address ownerAddress; - uint256 chainChainId; - bool validiumMode; - uint256 bridgehubCreateNewChainSalt; - address validatorSenderOperatorCommitEth; - address validatorSenderOperatorBlobsEth; - address baseToken; - bytes32 baseTokenAssetId; - uint128 baseTokenGasPriceMultiplierNominator; - uint128 baseTokenGasPriceMultiplierDenominator; - address bridgehub; - // TODO(EVM-744): maybe rename to asset router - address sharedBridgeProxy; - address nativeTokenVault; - address chainTypeManagerProxy; - address validatorTimelock; - bytes diamondCutData; - bytes forceDeployments; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - address l1Nullifier; - } - struct Output { address governance; address diamondProxy; @@ -167,6 +167,10 @@ contract RegisterZKChainScript is Script { config.validatorSenderOperatorBlobsEth = toml.readAddress("$.chain.validator_sender_operator_blobs_eth"); } + function getConfig() public view returns (Config memory) { + return config; + } + function initializeConfigTest() internal { // Grab config from output of l1 deployment string memory root = vm.projectRoot(); diff --git a/l1-contracts/deploy-scripts/Utils.sol b/l1-contracts/deploy-scripts/Utils.sol index b2d71bab7..9b8a9e7da 100644 --- a/l1-contracts/deploy-scripts/Utils.sol +++ b/l1-contracts/deploy-scripts/Utils.sol @@ -17,7 +17,6 @@ import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddre import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; import {IChainAdmin} from "contracts/governance/IChainAdmin.sol"; import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; -import {Call} from "contracts/governance/Common.sol"; /// @dev The offset from which the built-in, but user space contracts are located. uint160 constant USER_CONTRACTS_OFFSET = 0x10000; // 2^16 @@ -28,6 +27,8 @@ address constant L2_ASSET_ROUTER_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x03) address constant L2_NATIVE_TOKEN_VAULT_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x04); address constant L2_MESSAGE_ROOT_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x05); +address constant L2_CREATE2_FACTORY_ADDRESS = address(USER_CONTRACTS_OFFSET); + // solhint-disable-next-line gas-struct-packing struct StateTransitionDeployedAddresses { address chainTypeManagerProxy; @@ -244,14 +245,7 @@ library Utils { address bridgehubAddress, address l1SharedBridgeProxy ) internal returns (address) { - bytes32 bytecodeHash = L2ContractHelper.hashL2Bytecode(bytecode); - - bytes memory deployData = abi.encodeWithSignature( - "create2(bytes32,bytes32,bytes)", - create2salt, - bytecodeHash, - constructorargs - ); + (bytes32 bytecodeHash, bytes memory deployData) = getDeploymentCalldata(create2salt, bytecode, constructorargs); address contractAddress = L2ContractHelper.computeCreate2Address( msg.sender, @@ -260,21 +254,80 @@ library Utils { keccak256(constructorargs) ); - uint256 factoryDepsLength = factoryDeps.length; + bytes[] memory _factoryDeps = appendArray(factoryDeps, bytecode); - bytes[] memory _factoryDeps = new bytes[](factoryDepsLength + 1); + runL1L2Transaction({ + l2Calldata: deployData, + l2GasLimit: l2GasLimit, + l2Value: 0, + factoryDeps: _factoryDeps, + dstAddress: L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }); + return contractAddress; + } + + function getL2AddressViaCreate2Factory( + bytes32 create2Salt, + bytes32 bytecodeHash, + bytes memory constructorArgs + ) internal view returns (address) { + return + L2ContractHelper.computeCreate2Address( + L2_CREATE2_FACTORY_ADDRESS, + create2Salt, + bytecodeHash, + keccak256(constructorArgs) + ); + } - for (uint256 i = 0; i < factoryDepsLength; ++i) { - _factoryDeps[i] = factoryDeps[i]; + function getDeploymentCalldata( + bytes32 create2Salt, + bytes memory bytecode, + bytes memory constructorArgs + ) internal view returns (bytes32 bytecodeHash, bytes memory data) { + bytecodeHash = L2ContractHelper.hashL2Bytecode(bytecode); + + data = abi.encodeWithSignature("create2(bytes32,bytes32,bytes)", create2Salt, bytecodeHash, constructorArgs); + } + + function appendArray(bytes[] memory array, bytes memory element) internal pure returns (bytes[] memory) { + uint256 arrayLength = array.length; + bytes[] memory newArray = new bytes[](arrayLength + 1); + for (uint256 i = 0; i < arrayLength; ++i) { + newArray[i] = array[i]; } - _factoryDeps[factoryDepsLength] = bytecode; + newArray[arrayLength] = element; + return newArray; + } + + /** + * @dev Deploy l2 contracts through l1, while using built-in L2 Create2Factory contract. + */ + function deployThroughL1Deterministic( + bytes memory bytecode, + bytes memory constructorargs, + bytes32 create2salt, + uint256 l2GasLimit, + bytes[] memory factoryDeps, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) internal returns (address) { + (bytes32 bytecodeHash, bytes memory deployData) = getDeploymentCalldata(create2salt, bytecode, constructorargs); + + address contractAddress = getL2AddressViaCreate2Factory(create2salt, bytecodeHash, constructorargs); + + bytes[] memory _factoryDeps = appendArray(factoryDeps, bytecode); runL1L2Transaction({ l2Calldata: deployData, l2GasLimit: l2GasLimit, l2Value: 0, factoryDeps: _factoryDeps, - dstAddress: L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, + dstAddress: L2_CREATE2_FACTORY_ADDRESS, chainId: chainId, bridgehubAddress: bridgehubAddress, l1SharedBridgeProxy: l1SharedBridgeProxy diff --git a/l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol b/l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol new file mode 100644 index 000000000..7df6863d3 --- /dev/null +++ b/l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; +import {Utils, L2_BRIDGEHUB_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT_ADDRESS} from "../Utils.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {L2ContractsBytecodesLib} from "../L2ContractsBytecodesLib.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {Call} from "contracts/governance/Common.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; + +interface LegacyChainAdmin { + function owner() external view returns (address); +} + +contract ChainUpgrade is Script { + using stdToml for string; + + struct ChainConfig { + address deployerAddress; + address ownerAddress; + uint256 chainChainId; + address chainDiamondProxyAddress; + bool validiumMode; + bool permanentRollup; + // FIXME: From ecosystem, maybe move to a different struct + address expectedRollupL2DAValidator; + address expectedL2GatewayUpgrade; + address expectedValidiumL2DAValidator; + address permanentRollupRestriction; + address bridgehubProxyAddress; + address oldSharedBridgeProxyAddress; + } + + struct Output { + address l2DAValidator; + address accessControlRestriction; + address chainAdmin; + } + + address currentChainAdmin; + ChainConfig config; + Output output; + + function prepareChain( + string memory ecosystemInputPath, + string memory ecosystemOutputPath, + string memory configPath, + string memory outputPath + ) public { + string memory root = vm.projectRoot(); + ecosystemInputPath = string.concat(root, ecosystemInputPath); + ecosystemOutputPath = string.concat(root, ecosystemOutputPath); + configPath = string.concat(root, configPath); + outputPath = string.concat(root, outputPath); + + initializeConfig(configPath, ecosystemInputPath, ecosystemOutputPath); + + checkCorrectOwnerAddress(); + // Preparation of chain consists of two parts: + // - Deploying l2 da validator + // - Deploying new chain admin + + deployNewL2DAValidator(); + deployL2GatewayUpgrade(); + deployNewChainAdmin(); + governanceMoveToNewChainAdmin(); + + saveOutput(outputPath); + } + + function upgradeChain(uint256 oldProtocolVersion, Diamond.DiamondCutData memory upgradeCutData) public { + Utils.adminExecute( + output.chainAdmin, + output.accessControlRestriction, + config.chainDiamondProxyAddress, + abi.encodeCall(IAdmin.upgradeChainFromVersion, (oldProtocolVersion, upgradeCutData)), + 0 + ); + } + + function initializeConfig( + string memory configPath, + string memory ecosystemInputPath, + string memory ecosystemOutputPath + ) internal { + config.deployerAddress = msg.sender; + + // Grab config from output of l1 deployment + string memory toml = vm.readFile(configPath); + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + + config.ownerAddress = toml.readAddress("$.owner_address"); + config.chainChainId = toml.readUint("$.chain.chain_id"); + config.validiumMode = toml.readBool("$.chain.validium_mode"); + config.chainDiamondProxyAddress = toml.readAddress("$.chain.diamond_proxy_address"); + config.permanentRollup = toml.readBool("$.chain.permanent_rollup"); + + toml = vm.readFile(ecosystemOutputPath); + + config.expectedRollupL2DAValidator = toml.readAddress("$.contracts_config.expected_rollup_l2_da_validator"); + config.expectedValidiumL2DAValidator = toml.readAddress("$.contracts_config.expected_validium_l2_da_validator"); + config.expectedL2GatewayUpgrade = toml.readAddress("$.contracts_config.expected_l2_gateway_upgrade"); + config.permanentRollupRestriction = toml.readAddress("$.deployed_addresses.permanent_rollup_restriction"); + + toml = vm.readFile(ecosystemInputPath); + + config.bridgehubProxyAddress = toml.readAddress("$.contracts.bridgehub_proxy_address"); + config.oldSharedBridgeProxyAddress = toml.readAddress("$.contracts.old_shared_bridge_proxy_address"); + } + + function checkCorrectOwnerAddress() internal { + currentChainAdmin = address(IZKChain(config.chainDiamondProxyAddress).getAdmin()); + address currentAdminOwner = LegacyChainAdmin(currentChainAdmin).owner(); + + require(currentAdminOwner == config.ownerAddress, "Only the owner of the chain admin can call this function"); + } + + function deployNewL2DAValidator() internal { + address expectedL2DAValidator = Utils.deployThroughL1Deterministic({ + // FIXME: for now this script only works with rollup chains + bytecode: L2ContractsBytecodesLib.readRollupL2DAValidatorBytecode(), + constructorargs: hex"", + create2salt: bytes32(0), + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainChainId, + bridgehubAddress: config.bridgehubProxyAddress, + l1SharedBridgeProxy: config.oldSharedBridgeProxyAddress + }); + // FIXME: for now this script only works with rollup chains + require(expectedL2DAValidator == config.expectedRollupL2DAValidator, "Invalid L2DAValidator address"); + + output.l2DAValidator = expectedL2DAValidator; + } + + function deployL2GatewayUpgrade() internal { + address expectedGatewayUpgrade = Utils.deployThroughL1Deterministic({ + bytecode: L2ContractsBytecodesLib.readGatewayUpgradeBytecode(), + constructorargs: hex"", + create2salt: bytes32(0), + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainChainId, + bridgehubAddress: config.bridgehubProxyAddress, + l1SharedBridgeProxy: config.oldSharedBridgeProxyAddress + }); + require(expectedGatewayUpgrade == config.expectedL2GatewayUpgrade, "Invalid L2Gateway address"); + } + + function deployNewChainAdmin() internal { + AccessControlRestriction accessControlRestriction = new AccessControlRestriction(0, config.ownerAddress); + + address[] memory restrictions; + if (config.permanentRollup) { + restrictions = new address[](2); + restrictions[0] = address(accessControlRestriction); + restrictions[1] = config.permanentRollupRestriction; + } else { + restrictions = new address[](1); + restrictions[0] = address(accessControlRestriction); + } + + ChainAdmin newChainAdmin = new ChainAdmin(restrictions); + output.chainAdmin = address(newChainAdmin); + output.accessControlRestriction = address(accessControlRestriction); + } + + /// @dev The caller of this function needs to be the owner of the chain admin + /// of the + function governanceMoveToNewChainAdmin() internal { + // Firstly, we need to call the legacy chain admin to transfer the ownership to the new chain admin + Call[] memory calls = new Call[](1); + calls[0] = Call({ + target: config.chainDiamondProxyAddress, + value: 0, + data: abi.encodeCall(IAdmin.setPendingAdmin, (output.chainAdmin)) + }); + + vm.startBroadcast(config.ownerAddress); + ChainAdmin(payable(currentChainAdmin)).multicall(calls, true); + vm.stopBroadcast(); + + // Now we need to accept the adminship + Utils.adminExecute({ + _admin: output.chainAdmin, + _accessControlRestriction: output.accessControlRestriction, + _target: config.chainDiamondProxyAddress, + _data: abi.encodeCall(IAdmin.acceptAdmin, ()), + _value: 0 + }); + } + + function saveOutput(string memory outputPath) internal { + vm.serializeAddress("root", "l2_da_validator_addr", output.l2DAValidator); + vm.serializeAddress("root", "chain_admin_addr", output.chainAdmin); + + string memory toml = vm.serializeAddress("root", "access_control_restriction", output.accessControlRestriction); + string memory root = vm.projectRoot(); + vm.writeToml(toml, outputPath); + console.log("Output saved at:", outputPath); + } +} diff --git a/l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol b/l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol new file mode 100644 index 000000000..124d33294 --- /dev/null +++ b/l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {Utils, L2_BRIDGEHUB_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT_ADDRESS} from "../Utils.sol"; +import {Multicall3} from "contracts/dev-contracts/Multicall3.sol"; +import {Verifier} from "contracts/state-transition/Verifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; +import {Governance} from "contracts/governance/Governance.sol"; +import {L1GenesisUpgrade} from "contracts/upgrades/L1GenesisUpgrade.sol"; +import {GatewayUpgrade} from "contracts/upgrades/GatewayUpgrade.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; +import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; +import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; +import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; +import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; +import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; +import {ChainTypeManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IChainTypeManager.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; +import {InitializeDataNewChain as DiamondInitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; +import {FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1ERC20Bridge} from "contracts/bridge/L1ERC20Bridge.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {AddressHasNoCode} from "../ZkSyncScriptErrors.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IL2ContractDeployer} from "contracts/common/interfaces/IL2ContractDeployer.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {IL1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; +import {L1NullifierDev} from "contracts/dev-contracts/L1NullifierDev.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; +import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L2ContractsBytecodesLib} from "../L2ContractsBytecodesLib.sol"; +import {ValidiumL1DAValidator} from "contracts/state-transition/data-availability/ValidiumL1DAValidator.sol"; +import {Call} from "contracts/governance/Common.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {ProposedUpgrade} from "contracts/upgrades/BaseZkSyncUpgrade.sol"; + +import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; + +import {L2_FORCE_DEPLOYER_ADDR, L2_COMPLEX_UPGRADER_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {IComplexUpgrader} from "contracts/state-transition/l2-deps/IComplexUpgrader.sol"; +import {GatewayUpgradeEncodedInput} from "contracts/upgrades/GatewayUpgrade.sol"; +import {TransitionaryOwner} from "contracts/governance/TransitionaryOwner.sol"; + +struct FixedForceDeploymentsData { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + bytes32 l2TokenProxyBytecodeHash; + address aliasedL1Governance; + uint256 maxNumberOfZKChains; + bytes32 bridgehubBytecodeHash; + bytes32 l2AssetRouterBytecodeHash; + bytes32 l2NtvBytecodeHash; + bytes32 messageRootBytecodeHash; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; +} + +// A subset of the ones used for tests +struct StateTransitionDeployedAddresses { + address chainTypeManagerImplementation; + address verifier; + address adminFacet; + address mailboxFacet; + address executorFacet; + address gettersFacet; + address diamondInit; + address genesisUpgrade; + address defaultUpgrade; + address validatorTimelock; +} + +contract EcosystemUpgrade is Script { + using stdToml for string; + + address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; + address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + + // solhint-disable-next-line gas-struct-packing + struct DeployedAddresses { + BridgehubDeployedAddresses bridgehub; + StateTransitionDeployedAddresses stateTransition; + BridgesDeployedAddresses bridges; + L1NativeTokenVaultAddresses vaults; + DataAvailabilityDeployedAddresses daAddresses; + ExpectedL2Addresses expectedL2Addresses; + address chainAdmin; + address accessControlRestrictionAddress; + address permanentRollupRestriction; + address validatorTimelock; + address gatewayUpgrade; + address create2Factory; + address transitionaryOwner; + } + + struct ExpectedL2Addresses { + address expectedRollupL2DAValidator; + address expectedValidiumL2DAValidator; + address expectedL2GatewayUpgrade; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + // In reality, the following addresses need to be + // deployed only on a settlement layer, i.e. the Gateway. + address expectedL2ProxyAdminDeployer; + address expectedL2ProxyAdmin; + address expectedL2AdminFactory; + address expectedL2PermanentRestrictionImpl; + address expectedL2PermanentRestrictionProxy; + } + + // solhint-disable-next-line gas-struct-packing + struct L1NativeTokenVaultAddresses { + address l1NativeTokenVaultImplementation; + address l1NativeTokenVaultProxy; + } + + struct DataAvailabilityDeployedAddresses { + address l1RollupDAValidator; + address l1ValidiumDAValidator; + } + + // solhint-disable-next-line gas-struct-packing + struct BridgehubDeployedAddresses { + address bridgehubImplementation; + address ctmDeploymentTrackerImplementation; + address ctmDeploymentTrackerProxy; + address messageRootImplementation; + address messageRootProxy; + } + + // solhint-disable-next-line gas-struct-packing + struct BridgesDeployedAddresses { + address erc20BridgeImplementation; + address sharedBridgeProxy; + address sharedBridgeImplementation; + address l1NullifierImplementation; + address bridgedStandardERC20Implementation; + address bridgedTokenBeacon; + } + + // solhint-disable-next-line gas-struct-packing + struct Config { + uint256 l1ChainId; + address deployerAddress; + uint256 eraChainId; + address ownerAddress; + bool testnetVerifier; + ContractsConfig contracts; + TokensConfig tokens; + } + + // solhint-disable-next-line gas-struct-packing + struct GeneratedData { + bytes forceDeploymentsData; + bytes diamondCutData; + } + + // solhint-disable-next-line gas-struct-packing + struct ContractsConfig { + bytes32 create2FactorySalt; + address create2FactoryAddr; + uint256 validatorTimelockExecutionDelay; + bytes32 genesisRoot; + uint256 genesisRollupLeafIndex; + bytes32 genesisBatchCommitment; + uint256 latestProtocolVersion; + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; + uint256 priorityTxMaxGasLimit; + PubdataPricingMode diamondInitPubdataPricingMode; + uint256 diamondInitBatchOverheadL1Gas; + uint256 diamondInitMaxPubdataPerBatch; + uint256 diamondInitMaxL2GasPerBatch; + uint256 diamondInitPriorityTxMaxPubdata; + uint256 diamondInitMinimalL2GasPrice; + uint256 maxNumberOfChains; + bytes32 bootloaderHash; + bytes32 defaultAAHash; + address oldValidatorTimelock; + address legacyErc20BridgeAddress; + address bridgehubProxyAddress; + address oldSharedBridgeProxyAddress; + address stateTransitionManagerAddress; + address transparentProxyAdmin; + address eraDiamondProxy; + address blobVersionedHashRetriever; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; + } + + struct TokensConfig { + address tokenWethAddress; + } + + Config internal config; + GeneratedData internal generatedData; + DeployedAddresses internal addresses; + + function prepareEcosystemContracts(string memory configPath, string memory outputPath) public { + string memory root = vm.projectRoot(); + configPath = string.concat(root, configPath); + outputPath = string.concat(root, outputPath); + + initializeConfig(configPath); + + instantiateCreate2Factory(); + + deployVerifier(); + deployDefaultUpgrade(); + deployGenesisUpgrade(); + deployGatewayUpgrade(); + + deployDAValidators(); + deployValidatorTimelock(); + + // TODO: restore + // deployChainAdmin(); + deployBridgehubImplementation(); + deployMessageRootContract(); + + deployL1NullifierContracts(); + deploySharedBridgeContracts(); + deployBridgedStandardERC20Implementation(); + deployBridgedTokenBeacon(); + deployL1NativeTokenVaultImplementation(); + deployL1NativeTokenVaultProxy(); + deployErc20BridgeImplementation(); + + deployCTMDeploymentTracker(); + + initializeGeneratedData(); + initializeExpectedL2Addresses(); + + deployChainTypeManagerContract(); + setChainTypeManagerInValidatorTimelock(); + + deployPermanentRollupRestriction(); + + deployTransitionaryOwner(); + + updateOwners(); + + saveOutput(outputPath); + } + + function run() public { + console.log("Deploying L1 contracts"); + } + + function provideAcceptOwnershipCalls() public returns (Call[] memory calls) { + console.log("Providing accept ownership calls"); + + calls = new Call[](4); + calls[0] = Call({ + target: addresses.permanentRollupRestriction, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + calls[1] = Call({ + target: addresses.validatorTimelock, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + calls[2] = Call({ + target: addresses.bridges.sharedBridgeProxy, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + calls[3] = Call({ + target: addresses.bridgehub.ctmDeploymentTrackerProxy, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + } + + function getOwnerAddress() public returns (address) { + return config.ownerAddress; + } + + function _getFacetCutsForDeletion() internal returns (Diamond.FacetCut[] memory facetCuts) { + IZKChain.Facet[] memory facets = IZKChain(config.contracts.eraDiamondProxy).facets(); + + // Freezability does not matter when deleting, so we just put false everywhere + facetCuts = new Diamond.FacetCut[](facets.length); + for (uint i = 0; i < facets.length; i++) { + facetCuts[i] = Diamond.FacetCut({ + facet: address(0), + action: Diamond.Action.Remove, + isFreezable: false, + selectors: facets[i].selectors + }); + } + } + + function _composeUpgradeTx() internal returns (L2CanonicalTransaction memory transaction) { + transaction = L2CanonicalTransaction({ + // FIXME: dont use hardcoded values + txType: 254, + from: uint256(uint160(L2_FORCE_DEPLOYER_ADDR)), + to: uint256(uint160(address(L2_COMPLEX_UPGRADER_ADDR))), + gasLimit: 72_000_000, + gasPerPubdataByteLimit: 800, + maxFeePerGas: 0, + maxPriorityFeePerGas: 0, + paymaster: uint256(uint160(address(0))), + nonce: 25, + value: 0, + reserved: [uint256(0), uint256(0), uint256(0), uint256(0)], + // Note, that the data is empty, it will be fully composed inside the `GatewayUpgrade` contract + data: new bytes(0), + signature: new bytes(0), + // All factory deps should've been published before + factoryDeps: new uint256[](0), + paymasterInput: new bytes(0), + // Reserved dynamic type for the future use-case. Using it should be avoided, + // But it is still here, just in case we want to enable some additional functionality + reservedDynamic: new bytes(0) + }); + } + + function getNewProtocolVersion() public returns (uint256) { + return 0x1900000000; + } + + function getOldProtocolDeadline() public returns (uint256) { + return 7 days; + } + + function getOldProtocolVersion() public returns (uint256) { + return 0x1800000002; + } + + function provideSetNewVersionUpgradeCall() public returns (Call[] memory calls) { + // Just retrieved it from the contract + uint256 PREVIOUS_PROTOCOL_VERSION = getOldProtocolVersion(); + uint256 DEADLINE = getOldProtocolDeadline(); + uint256 NEW_PROTOCOL_VERSION = getNewProtocolVersion(); + Call memory call = Call({ + target: config.contracts.stateTransitionManagerAddress, + data: abi.encodeCall( + ChainTypeManager.setNewVersionUpgrade, + (getChainUpgradeInfo(), PREVIOUS_PROTOCOL_VERSION, DEADLINE, NEW_PROTOCOL_VERSION) + ), + value: 0 + }); + + calls = new Call[](1); + calls[0] = call; + } + + function getChainUpgradeInfo() public returns (Diamond.DiamondCutData memory upgradeCutData) { + Diamond.FacetCut[] memory deletedFacets = _getFacetCutsForDeletion(); + + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](deletedFacets.length + 4); + for (uint i = 0; i < deletedFacets.length; i++) { + facetCuts[i] = deletedFacets[i]; + } + facetCuts[deletedFacets.length] = Diamond.FacetCut({ + facet: addresses.stateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.adminFacet.code) + }); + facetCuts[deletedFacets.length + 1] = Diamond.FacetCut({ + facet: addresses.stateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.gettersFacet.code) + }); + facetCuts[deletedFacets.length + 2] = Diamond.FacetCut({ + facet: addresses.stateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.mailboxFacet.code) + }); + facetCuts[deletedFacets.length + 3] = Diamond.FacetCut({ + facet: addresses.stateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.executorFacet.code) + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash + }); + + // TODO: we should fill this one up completely, but it is straightforward + IL2ContractDeployer.ForceDeployment[] memory baseForceDeployments = new IL2ContractDeployer.ForceDeployment[]( + 0 + ); + address ctmDeployer = addresses.bridgehub.ctmDeploymentTrackerProxy; + + GatewayUpgradeEncodedInput memory gateUpgradeInput = GatewayUpgradeEncodedInput({ + baseForceDeployments: baseForceDeployments, + ctmDeployer: ctmDeployer, + fixedForceDeploymentsData: generatedData.forceDeploymentsData, + l2GatewayUpgrade: addresses.expectedL2Addresses.expectedL2GatewayUpgrade, + oldValidatorTimelock: config.contracts.oldValidatorTimelock, + newValidatorTimelock: addresses.validatorTimelock + }); + + bytes memory postUpgradeCalldata = abi.encode(gateUpgradeInput); + + ProposedUpgrade memory proposedUpgrade = ProposedUpgrade({ + l2ProtocolUpgradeTx: _composeUpgradeTx(), + factoryDeps: new bytes[](0), + bootloaderHash: config.contracts.bootloaderHash, + defaultAccountHash: config.contracts.defaultAAHash, + verifier: addresses.stateTransition.verifier, + verifierParams: verifierParams, + l1ContractsUpgradeCalldata: new bytes(0), + postUpgradeCalldata: postUpgradeCalldata, + // FIXME: TBH, I am not sure if even should even put any time there, + // but we may + upgradeTimestamp: 0, + newProtocolVersion: getNewProtocolVersion() + }); + + upgradeCutData = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: addresses.gatewayUpgrade, + initCalldata: abi.encodeCall(GatewayUpgrade.upgrade, (proposedUpgrade)) + }); + } + + function getStage2UpgradeCalls() public returns (Call[] memory calls) { + calls = new Call[](9); + + // We need to firstly update all the contracts + calls[0] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgrade, + ( + ITransparentUpgradeableProxy(payable(config.contracts.stateTransitionManagerAddress)), + addresses.stateTransition.chainTypeManagerImplementation + ) + ), + value: 0 + }); + calls[1] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgradeAndCall, + ( + ITransparentUpgradeableProxy(payable(config.contracts.bridgehubProxyAddress)), + addresses.bridgehub.bridgehubImplementation, + abi.encodeCall(Bridgehub.initializeV2, ()) + ) + ), + value: 0 + }); + calls[2] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgrade, + ( + ITransparentUpgradeableProxy(payable(config.contracts.oldSharedBridgeProxyAddress)), + addresses.bridges.l1NullifierImplementation + ) + ), + value: 0 + }); + calls[3] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgrade, + ( + ITransparentUpgradeableProxy(payable(config.contracts.legacyErc20BridgeAddress)), + addresses.bridges.erc20BridgeImplementation + ) + ), + value: 0 + }); + + // Now, updating chain creation params + calls[4] = Call({ + target: config.contracts.stateTransitionManagerAddress, + data: abi.encodeCall(ChainTypeManager.setChainCreationParams, (prepareNewChainCreationParams())), + value: 0 + }); + calls[5] = Call({ + target: config.contracts.stateTransitionManagerAddress, + data: abi.encodeCall(ChainTypeManager.setValidatorTimelock, (addresses.validatorTimelock)), + value: 0 + }); + + // Now, we need to update the bridgehub + calls[6] = Call({ + target: config.contracts.bridgehubProxyAddress, + data: abi.encodeCall( + Bridgehub.setAddresses, + ( + addresses.bridges.sharedBridgeProxy, + CTMDeploymentTracker(addresses.bridgehub.ctmDeploymentTrackerProxy), + MessageRoot(addresses.bridgehub.messageRootProxy) + ) + ), + value: 0 + }); + + // Setting the necessary params for the L1Nullifier contract + calls[7] = Call({ + target: config.contracts.oldSharedBridgeProxyAddress, + data: abi.encodeCall( + L1Nullifier.setL1NativeTokenVault, + (L1NativeTokenVault(payable(addresses.vaults.l1NativeTokenVaultProxy))) + ), + value: 0 + }); + calls[8] = Call({ + target: config.contracts.oldSharedBridgeProxyAddress, + data: abi.encodeCall(L1Nullifier.setL1AssetRouter, (addresses.bridges.sharedBridgeProxy)), + value: 0 + }); + } + + function initializeConfig(string memory configPath) internal { + string memory toml = vm.readFile(configPath); + + config.l1ChainId = block.chainid; + config.deployerAddress = msg.sender; + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + config.eraChainId = toml.readUint("$.era_chain_id"); + config.ownerAddress = toml.readAddress("$.owner_address"); + config.testnetVerifier = toml.readBool("$.testnet_verifier"); + + config.contracts.maxNumberOfChains = toml.readUint("$.contracts.max_number_of_chains"); + config.contracts.create2FactorySalt = toml.readBytes32("$.contracts.create2_factory_salt"); + if (vm.keyExistsToml(toml, "$.contracts.create2_factory_addr")) { + config.contracts.create2FactoryAddr = toml.readAddress("$.contracts.create2_factory_addr"); + } + config.contracts.validatorTimelockExecutionDelay = toml.readUint( + "$.contracts.validator_timelock_execution_delay" + ); + config.contracts.genesisRoot = toml.readBytes32("$.contracts.genesis_root"); + config.contracts.genesisRollupLeafIndex = toml.readUint("$.contracts.genesis_rollup_leaf_index"); + config.contracts.genesisBatchCommitment = toml.readBytes32("$.contracts.genesis_batch_commitment"); + config.contracts.latestProtocolVersion = toml.readUint("$.contracts.latest_protocol_version"); + config.contracts.recursionNodeLevelVkHash = toml.readBytes32("$.contracts.recursion_node_level_vk_hash"); + config.contracts.recursionLeafLevelVkHash = toml.readBytes32("$.contracts.recursion_leaf_level_vk_hash"); + config.contracts.recursionCircuitsSetVksHash = toml.readBytes32("$.contracts.recursion_circuits_set_vks_hash"); + config.contracts.priorityTxMaxGasLimit = toml.readUint("$.contracts.priority_tx_max_gas_limit"); + config.contracts.diamondInitPubdataPricingMode = PubdataPricingMode( + toml.readUint("$.contracts.diamond_init_pubdata_pricing_mode") + ); + config.contracts.diamondInitBatchOverheadL1Gas = toml.readUint( + "$.contracts.diamond_init_batch_overhead_l1_gas" + ); + config.contracts.diamondInitMaxPubdataPerBatch = toml.readUint( + "$.contracts.diamond_init_max_pubdata_per_batch" + ); + config.contracts.diamondInitMaxL2GasPerBatch = toml.readUint("$.contracts.diamond_init_max_l2_gas_per_batch"); + config.contracts.diamondInitPriorityTxMaxPubdata = toml.readUint( + "$.contracts.diamond_init_priority_tx_max_pubdata" + ); + config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); + config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); + config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); + + config.contracts.stateTransitionManagerAddress = toml.readAddress( + "$.contracts.state_transition_manager_address" + ); + config.contracts.bridgehubProxyAddress = toml.readAddress("$.contracts.bridgehub_proxy_address"); + config.contracts.oldSharedBridgeProxyAddress = toml.readAddress("$.contracts.old_shared_bridge_proxy_address"); + config.contracts.transparentProxyAdmin = toml.readAddress("$.contracts.transparent_proxy_admin"); + config.contracts.eraDiamondProxy = toml.readAddress("$.contracts.era_diamond_proxy"); + config.contracts.legacyErc20BridgeAddress = toml.readAddress("$.contracts.legacy_erc20_bridge_address"); + config.contracts.oldValidatorTimelock = toml.readAddress("$.contracts.old_validator_timelock"); + // FIXME: value stored there is incorrect at the moment, figure out the correct value + config.contracts.blobVersionedHashRetriever = toml.readAddress("$.contracts.blob_versioned_hash_retriever"); + config.contracts.l2BridgeProxyOwnerAddress = toml.readAddress("$.contracts.l2_bridge_proxy_owner_address"); + config.contracts.l2BridgedStandardERC20ProxyOwnerAddress = toml.readAddress( + "$.contracts.l2_bridged_standard_erc20_proxy_owner_address" + ); + + config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); + } + + function initializeGeneratedData() internal { + generatedData.forceDeploymentsData = prepareForceDeploymentsData(); + } + + function initializeExpectedL2Addresses() internal { + address aliasedGovernance = AddressAliasHelper.applyL1ToL2Alias(config.ownerAddress); + + address expectedL2ProxyAdminDeployer = Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readProxyAdminDeployerBytecode()), + abi.encode(aliasedGovernance) + ); + address expectedL2ProxyAdmin = L2ContractHelper.computeCreate2Address( + expectedL2ProxyAdminDeployer, + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readProxyAdminBytecode()), + keccak256(hex"") + ); + + address permanentRestrictionImpl = Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readPermanentRestrictionBytecode()), + // Note that for L2 deployments the L2AdminFactory is 0. + abi.encode(L2_BRIDGEHUB_ADDRESS, address(0)) + ); + + address permanentRestrictionProxy = Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode()), + abi.encode( + permanentRestrictionImpl, + expectedL2ProxyAdmin, + abi.encodeCall(PermanentRestriction.initialize, (aliasedGovernance)) + ) + ); + + address[] memory requiredL2Restrictions = new address[](1); + requiredL2Restrictions[0] = permanentRestrictionProxy; + + addresses.expectedL2Addresses = ExpectedL2Addresses({ + expectedRollupL2DAValidator: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readRollupL2DAValidatorBytecode()), + hex"" + ), + expectedValidiumL2DAValidator: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readValidiumL2DAValidatorBytecode()), + hex"" + ), + expectedL2GatewayUpgrade: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readGatewayUpgradeBytecode()), + hex"" + ), + l2SharedBridgeLegacyImpl: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readL2LegacySharedBridgeBytecode()), + hex"" + ), + l2BridgedStandardERC20Impl: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readStandardERC20Bytecode()), + hex"" + ), + expectedL2ProxyAdminDeployer: expectedL2ProxyAdminDeployer, + expectedL2ProxyAdmin: expectedL2ProxyAdmin, + expectedL2AdminFactory: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readL2AdminFactoryBytecode()), + abi.encode(requiredL2Restrictions) + ), + expectedL2PermanentRestrictionImpl: permanentRestrictionImpl, + expectedL2PermanentRestrictionProxy: permanentRestrictionProxy + }); + } + + function instantiateCreate2Factory() internal { + address contractAddress; + + bool isDeterministicDeployed = DETERMINISTIC_CREATE2_ADDRESS.code.length > 0; + bool isConfigured = config.contracts.create2FactoryAddr != address(0); + + if (isConfigured) { + if (config.contracts.create2FactoryAddr.code.length == 0) { + revert AddressHasNoCode(config.contracts.create2FactoryAddr); + } + contractAddress = config.contracts.create2FactoryAddr; + console.log("Using configured Create2Factory address:", contractAddress); + } else if (isDeterministicDeployed) { + contractAddress = DETERMINISTIC_CREATE2_ADDRESS; + console.log("Using deterministic Create2Factory address:", contractAddress); + } else { + contractAddress = Utils.deployCreate2Factory(); + console.log("Create2Factory deployed at:", contractAddress); + } + + addresses.create2Factory = contractAddress; + } + + function deployVerifier() internal { + bytes memory code; + if (config.testnetVerifier) { + code = type(TestnetVerifier).creationCode; + } else { + code = type(Verifier).creationCode; + } + address contractAddress = deployViaCreate2(code); + console.log("Verifier deployed at:", contractAddress); + addresses.stateTransition.verifier = contractAddress; + } + + function deployDefaultUpgrade() internal { + address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode); + console.log("DefaultUpgrade deployed at:", contractAddress); + addresses.stateTransition.defaultUpgrade = contractAddress; + } + + function deployGenesisUpgrade() internal { + bytes memory bytecode = abi.encodePacked(type(L1GenesisUpgrade).creationCode); + address contractAddress = deployViaCreate2(bytecode); + console.log("GenesisUpgrade deployed at:", contractAddress); + addresses.stateTransition.genesisUpgrade = contractAddress; + } + + function deployGatewayUpgrade() internal { + bytes memory bytecode = abi.encodePacked(type(GatewayUpgrade).creationCode); + address contractAddress = deployViaCreate2(bytecode); + console.log("GatewayUpgrade deployed at:", contractAddress); + addresses.gatewayUpgrade = contractAddress; + } + + function deployDAValidators() internal { + address contractAddress = deployViaCreate2(Utils.readRollupDAValidatorBytecode()); + console.log("L1RollupDAValidator deployed at:", contractAddress); + addresses.daAddresses.l1RollupDAValidator = contractAddress; + + contractAddress = deployViaCreate2(type(ValidiumL1DAValidator).creationCode); + console.log("L1ValidiumDAValidator deployed at:", contractAddress); + addresses.daAddresses.l1ValidiumDAValidator = contractAddress; + } + + function deployPermanentRollupRestriction() internal { + bytes memory bytecode = abi.encodePacked( + type(PermanentRestriction).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress, addresses.expectedL2Addresses.expectedL2AdminFactory) + ); + address implementationAddress = deployViaCreate2(bytecode); + + bytes memory proxyBytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + implementationAddress, + config.contracts.transparentProxyAdmin, + abi.encodeCall(PermanentRestriction.initialize, (config.deployerAddress)) + ) + ); + + address proxyAddress = deployViaCreate2(proxyBytecode); + addresses.permanentRollupRestriction = proxyAddress; + // FIXME: supply restrictions + } + + function deployValidatorTimelock() internal { + uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); + bytes memory bytecode = abi.encodePacked( + type(ValidatorTimelock).creationCode, + abi.encode(config.deployerAddress, executionDelay, config.eraChainId) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("ValidatorTimelock deployed at:", contractAddress); + addresses.validatorTimelock = contractAddress; + } + + function deployChainAdmin() internal { + bytes memory accessControlRestrictionBytecode = abi.encodePacked( + type(AccessControlRestriction).creationCode, + abi.encode(uint256(0), config.ownerAddress) + ); + + address accessControlRestriction = deployViaCreate2(accessControlRestrictionBytecode); + console.log("Access control restriction deployed at:", accessControlRestriction); + address[] memory restrictions = new address[](1); + restrictions[0] = accessControlRestriction; + addresses.accessControlRestrictionAddress = accessControlRestriction; + + bytes memory bytecode = abi.encodePacked(type(ChainAdmin).creationCode, abi.encode(restrictions)); + address contractAddress = deployViaCreate2(bytecode); + console.log("ChainAdmin deployed at:", contractAddress); + addresses.chainAdmin = contractAddress; + } + + function deployBridgehubImplementation() internal { + bytes memory bridgeHubBytecode = abi.encodePacked( + type(Bridgehub).creationCode, + abi.encode(config.l1ChainId, config.ownerAddress, (config.contracts.maxNumberOfChains)) + ); + address bridgehubImplementation = deployViaCreate2(bridgeHubBytecode); + console.log("Bridgehub Implementation deployed at:", bridgehubImplementation); + addresses.bridgehub.bridgehubImplementation = bridgehubImplementation; + } + + function deployMessageRootContract() internal { + bytes memory messageRootBytecode = abi.encodePacked( + type(MessageRoot).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress) + ); + address messageRootImplementation = deployViaCreate2(messageRootBytecode); + console.log("MessageRoot Implementation deployed at:", messageRootImplementation); + addresses.bridgehub.messageRootImplementation = messageRootImplementation; + + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + messageRootImplementation, + config.contracts.transparentProxyAdmin, + abi.encodeCall(MessageRoot.initialize, ()) + ) + ); + address messageRootProxy = deployViaCreate2(bytecode); + console.log("Message Root Proxy deployed at:", messageRootProxy); + addresses.bridgehub.messageRootProxy = messageRootProxy; + } + + function deployCTMDeploymentTracker() internal { + bytes memory ctmDTBytecode = abi.encodePacked( + type(CTMDeploymentTracker).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress, addresses.bridges.sharedBridgeProxy) + ); + address ctmDTImplementation = deployViaCreate2(ctmDTBytecode); + console.log("CTM Deployment Tracker Implementation deployed at:", ctmDTImplementation); + addresses.bridgehub.ctmDeploymentTrackerImplementation = ctmDTImplementation; + + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + ctmDTImplementation, + config.contracts.transparentProxyAdmin, + abi.encodeCall(CTMDeploymentTracker.initialize, (config.deployerAddress)) + ) + ); + address ctmDTProxy = deployViaCreate2(bytecode); + console.log("CTM Deployment Tracker Proxy deployed at:", ctmDTProxy); + addresses.bridgehub.ctmDeploymentTrackerProxy = ctmDTProxy; + } + + function deployChainTypeManagerContract() internal { + deployStateTransitionDiamondFacets(); + deployChainTypeManagerImplementation(); + // registerChainTypeManager(); + } + + function deployStateTransitionDiamondFacets() internal { + address executorFacet = deployViaCreate2(type(ExecutorFacet).creationCode); + console.log("ExecutorFacet deployed at:", executorFacet); + addresses.stateTransition.executorFacet = executorFacet; + + 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, config.l1ChainId)) + ); + console.log("MailboxFacet deployed at:", mailboxFacet); + addresses.stateTransition.mailboxFacet = mailboxFacet; + + address gettersFacet = deployViaCreate2(type(GettersFacet).creationCode); + console.log("GettersFacet deployed at:", gettersFacet); + addresses.stateTransition.gettersFacet = gettersFacet; + + address diamondInit = deployViaCreate2(type(DiamondInit).creationCode); + console.log("DiamondInit deployed at:", diamondInit); + addresses.stateTransition.diamondInit = diamondInit; + } + + function deployChainTypeManagerImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(ChainTypeManager).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("ChainTypeManagerImplementation deployed at:", contractAddress); + addresses.stateTransition.chainTypeManagerImplementation = contractAddress; + } + + function setChainTypeManagerInValidatorTimelock() internal { + ValidatorTimelock validatorTimelock = ValidatorTimelock(addresses.validatorTimelock); + vm.broadcast(msg.sender); + validatorTimelock.setChainTypeManager(IChainTypeManager(config.contracts.stateTransitionManagerAddress)); + console.log("ChainTypeManager set in ValidatorTimelock"); + } + + function deploySharedBridgeContracts() internal { + deploySharedBridgeImplementation(); + deploySharedBridgeProxy(); + setL1LegacyBridge(); + } + + function deployL1NullifierContracts() internal { + deployL1NullifierImplementation(); + } + + function deployL1NullifierImplementation() internal { + // TODO(EVM-743): allow non-dev nullifier in the local deployment + bytes memory bytecode = abi.encodePacked( + type(L1NullifierDev).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode(config.contracts.bridgehubProxyAddress, config.eraChainId, config.contracts.eraDiamondProxy) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("L1NullifierImplementation deployed at:", contractAddress); + addresses.bridges.l1NullifierImplementation = contractAddress; + } + + function deploySharedBridgeImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(L1AssetRouter).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode( + config.tokens.tokenWethAddress, + config.contracts.bridgehubProxyAddress, + config.contracts.oldSharedBridgeProxyAddress, + config.eraChainId, + config.contracts.eraDiamondProxy + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("SharedBridgeImplementation deployed at:", contractAddress); + addresses.bridges.sharedBridgeImplementation = contractAddress; + } + + function deploySharedBridgeProxy() internal { + bytes memory initCalldata = abi.encodeCall(L1AssetRouter.initialize, (config.deployerAddress)); + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + addresses.bridges.sharedBridgeImplementation, + config.contracts.transparentProxyAdmin, + initCalldata + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("SharedBridgeProxy deployed at:", contractAddress); + addresses.bridges.sharedBridgeProxy = contractAddress; + } + + function setL1LegacyBridge() internal { + vm.broadcast(msg.sender); + L1AssetRouter(addresses.bridges.sharedBridgeProxy).setL1Erc20Bridge( + L1ERC20Bridge(config.contracts.legacyErc20BridgeAddress) + ); + } + + function deployErc20BridgeImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(L1ERC20Bridge).creationCode, + abi.encode( + config.contracts.oldSharedBridgeProxyAddress, + addresses.bridges.sharedBridgeProxy, + addresses.vaults.l1NativeTokenVaultProxy, + config.eraChainId + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("Erc20BridgeImplementation deployed at:", contractAddress); + addresses.bridges.erc20BridgeImplementation = contractAddress; + } + + function deployBridgedStandardERC20Implementation() internal { + bytes memory bytecode = abi.encodePacked( + type(BridgedStandardERC20).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode() + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("BridgedStandardERC20Implementation deployed at:", contractAddress); + addresses.bridges.bridgedStandardERC20Implementation = contractAddress; + } + + function deployBridgedTokenBeacon() internal { + bytes memory bytecode = abi.encodePacked( + type(UpgradeableBeacon).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode(addresses.bridges.bridgedStandardERC20Implementation) + ); + UpgradeableBeacon beacon = new UpgradeableBeacon(addresses.bridges.bridgedStandardERC20Implementation); + address contractAddress = address(beacon); + beacon.transferOwnership(config.ownerAddress); + console.log("BridgedTokenBeacon deployed at:", contractAddress); + addresses.bridges.bridgedTokenBeacon = contractAddress; + } + + function deployL1NativeTokenVaultImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(L1NativeTokenVault).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode( + config.tokens.tokenWethAddress, + addresses.bridges.sharedBridgeProxy, + config.eraChainId, + config.contracts.oldSharedBridgeProxyAddress + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("L1NativeTokenVaultImplementation deployed at:", contractAddress); + addresses.vaults.l1NativeTokenVaultImplementation = contractAddress; + } + + function deployL1NativeTokenVaultProxy() internal { + bytes memory initCalldata = abi.encodeCall( + L1NativeTokenVault.initialize, + (config.ownerAddress, addresses.bridges.bridgedTokenBeacon) + ); + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + addresses.vaults.l1NativeTokenVaultImplementation, + config.contracts.transparentProxyAdmin, + initCalldata + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("L1NativeTokenVaultProxy deployed at:", contractAddress); + addresses.vaults.l1NativeTokenVaultProxy = contractAddress; + + IL1AssetRouter sharedBridge = IL1AssetRouter(addresses.bridges.sharedBridgeProxy); + IL1Nullifier l1Nullifier = IL1Nullifier(config.contracts.oldSharedBridgeProxyAddress); + // Ownable ownable = Ownable(addresses.bridges.sharedBridgeProxy); + + vm.broadcast(msg.sender); + sharedBridge.setNativeTokenVault(INativeTokenVault(addresses.vaults.l1NativeTokenVaultProxy)); + vm.broadcast(msg.sender); + IL1NativeTokenVault(addresses.vaults.l1NativeTokenVaultProxy).registerEthToken(); + } + + function deployTransitionaryOwner() internal { + bytes memory bytecode = abi.encodePacked( + type(TransitionaryOwner).creationCode, + abi.encode(config.ownerAddress) + ); + + addresses.transitionaryOwner = deployViaCreate2(bytecode); + } + + function _moveGovernanceToOwner(address target) internal { + Ownable2StepUpgradeable(target).transferOwnership(addresses.transitionaryOwner); + TransitionaryOwner(addresses.transitionaryOwner).claimOwnershipAndGiveToGovernance(target); + } + + function updateOwners() internal { + vm.startBroadcast(msg.sender); + + // Note, that it will take some time for the governance to sign the "acceptOwnership" transaction, + // in order to avoid any possibility of the front-run, we will temporarily give the ownership to the + // contract that can only transfer ownership to the governance. + _moveGovernanceToOwner(addresses.validatorTimelock); + _moveGovernanceToOwner(addresses.bridges.sharedBridgeProxy); + _moveGovernanceToOwner(addresses.bridgehub.ctmDeploymentTrackerProxy); + _moveGovernanceToOwner(addresses.permanentRollupRestriction); + + vm.stopBroadcast(); + console.log("Owners updated"); + } + + function prepareNewChainCreationParams() internal returns (ChainCreationParams memory chainCreationParams) { + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); + facetCuts[0] = Diamond.FacetCut({ + facet: addresses.stateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.adminFacet.code) + }); + facetCuts[1] = Diamond.FacetCut({ + facet: addresses.stateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.gettersFacet.code) + }); + facetCuts[2] = Diamond.FacetCut({ + facet: addresses.stateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.mailboxFacet.code) + }); + facetCuts[3] = Diamond.FacetCut({ + facet: addresses.stateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.executorFacet.code) + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash + }); + + FeeParams memory feeParams = FeeParams({ + pubdataPricingMode: config.contracts.diamondInitPubdataPricingMode, + batchOverheadL1Gas: uint32(config.contracts.diamondInitBatchOverheadL1Gas), + maxPubdataPerBatch: uint32(config.contracts.diamondInitMaxPubdataPerBatch), + maxL2GasPerBatch: uint32(config.contracts.diamondInitMaxL2GasPerBatch), + priorityTxMaxPubdata: uint32(config.contracts.diamondInitPriorityTxMaxPubdata), + minimalL2GasPrice: uint64(config.contracts.diamondInitMinimalL2GasPrice) + }); + + DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ + verifier: IVerifier(addresses.stateTransition.verifier), + verifierParams: verifierParams, + l2BootloaderBytecodeHash: config.contracts.bootloaderHash, + l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, + priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, + feeParams: feeParams, + blobVersionedHashRetriever: config.contracts.blobVersionedHashRetriever + }); + + Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: addresses.stateTransition.diamondInit, + initCalldata: abi.encode(initializeData) + }); + + chainCreationParams = ChainCreationParams({ + genesisUpgrade: addresses.stateTransition.genesisUpgrade, + genesisBatchHash: config.contracts.genesisRoot, + genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), + genesisBatchCommitment: config.contracts.genesisBatchCommitment, + diamondCut: diamondCut, + forceDeploymentsData: generatedData.forceDeploymentsData + }); + } + + function saveOutput(string memory outputPath) internal { + vm.serializeAddress("bridgehub", "bridgehub_implementation_addr", addresses.bridgehub.bridgehubImplementation); + vm.serializeAddress( + "bridgehub", + "ctm_deployment_tracker_proxy_addr", + addresses.bridgehub.ctmDeploymentTrackerProxy + ); + vm.serializeAddress( + "bridgehub", + "ctm_deployment_tracker_implementation_addr", + addresses.bridgehub.ctmDeploymentTrackerImplementation + ); + vm.serializeAddress("bridgehub", "message_root_proxy_addr", addresses.bridgehub.messageRootProxy); + string memory bridgehub = vm.serializeAddress( + "bridgehub", + "message_root_implementation_addr", + addresses.bridgehub.messageRootImplementation + ); + + // TODO(EVM-744): this has to be renamed to chain type manager + vm.serializeAddress( + "state_transition", + "state_transition_implementation_addr", + addresses.stateTransition.chainTypeManagerImplementation + ); + vm.serializeAddress("state_transition", "verifier_addr", addresses.stateTransition.verifier); + vm.serializeAddress("state_transition", "admin_facet_addr", addresses.stateTransition.adminFacet); + vm.serializeAddress("state_transition", "mailbox_facet_addr", addresses.stateTransition.mailboxFacet); + vm.serializeAddress("state_transition", "executor_facet_addr", addresses.stateTransition.executorFacet); + vm.serializeAddress("state_transition", "getters_facet_addr", addresses.stateTransition.gettersFacet); + vm.serializeAddress("state_transition", "diamond_init_addr", addresses.stateTransition.diamondInit); + vm.serializeAddress("state_transition", "genesis_upgrade_addr", addresses.stateTransition.genesisUpgrade); + string memory stateTransition = vm.serializeAddress( + "state_transition", + "default_upgrade_addr", + addresses.stateTransition.defaultUpgrade + ); + + vm.serializeAddress("bridges", "erc20_bridge_implementation_addr", addresses.bridges.erc20BridgeImplementation); + vm.serializeAddress("bridges", "l1_nullifier_implementation_addr", addresses.bridges.l1NullifierImplementation); + vm.serializeAddress( + "bridges", + "shared_bridge_implementation_addr", + addresses.bridges.sharedBridgeImplementation + ); + string memory bridges = vm.serializeAddress( + "bridges", + "shared_bridge_proxy_addr", + addresses.bridges.sharedBridgeProxy + ); + + vm.serializeUint( + "contracts_config", + "diamond_init_max_l2_gas_per_batch", + config.contracts.diamondInitMaxL2GasPerBatch + ); + vm.serializeUint( + "contracts_config", + "diamond_init_batch_overhead_l1_gas", + config.contracts.diamondInitBatchOverheadL1Gas + ); + vm.serializeUint( + "contracts_config", + "diamond_init_max_pubdata_per_batch", + config.contracts.diamondInitMaxPubdataPerBatch + ); + vm.serializeUint( + "contracts_config", + "diamond_init_minimal_l2_gas_price", + config.contracts.diamondInitMinimalL2GasPrice + ); + vm.serializeUint( + "contracts_config", + "diamond_init_priority_tx_max_pubdata", + config.contracts.diamondInitPriorityTxMaxPubdata + ); + vm.serializeUint( + "contracts_config", + "diamond_init_pubdata_pricing_mode", + uint256(config.contracts.diamondInitPubdataPricingMode) + ); + vm.serializeUint("contracts_config", "priority_tx_max_gas_limit", config.contracts.priorityTxMaxGasLimit); + vm.serializeBytes32( + "contracts_config", + "recursion_circuits_set_vks_hash", + config.contracts.recursionCircuitsSetVksHash + ); + vm.serializeBytes32( + "contracts_config", + "recursion_leaf_level_vk_hash", + config.contracts.recursionLeafLevelVkHash + ); + vm.serializeBytes32( + "contracts_config", + "recursion_node_level_vk_hash", + config.contracts.recursionNodeLevelVkHash + ); + + vm.serializeAddress( + "contracts_config", + "expected_rollup_l2_da_validator", + addresses.expectedL2Addresses.expectedRollupL2DAValidator + ); + vm.serializeAddress( + "contracts_config", + "expected_validium_l2_da_validator", + addresses.expectedL2Addresses.expectedValidiumL2DAValidator + ); + vm.serializeAddress( + "contracts_config", + "expected_l2_gateway_upgrade", + addresses.expectedL2Addresses.expectedL2GatewayUpgrade + ); + vm.serializeBytes("contracts_config", "diamond_cut_data", generatedData.diamondCutData); + + string memory contractsConfig = vm.serializeBytes( + "contracts_config", + "force_deployments_data", + generatedData.forceDeploymentsData + ); + + vm.serializeAddress("deployed_addresses", "validator_timelock_addr", addresses.validatorTimelock); + vm.serializeAddress("deployed_addresses", "chain_admin", addresses.chainAdmin); + vm.serializeAddress( + "deployed_addresses", + "access_control_restriction_addr", + addresses.accessControlRestrictionAddress + ); + vm.serializeAddress("deployed_addresses", "permanent_rollup_restriction", addresses.permanentRollupRestriction); + vm.serializeString("deployed_addresses", "bridgehub", bridgehub); + vm.serializeString("deployed_addresses", "bridges", bridges); + vm.serializeString("deployed_addresses", "state_transition", stateTransition); + + vm.serializeAddress( + "deployed_addresses", + "rollup_l1_da_validator_addr", + addresses.daAddresses.l1RollupDAValidator + ); + vm.serializeAddress( + "deployed_addresses", + "validium_l1_da_validator_addr", + addresses.daAddresses.l1ValidiumDAValidator + ); + + string memory deployedAddresses = vm.serializeAddress( + "deployed_addresses", + "native_token_vault_addr", + addresses.vaults.l1NativeTokenVaultProxy + ); + + vm.serializeAddress("root", "create2_factory_addr", addresses.create2Factory); + vm.serializeBytes32("root", "create2_factory_salt", config.contracts.create2FactorySalt); + vm.serializeUint("root", "l1_chain_id", config.l1ChainId); + vm.serializeUint("root", "era_chain_id", config.eraChainId); + vm.serializeAddress("root", "deployer_addr", config.deployerAddress); + vm.serializeString("root", "deployed_addresses", deployedAddresses); + vm.serializeString("root", "contracts_config", contractsConfig); + string memory toml = vm.serializeAddress("root", "owner_address", config.ownerAddress); + + vm.writeToml(toml, outputPath); + } + + function deployViaCreate2(bytes memory _bytecode) internal returns (address) { + return Utils.deployViaCreate2(_bytecode, config.contracts.create2FactorySalt, addresses.create2Factory); + } + + function prepareForceDeploymentsData() internal view returns (bytes memory) { + require(config.ownerAddress != address(0), "owner not set"); + + FixedForceDeploymentsData memory data = FixedForceDeploymentsData({ + l1ChainId: config.l1ChainId, + eraChainId: config.eraChainId, + l1AssetRouter: addresses.bridges.sharedBridgeProxy, + l2TokenProxyBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readBeaconProxyBytecode() + ), + aliasedL1Governance: AddressAliasHelper.applyL1ToL2Alias(config.ownerAddress), + maxNumberOfZKChains: config.contracts.maxNumberOfChains, + bridgehubBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readBridgehubBytecode()), + l2AssetRouterBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readL2AssetRouterBytecode() + ), + l2NtvBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readL2NativeTokenVaultBytecode() + ), + messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()), + l2SharedBridgeLegacyImpl: addresses.expectedL2Addresses.l2SharedBridgeLegacyImpl, + l2BridgedStandardERC20Impl: addresses.expectedL2Addresses.l2BridgedStandardERC20Impl, + l2BridgeProxyOwnerAddress: config.contracts.l2BridgeProxyOwnerAddress, + l2BridgedStandardERC20ProxyOwnerAddress: config.contracts.l2BridgedStandardERC20ProxyOwnerAddress + }); + + return abi.encode(data); + } + + // add this to be excluded from coverage report + function test() internal {} +} diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index c9a299f88..b4dc2ca3b 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -19,6 +19,8 @@ fs_permissions = [ { access = "read", path = "./out" }, { access = "read-write", path = "./test/foundry/l1/integration/deploy-scripts/script-config/" }, { access = "read-write", path = "./test/foundry/l1/integration/deploy-scripts/script-out/" }, + { access = "read-write", path = "./test/foundry/l1/integration/upgrade-envs/script-config/" }, + { access = "read-write", path = "./test/foundry/l1/integration/upgrade-envs/script-out/" }, { access = "read", path = "zkout" }, ] ignored_error_codes = ["missing-receive-ether", "code-size"] @@ -34,7 +36,7 @@ remappings = [ "@openzeppelin/contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/contracts/", ] optimizer = true -optimizer_runs = 9999999 +optimizer_runs = 200 [profile.default.zksync] enable_eravm_extensions = true zksolc = "1.5.3" diff --git a/l1-contracts/package.json b/l1-contracts/package.json index 6d68729e2..8a6896e86 100644 --- a/l1-contracts/package.json +++ b/l1-contracts/package.json @@ -58,10 +58,11 @@ "clean": "hardhat clean && CONTRACTS_BASE_NETWORK_ZKSYNC=true hardhat clean", "clean:foundry": "forge clean", "test": "yarn workspace da-contracts build && hardhat test test/unit_tests/*.spec.ts --network hardhat", - "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*'", - "test:zkfoundry": "forge test --zksync --match-path 'test/foundry/l2/*'", + "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*' --no-match-test 'test_MainnetFork'", + "test:zkfoundry": "forge script --sig 0x2dd0ebe3 DeployL1Script --ffi && forge test --zksync --match-path 'test/foundry/l2/*'", + "test:mainnet-upgrade-fork": "forge test --match-test test_MainnetFork --ffi --rpc-url $INFURA_MAINNET", "test:fork": "TEST_CONTRACTS_FORK=1 yarn run hardhat test test/unit_tests/*.fork.ts --network hardhat", - "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/bridge/.*L2.*.sol'", + "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/(bridge/.*L2.*\\.sol|governance/L2AdminFactory\\.sol)' --no-match-test test_MainnetFork", "deploy-no-build": "ts-node scripts/deploy.ts", "register-zk-chain": "ts-node scripts/register-zk-chain.ts", "deploy-weth-bridges": "ts-node scripts/deploy-weth-bridges.ts", diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 75aaac921..0c8ccc2cc 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -209,6 +209,10 @@ export class Deployer { l2AssetRouterBytecodeHash: ethers.utils.hexlify(hashL2Bytecode(assetRouterZKBytecode)), l2NtvBytecodeHash: ethers.utils.hexlify(hashL2Bytecode(nativeTokenVaultZKBytecode)), messageRootBytecodeHash: ethers.utils.hexlify(hashL2Bytecode(messageRootZKBytecode)), + l2SharedBridgeLegacyImpl: ethers.constants.AddressZero, + l2BridgedStandardERC20Impl: ethers.constants.AddressZero, + l2BridgeProxyOwnerAddress: ethers.constants.AddressZero, + l2BridgedStandardERC20ProxyOwnerAddress: ethers.constants.AddressZero, }; return ethers.utils.defaultAbiCoder.encode([FIXED_FORCE_DEPLOYMENTS_DATA_ABI_STRING], [fixedForceDeploymentsData]); diff --git a/l1-contracts/src.ts/utils.ts b/l1-contracts/src.ts/utils.ts index ce57958e0..190a5743c 100644 --- a/l1-contracts/src.ts/utils.ts +++ b/l1-contracts/src.ts/utils.ts @@ -47,7 +47,7 @@ export const FORCE_DEPLOYMENT_ABI_STRING = "tuple(bytes32 bytecodeHash, address newAddress, bool callConstructor, uint256 value, bytes input)[]"; export const BRIDGEHUB_CTM_ASSET_DATA_ABI_STRING = "tuple(uint256 chainId, bytes ctmData, bytes chainData)"; export const FIXED_FORCE_DEPLOYMENTS_DATA_ABI_STRING = - "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash)"; + "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash, address l2SharedBridgeLegacyImpl, address l2BridgedStandardERC20Impl, address l2BridgeProxyOwnerAddress, address l2BridgedStandardERC20ProxyOwnerAddress)"; export const ADDITIONAL_FORCE_DEPLOYMENTS_DATA_ABI_STRING = "tuple(bytes32 baseTokenAssetId, address l2Weth)"; export function applyL1ToL2Alias(address: string): string { @@ -321,7 +321,6 @@ export function compileInitialCutHash( admin: "0x0000000000000000000000000000000000003234", validatorTimelock: "0x0000000000000000000000000000000000004234", baseTokenAssetId: "0x0000000000000000000000000000000000000000000000000000000000004234", - baseTokenBridge: "0x0000000000000000000000000000000000004234", storedBatchZero: "0x0000000000000000000000000000000000000000000000000000000000005432", verifier, verifierParams, @@ -333,7 +332,7 @@ export function compileInitialCutHash( }, ]); - return diamondCut(facetCuts, diamondInit, "0x" + diamondInitCalldata.slice(2 + (4 + 9 * 32) * 2)); + return diamondCut(facetCuts, diamondInit, "0x" + diamondInitCalldata.slice(2 + (4 + 8 * 32) * 2)); } export enum PubdataSource { diff --git a/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol b/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol index 6eb78944d..5d23e1ad6 100644 --- a/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/BridgeHubInvariantTests.t.sol @@ -509,7 +509,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -565,7 +565,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -643,7 +643,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe } function getAddressesToExclude() public returns (address[] memory) { - addressesToExclude.push(bridgehubProxyAddress); + addressesToExclude.push(bridgeHubProxyAddress); addressesToExclude.push(address(sharedBridge)); for (uint256 i = 0; i < users.length; i++) { diff --git a/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol b/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol index 09b07cca0..a9e54825a 100644 --- a/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/BridgehubTests.t.sol @@ -509,7 +509,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -565,7 +565,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe }); vm.mockCall( - bridgehubProxyAddress, + bridgeHubProxyAddress, // solhint-disable-next-line func-named-parameters abi.encodeWithSelector( IBridgehub.proveL2MessageInclusion.selector, @@ -643,7 +643,7 @@ contract BridgeHubInvariantTests is L1ContractDeployer, ZKChainDeployer, TokenDe } function getAddressesToExclude() public returns (address[] memory) { - addressesToExclude.push(bridgehubProxyAddress); + addressesToExclude.push(bridgeHubProxyAddress); addressesToExclude.push(address(sharedBridge)); for (uint256 i = 0; i < users.length; i++) { diff --git a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol index 9224eea05..77aad90a5 100644 --- a/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/DeploymentTest.t.sol @@ -4,6 +4,8 @@ pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; +import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; + import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; @@ -22,6 +24,8 @@ import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAdd import {IL1ERC20Bridge} from "contracts/bridge/interfaces/IL1ERC20Bridge.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker { uint256 constant TEST_USERS_COUNT = 10; @@ -69,30 +73,26 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, // equals the balance of L1Shared bridge. function test_initialDeployment() public { uint256 chainId = zkChainIds[0]; - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - address newChainAddress = bridgehub.getZKChain(chainId); - address admin = IZKChain(bridgehub.getZKChain(chainId)).getAdmin(); - IChainTypeManager ctm = IChainTypeManager(bridgehub.chainTypeManager(chainId)); + address newChainAddress = bridgeHub.getZKChain(chainId); + address admin = IZKChain(bridgeHub.getZKChain(chainId)).getAdmin(); assertNotEq(admin, address(0)); assertNotEq(newChainAddress, address(0)); - address[] memory chainAddresses = bridgehub.getAllZKChains(); + address[] memory chainAddresses = bridgeHub.getAllZKChains(); assertEq(chainAddresses.length, 1); assertEq(chainAddresses[0], newChainAddress); - uint256[] memory chainIds = bridgehub.getAllZKChainChainIDs(); + uint256[] memory chainIds = bridgeHub.getAllZKChainChainIDs(); assertEq(chainIds.length, 1); assertEq(chainIds[0], chainId); - uint256 protocolVersion = ctm.getProtocolVersion(chainId); - assertEq(protocolVersion, 0); + uint256 protocolVersion = chainTypeManager.getProtocolVersion(chainId); + assertEq(protocolVersion, 25); } - function test_bridgehubSetter() public { - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); + function test_bridgeHubSetter() public { uint256 chainId = zkChainIds[0]; - IChainTypeManager chainTypeManager = IChainTypeManager(bridgehub.chainTypeManager(chainId)); uint256 randomChainId = 123456; vm.mockCall( @@ -100,14 +100,73 @@ contract DeploymentTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, abi.encodeWithSelector(IChainTypeManager.getZKChainLegacy.selector, randomChainId), abi.encode(address(0x01)) ); - vm.store(address(bridgehub), keccak256(abi.encode(randomChainId, 205)), bytes32(uint256(uint160(1)))); + vm.store(address(bridgeHub), keccak256(abi.encode(randomChainId, 205)), bytes32(uint256(uint160(1)))); vm.store( - address(bridgehub), + address(bridgeHub), keccak256(abi.encode(randomChainId, 204)), bytes32(uint256(uint160(address(chainTypeManager)))) ); - bridgehub.setLegacyBaseTokenAssetId(randomChainId); - bridgehub.setLegacyChainAddress(randomChainId); + bridgeHub.setLegacyBaseTokenAssetId(randomChainId); + bridgeHub.setLegacyChainAddress(randomChainId); + } + + function test_registerAlreadyDeployedZKChain() public { + address owner = Ownable(address(bridgeHub)).owner(); + + { + uint256 chainId = currentZKChainId++; + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); + + address chain = _deployZkChain( + chainId, + baseTokenAssetId, + owner, + chainTypeManager.protocolVersion(), + chainTypeManager.storedBatchZero(), + address(bridgeHub) + ); + + address stmAddr = IZKChain(chain).getChainTypeManager(); + + vm.startBroadcast(owner); + bridgeHub.addChainTypeManager(stmAddr); + bridgeHub.addTokenAssetId(baseTokenAssetId); + bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); + vm.stopBroadcast(); + + address bridgeHubStmForChain = bridgeHub.chainTypeManager(chainId); + bytes32 bridgeHubBaseAssetIdForChain = bridgeHub.baseTokenAssetId(chainId); + address bridgeHubChainAddressdForChain = bridgeHub.getZKChain(chainId); + address bhAddr = IZKChain(chain).getBridgehub(); + + assertEq(bridgeHubStmForChain, stmAddr); + assertEq(bridgeHubBaseAssetIdForChain, baseTokenAssetId); + assertEq(bridgeHubChainAddressdForChain, chain); + assertEq(bhAddr, address(bridgeHub)); + } + + { + uint256 chainId = currentZKChainId++; + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); + address chain = _deployZkChain( + chainId, + baseTokenAssetId, + owner, + chainTypeManager.protocolVersion(), + chainTypeManager.storedBatchZero(), + address(bridgeHub.sharedBridge()) + ); + + address stmAddr = IZKChain(chain).getChainTypeManager(); + + vm.startBroadcast(owner); + bridgeHub.addTokenAssetId(baseTokenAssetId); + vm.expectRevert( + abi.encodeWithSelector(IncorrectBridgeHubAddress.selector, address(bridgeHub.sharedBridge())) + ); + bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); + vm.stopBroadcast(); + } } // add this to be excluded from coverage report diff --git a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol similarity index 65% rename from l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol rename to l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol index 0cfcbf049..5fd43c169 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/L1GatewayTests.t.sol @@ -5,6 +5,8 @@ import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; import "forge-std/console.sol"; +import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; + import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter, BridgehubMintCTMAssetData, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {TestnetERC20Token} from "contracts/dev-contracts/TestnetERC20Token.sol"; import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; @@ -21,11 +23,13 @@ import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; import {L2Message} from "contracts/common/Messaging.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IL1ERC20Bridge} from "contracts/bridge/interfaces/IL1ERC20Bridge.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {FinalizeL1DepositParams} from "contracts/bridge/L1Nullifier.sol"; -import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; @@ -34,8 +38,9 @@ import {TxStatus} from "contracts/common/Messaging.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; -contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { +contract L1GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { uint256 constant TEST_USERS_COUNT = 10; address[] public users; address[] public l2ContractAddresses; @@ -85,15 +90,14 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T _initializeGatewayScript(); - vm.deal(Ownable(l1Script.getBridgehubProxyAddress()).owner(), 100000000000000000000000000000000000); - vm.deal(l1Script.getOwnerAddress(), 100000000000000000000000000000000000); - migratingChain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(migratingChainId)); - gatewayChain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(gatewayChainId)); + vm.deal(ecosystemConfig.ownerAddress, 100000000000000000000000000000000000); + migratingChain = IZKChain(IBridgehub(bridgeHub).getZKChain(migratingChainId)); + gatewayChain = IZKChain(IBridgehub(bridgeHub).getZKChain(gatewayChainId)); vm.deal(migratingChain.getAdmin(), 100000000000000000000000000000000000); vm.deal(gatewayChain.getAdmin(), 100000000000000000000000000000000000); // vm.deal(msg.sender, 100000000000000000000000000000000000); - // vm.deal(l1Script.getBridgehubProxyAddress(), 100000000000000000000000000000000000); + // vm.deal(bridgeHub, 100000000000000000000000000000000000); } // This is a method to simplify porting the tests for now. @@ -124,7 +128,7 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T _extractAccessControlRestriction(migratingChain.getAdmin()), migratingChainId ); - // require(bridgehub.settlementLayer()) + require(bridgeHub.settlementLayer(migratingChainId) == gatewayChainId, "Migration failed"); } function test_l2Registration() public { @@ -138,13 +142,14 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T gatewayScript.registerAssetIdInBridgehub(address(0x01), bytes32(0)); } - function test_finishMoveChain() public { - finishMoveChain(); - } - function test_startMessageToL3() public { - finishMoveChain(); - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); + IBridgehub bridgehub = IBridgehub(bridgeHub); uint256 expectedValue = 1000000000000000000000; L2TransactionRequestDirect memory request = _createL2TransactionRequestDirect( @@ -158,16 +163,6 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T bridgehub.requestL2TransactionDirect{value: expectedValue}(request); } - function test_forwardToL3OnGateway() public { - finishMoveChain(); - - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - vm.chainId(12345); - vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); - bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); - vm.stopBroadcast(); - } - function test_recoverFromFailedChainMigration() public { _setUpGatewayWithFilterer(); gatewayScript.migrateChainToGateway( @@ -177,16 +172,19 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T ); // Setup - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - IChainTypeManager ctm = IChainTypeManager(l1Script.getCTM()); + IBridgehub bridgehub = IBridgehub(bridgeHub); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); bytes memory transferData; { IZKChain chain = IZKChain(bridgehub.getZKChain(migratingChainId)); - bytes memory initialDiamondCut = l1Script.getInitialDiamondCutData(); bytes memory chainData = abi.encode(chain.getProtocolVersion()); - bytes memory ctmData = abi.encode(address(1), msg.sender, ctm.protocolVersion(), initialDiamondCut); + bytes memory ctmData = abi.encode( + address(1), + msg.sender, + chainTypeManager.protocolVersion(), + ecosystemConfig.contracts.diamondCutData + ); BridgehubBurnCTMAssetData memory data = BridgehubBurnCTMAssetData({ chainId: migratingChainId, ctmData: ctmData, @@ -244,74 +242,18 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T vm.stopBroadcast(); } - function test_registerAlreadyDeployedZKChain() public { - gatewayScript.governanceRegisterGateway(); - - IChainTypeManager stm = IChainTypeManager(l1Script.getCTM()); - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - address owner = Ownable(address(bridgeHub)).owner(); - - { - uint256 chainId = currentZKChainId++; - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); - - address chain = _deployZkChain( - chainId, - baseTokenAssetId, - address(bridgehub.sharedBridge()), - owner, - stm.protocolVersion(), - stm.storedBatchZero(), - address(bridgehub) - ); - - address stmAddr = IZKChain(chain).getChainTypeManager(); - - vm.startBroadcast(owner); - bridgeHub.addChainTypeManager(stmAddr); - bridgeHub.addTokenAssetId(baseTokenAssetId); - bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); - vm.stopBroadcast(); - - address bridgeHubStmForChain = bridgeHub.chainTypeManager(chainId); - bytes32 bridgeHubBaseAssetIdForChain = bridgeHub.baseTokenAssetId(chainId); - address bridgeHubChainAddressdForChain = bridgeHub.getZKChain(chainId); - address bhAddr = IZKChain(chain).getBridgehub(); - - assertEq(bridgeHubStmForChain, stmAddr); - assertEq(bridgeHubBaseAssetIdForChain, baseTokenAssetId); - assertEq(bridgeHubChainAddressdForChain, chain); - assertEq(bhAddr, address(bridgeHub)); - } - - { - uint256 chainId = currentZKChainId++; - bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(chainId, ETH_TOKEN_ADDRESS); - address chain = _deployZkChain( - chainId, - baseTokenAssetId, - address(bridgehub.sharedBridge()), - owner, - stm.protocolVersion(), - stm.storedBatchZero(), - address(bridgehub.sharedBridge()) - ); - - address stmAddr = IZKChain(chain).getChainTypeManager(); - - vm.startBroadcast(owner); - bridgeHub.addTokenAssetId(baseTokenAssetId); - vm.expectRevert( - abi.encodeWithSelector(IncorrectBridgeHubAddress.selector, address(bridgehub.sharedBridge())) - ); - bridgeHub.registerAlreadyDeployedZKChain(chainId, chain); - vm.stopBroadcast(); - } + function test_migrateBackChain() public { + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); + migrateBackChain(); } - function finishMoveChain() public { - IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); - IChainTypeManager ctm = IChainTypeManager(l1Script.getCTM()); + function migrateBackChain() public { + IBridgehub bridgehub = IBridgehub(bridgeHub); IZKChain migratingChain = IZKChain(bridgehub.getZKChain(migratingChainId)); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); @@ -319,27 +261,73 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T bridgehub.registerSettlementLayer(gatewayChainId, true); vm.stopBroadcast(); - bytes32 baseTokenAssetId = keccak256("baseTokenAssetId"); - bytes memory initialDiamondCut = l1Script.getInitialDiamondCutData(); - bytes memory chainData = abi.encode(AdminFacet(address(migratingChain)).prepareChainCommitment()); - bytes memory ctmData = abi.encode(baseTokenAssetId, msg.sender, ctm.protocolVersion(), initialDiamondCut); + bytes32 baseTokenAssetId = eraConfig.baseTokenAssetId; + + uint256 currentChainId = block.chainid; + // we are already on L1, so we have to set another chain id, it cannot be GW or mintChainId. + vm.chainId(migratingChainId); + vm.mockCall( + address(bridgeHub), + abi.encodeWithSelector(IBridgehub.proveL2MessageInclusion.selector), + abi.encode(true) + ); + vm.mockCall( + address(bridgehub), + abi.encodeWithSelector(IBridgehub.ctmAssetIdFromChainId.selector), + abi.encode(assetId) + ); + vm.mockCall( + address(chainTypeManager), + abi.encodeWithSelector(IChainTypeManager.protocolVersion.selector), + abi.encode(chainTypeManager.protocolVersion()) + ); + + uint256 protocolVersion = chainTypeManager.getProtocolVersion(migratingChainId); + + bytes memory chainData = abi.encode(IAdmin(address(migratingChain)).prepareChainCommitment()); + bytes memory ctmData = abi.encode( + baseTokenAssetId, + msg.sender, + protocolVersion, + ecosystemConfig.contracts.diamondCutData + ); BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ - chainId: mintChainId, + chainId: migratingChainId, baseTokenAssetId: baseTokenAssetId, ctmData: ctmData, chainData: chainData }); bytes memory bridgehubMintData = abi.encode(data); - vm.startBroadcast(address(bridgehub.sharedBridge())); - uint256 currentChainId = block.chainid; - vm.chainId(migratingChainId); - bridgehub.bridgeMint(gatewayChainId, assetId, bridgehubMintData); - vm.stopBroadcast(); + bytes memory message = abi.encodePacked( + IAssetRouterBase.finalizeDeposit.selector, + gatewayChainId, + assetId, + bridgehubMintData + ); + gatewayScript.finishMigrateChainFromGateway( + migratingChainId, + gatewayChainId, + 0, + 0, + 0, + message, + new bytes32[](0) + ); + vm.chainId(currentChainId); - assertEq(bridgehub.baseTokenAssetId(mintChainId), baseTokenAssetId); - IZKChain mintedZKChain = IZKChain(bridgehub.getZKChain(mintChainId)); - assertEq(mintedZKChain.getBaseTokenAssetId(), baseTokenAssetId); + assertEq(bridgehub.baseTokenAssetId(migratingChainId), baseTokenAssetId); + IZKChain migratingChainContract = IZKChain(bridgehub.getZKChain(migratingChainId)); + assertEq(migratingChainContract.getBaseTokenAssetId(), baseTokenAssetId); + } + + /// to increase coverage, properly tested in L2GatewayTests + function test_forwardToL3OnGateway() public { + _setUpGatewayWithFilterer(); + vm.chainId(12345); + vm.startBroadcast(SETTLEMENT_LAYER_RELAY_SENDER); + bridgeHub.forwardTransactionOnGateway(migratingChainId, bytes32(0), 0); + vm.stopBroadcast(); } // add this to be excluded from coverage report diff --git a/l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol b/l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol new file mode 100644 index 000000000..3cbcf454b --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors + +import {Script, console2 as console} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; + +import {EcosystemUpgrade} from "deploy-scripts/upgrade/EcosystemUpgrade.s.sol"; +import {ChainUpgrade} from "deploy-scripts/upgrade/ChainUpgrade.s.sol"; +import {Call} from "contracts/governance/Common.sol"; +import {Test} from "forge-std/Test.sol"; + +string constant ECOSYSTEM_INPUT = "/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml"; +string constant ECOSYSTEM_OUTPUT = "/test/foundry/l1/integration/upgrade-envs/script-out/mainnet.toml"; +string constant CHAIN_INPUT = "/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml"; +string constant CHAIN_OUTPUT = "/test/foundry/l1/integration/upgrade-envs/script-out/mainnet-era.toml"; + +contract UpgradeTest is Test { + EcosystemUpgrade generateUpgradeData; + ChainUpgrade chainUpgrade; + + function setUp() public { + generateUpgradeData = new EcosystemUpgrade(); + chainUpgrade = new ChainUpgrade(); + } + + function test_MainnetFork() public { + console.log("Preparing ecosystem contracts"); + // Firstly, we deploy all the contracts. + generateUpgradeData.prepareEcosystemContracts(ECOSYSTEM_INPUT, ECOSYSTEM_OUTPUT); + + // For chain, we have deployed the DA validator contracts + // and also updated the chain admin. + // IMPORTANT: for erc20-based chains with token multiplier setter + // this should be coordinated with the server. + console.log("Preparing chain for the upgrade"); + chainUpgrade.prepareChain(ECOSYSTEM_INPUT, ECOSYSTEM_OUTPUT, CHAIN_INPUT, CHAIN_OUTPUT); + + console.log("Starting stage1 of the upgrade!"); + // Now, some time has passed and we are ready to start the upgrade of the + // ecosystem. + // Stage 1 of the upgrade: + // - accept all the ownerships of the contracts + // - set the new upgrade data for chains + update validator timelock. + Call[] memory stage1Calls = mergeCalls( + generateUpgradeData.provideAcceptOwnershipCalls(), + generateUpgradeData.provideSetNewVersionUpgradeCall() + ); + + governanceMulticall(generateUpgradeData.getOwnerAddress(), stage1Calls); + + console.log("Stage1 is done, now all the chains have to upgrade to the new version"); + + console.log("Upgrading Era"); + + // Now, the admin of the Era needs to call the upgrade function. + // Note, that the step below also updated ValidatorTimelock so the server needs to be ready for that. + // TODO: We do not include calls that ensure that the server is ready for the sake of brevity. + chainUpgrade.upgradeChain( + generateUpgradeData.getOldProtocolVersion(), + generateUpgradeData.getChainUpgradeInfo() + ); + + // TODO: here we should include tests that depoists work for upgraded chains + // including era specific deposit/withdraw functions + // We also may need to test that normal flow of block commit / verify / execute works (but it is hard) + + vm.warp(generateUpgradeData.getOldProtocolDeadline()); + + console.log("Starting stage2 of the upgrade!"); + governanceMulticall(generateUpgradeData.getOwnerAddress(), generateUpgradeData.getStage2UpgradeCalls()); + + // TODO: here we should have tests that the bridging works for the previously deployed chains + // and that it does not work for those that did not upgrade. + // TODO: test that creation of new chains works under new conditions. + // TODO: if not hard, include test for deploying a gateway and migrating Era to it. + } + + /// @dev This is a contract that is used for additional visibility of transactions + /// that the decentralized governance should do. + function governanceMulticall(address governanceAddr, Call[] memory calls) internal { + // How the governance is implemented is out of scope here + vm.startBroadcast(governanceAddr); + + for (uint256 i = 0; i < calls.length; i++) { + Call memory call = calls[i]; + + (bool success, bytes memory data) = payable(call.target).call{value: call.value}(call.data); + require(success, "Multicall failed"); + } + + vm.stopBroadcast(); + } + + function mergeCalls(Call[] memory a, Call[] memory b) internal pure returns (Call[] memory result) { + result = new Call[](a.length + b.length); + for (uint256 i = 0; i < a.length; i++) { + result[i] = a[i]; + } + for (uint256 i = 0; i < b.length; i++) { + result[a.length + i] = b[i]; + } + } +} diff --git a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol b/l1-contracts/test/foundry/l1/integration/_GatewayPreparationForTests.sol similarity index 87% rename from l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol rename to l1-contracts/test/foundry/l1/integration/_GatewayPreparationForTests.sol index 271dfdf0c..26e754d9f 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol +++ b/l1-contracts/test/foundry/l1/integration/_GatewayPreparationForTests.sol @@ -22,12 +22,12 @@ contract GatewayPreparationForTests is GatewayPreparation { ); config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); - path = string.concat(root, vm.envString("GATEWAY_CONFIG")); + path = string.concat(root, vm.envString("GATEWAY_AS_CHAIN_CONFIG")); toml = vm.readFile(path); config.gatewayChainId = toml.readUint("$.chain.chain_chain_id"); - path = string.concat(root, vm.envString("GATEWAY_OUTPUT")); + path = string.concat(root, vm.envString("GATEWAY_AS_CHAIN_OUTPUT")); toml = vm.readFile(path); config.gatewayChainAdmin = toml.readAddress("$.chain_admin_addr"); @@ -35,6 +35,7 @@ contract GatewayPreparationForTests is GatewayPreparation { config.gatewayAccessControlRestriction = toml.readAddress( "$.deployed_addresses.access_control_restriction_addr" ); + config.l1NullifierProxy = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); console.log("chain chain id = ", config.gatewayChainId); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol index 66e0aa628..bd72835af 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; import {L1ContractDeployer} from "./_SharedL1ContractDeployer.t.sol"; -import {GatewayPreparationForTests} from "./GatewayPreparationForTests.sol"; +import {GatewayPreparationForTests} from "./_GatewayPreparationForTests.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import "@openzeppelin/contracts-v4/utils/Strings.sol"; @@ -17,11 +17,11 @@ contract GatewayDeployer is L1ContractDeployer { "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-10.toml" ); vm.setEnv( - "GATEWAY_CONFIG", + "GATEWAY_AS_CHAIN_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-11.toml" ); vm.setEnv( - "GATEWAY_OUTPUT", + "GATEWAY_AS_CHAIN_OUTPUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-11.toml" ); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index b180f6090..d7d7866d9 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -11,12 +11,18 @@ import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; contract L1ContractDeployer is Test { using stdStorage for StdStorage; - address bridgehubProxyAddress; - address bridgehubOwnerAddress; + DeployL1Script l1Script; + DeployedAddresses public ecosystemAddresses; + Config public ecosystemConfig; + + address bridgeHubProxyAddress; + address bridgeHubOwnerAddress; Bridgehub bridgeHub; CTMDeploymentTracker ctmDeploymentTracker; @@ -25,7 +31,7 @@ contract L1ContractDeployer is Test { L1Nullifier public l1Nullifier; L1NativeTokenVault public l1NativeTokenVault; - DeployL1Script l1Script; + IChainTypeManager chainTypeManager; function _deployL1Contracts() internal { vm.setEnv("L1_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml"); @@ -38,28 +44,29 @@ contract L1ContractDeployer is Test { "ZK_CHAIN_OUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-era.toml" ); + vm.setEnv( + "GATEWAY_PREPARATION_L1_CONFIG", + "/test/foundry/l1/integration/deploy-scripts/script-config/gateway-preparation-l1.toml" + ); l1Script = new DeployL1Script(); l1Script.runForTest(); - bridgehubProxyAddress = l1Script.getBridgehubProxyAddress(); - bridgeHub = Bridgehub(bridgehubProxyAddress); - - address sharedBridgeProxyAddress = l1Script.getSharedBridgeProxyAddress(); - sharedBridge = L1AssetRouter(sharedBridgeProxyAddress); - - address l1NullifierProxyAddress = l1Script.getL1NullifierProxyAddress(); - l1Nullifier = L1Nullifier(l1NullifierProxyAddress); + ecosystemAddresses = l1Script.getAddresses(); + ecosystemConfig = l1Script.getConfig(); - address l1NativeTokenVaultProxyAddress = l1Script.getNativeTokenVaultProxyAddress(); - l1NativeTokenVault = L1NativeTokenVault(payable(l1NativeTokenVaultProxyAddress)); + bridgeHub = Bridgehub(ecosystemAddresses.bridgehub.bridgehubProxy); + chainTypeManager = IChainTypeManager(ecosystemAddresses.stateTransition.chainTypeManagerProxy); + ctmDeploymentTracker = CTMDeploymentTracker(ecosystemAddresses.bridgehub.ctmDeploymentTrackerProxy); - ctmDeploymentTracker = CTMDeploymentTracker(l1Script.getCTMDeploymentTrackerAddress()); + sharedBridge = L1AssetRouter(ecosystemAddresses.bridges.sharedBridgeProxy); + l1Nullifier = L1Nullifier(ecosystemAddresses.bridges.l1NullifierProxy); + l1NativeTokenVault = L1NativeTokenVault(payable(ecosystemAddresses.vaults.l1NativeTokenVaultProxy)); _acceptOwnership(); _setEraBatch(); - bridgehubOwnerAddress = bridgeHub.owner(); + bridgeHubOwnerAddress = bridgeHub.owner(); } function _acceptOwnership() private { @@ -80,7 +87,7 @@ contract L1ContractDeployer is Test { function _registerNewToken(address _tokenAddress) internal { bytes32 tokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, _tokenAddress); if (!bridgeHub.assetIdIsRegistered(tokenAssetId)) { - vm.prank(bridgehubOwnerAddress); + vm.prank(bridgeHubOwnerAddress); bridgeHub.addTokenAssetId(tokenAssetId); } } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol index 6220818ef..d9b47ab06 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol @@ -13,6 +13,8 @@ import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; import {IDiamondInit} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; +import {Config as ChainConfig} from "deploy-scripts/RegisterZKChain.s.sol"; + contract ZKChainDeployer is L1ContractDeployer { using stdStorage for StdStorage; @@ -29,6 +31,8 @@ contract ZKChainDeployer is L1ContractDeployer { uint128 baseTokenGasPriceMultiplierDenominator; } + ChainConfig internal eraConfig; + uint256 currentZKChainId = 10; uint256 eraZKChainId = 9; uint256[] public zkChainIds; @@ -47,6 +51,7 @@ contract ZKChainDeployer is L1ContractDeployer { vm.warp(100); deployScript.runForTest(); zkChainIds.push(eraZKChainId); + eraConfig = deployScript.getConfig(); } function _deployZKChain(address _baseToken) internal { @@ -156,14 +161,13 @@ contract ZKChainDeployer is L1ContractDeployer { function _deployZkChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _sharedBridge, address _admin, uint256 _protocolVersion, bytes32 _storedBatchZero, address _bridgeHub ) internal returns (address) { Diamond.DiamondCutData memory diamondCut = abi.decode( - l1Script.getInitialDiamondCutData(), + ecosystemConfig.contracts.diamondCutData, (Diamond.DiamondCutData) ); bytes memory initData; @@ -178,7 +182,6 @@ contract ZKChainDeployer is L1ContractDeployer { bytes32(uint256(uint160(_admin))), bytes32(uint256(uint160(address(0x1337)))), _baseTokenAssetId, - bytes32(uint256(uint160(_sharedBridge))), _storedBatchZero, diamondCut.initCalldata ); diff --git a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml index 5683d081a..d52a6b704 100644 --- a/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml +++ b/l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml @@ -12,7 +12,7 @@ validator_timelock_execution_delay = 0 genesis_root = "0x1000000000000000000000000000000000000000000000000000000000000000" genesis_rollup_leaf_index = 1 genesis_batch_commitment = "0x1000000000000000000000000000000000000000000000000000000000000000" -latest_protocol_version = 0 +latest_protocol_version = 25 recursion_node_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" recursion_leaf_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" recursion_circuits_set_vks_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol new file mode 100644 index 000000000..b6346a09d --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20L1Test.t.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {L2Erc20TestAbstract} from "./L2Erc20TestAbstract.t.sol"; +import {SharedL2ContractL1DeployerUtils} from "./_SharedL2ContractL1DeployerUtils.sol"; + +contract L2Erc20L1Test is Test, SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer, L2Erc20TestAbstract { + function test() internal virtual override(DeployUtils, SharedL2ContractL1DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol similarity index 58% rename from l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol index 5e195353e..fcabf0d01 100644 --- a/l1-contracts/test/foundry/l2/unit/erc20/L2Erc20BridgeTest.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol @@ -14,71 +14,33 @@ import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol" import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; -import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import {L2Utils} from "../utils/L2Utils.sol"; +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; -contract L2Erc20BridgeTest is Test { - // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. - // It is a bit easier to use EOA and it is sufficient for the tests. - address internal l1BridgeWallet = address(1); - address internal aliasedL1BridgeWallet; - - // The owner of the beacon and the native token vault - address internal ownerWallet = address(2); - - BridgedStandardERC20 internal standardErc20Impl; - - UpgradeableBeacon internal beacon; - BeaconProxy internal proxy; - - uint256 internal constant L1_CHAIN_ID = 9; - uint256 internal ERA_CHAIN_ID = 270; - - // We won't actually deploy an L1 token in these tests, but we need some address for it. - address internal L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; - - string internal constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; - string internal constant TOKEN_DEFAULT_SYMBOL = "TET"; - uint8 internal constant TOKEN_DEFAULT_DECIMALS = 18; - - function setUp() public { - aliasedL1BridgeWallet = AddressAliasHelper.applyL1ToL2Alias(l1BridgeWallet); - - standardErc20Impl = new BridgedStandardERC20(); - beacon = new UpgradeableBeacon(address(standardErc20Impl)); - beacon.transferOwnership(ownerWallet); - - // One of the purposes of deploying it here is to publish its bytecode - BeaconProxy beaconProxy = new BeaconProxy(address(beacon), new bytes(0)); - proxy = beaconProxy; - bytes32 beaconProxyBytecodeHash; - assembly { - beaconProxyBytecodeHash := extcodehash(beaconProxy) - } - - L2Utils.initSystemContracts(); - L2Utils.forceDeployAssetRouter(L1_CHAIN_ID, ERA_CHAIN_ID, ownerWallet, l1BridgeWallet, address(0)); - L2Utils.forceDeployNativeTokenVault({ - _l1ChainId: L1_CHAIN_ID, - _aliasedOwner: ownerWallet, - _l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, - _legacySharedBridge: address(0), - _l2TokenBeacon: address(beacon), - _contractsDeployedAlready: true - }); - } +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +abstract contract L2Erc20TestAbstract is Test, SharedL2ContractDeployer { function performDeposit(address depositor, address receiver, uint256 amount) internal { - vm.prank(aliasedL1BridgeWallet); + vm.prank(aliasedL1AssetRouter); L2AssetRouter(L2_ASSET_ROUTER_ADDR).finalizeDeposit({ _l1Sender: depositor, _l2Receiver: receiver, _l1Token: L1_TOKEN_ADDRESS, _amount: amount, - _data: L2Utils.encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) + _data: encodeTokenData(TOKEN_DEFAULT_NAME, TOKEN_DEFAULT_SYMBOL, TOKEN_DEFAULT_DECIMALS) }); } diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol new file mode 100644 index 000000000..3e8b04e42 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayL1Test.t.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {L2GatewayTestAbstract} from "./L2GatewayTestAbstract.t.sol"; +import {SharedL2ContractL1DeployerUtils} from "./_SharedL2ContractL1DeployerUtils.sol"; + +contract L2GatewayL1Test is Test, SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer, L2GatewayTestAbstract { + function test() internal virtual override(DeployUtils, SharedL2ContractL1DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public virtual override(SharedL2ContractDeployer, SharedL2ContractL1DeployerUtils) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol new file mode 100644 index 000000000..e97c2017b --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR, L2_MESSENGER} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +abstract contract L2GatewayTestAbstract is Test, SharedL2ContractDeployer { + function test_gatewayShouldFinalizeDeposit() public { + finalizeDeposit(); + require(l2Bridgehub.ctmAssetIdFromAddress(address(chainTypeManager)) == ctmAssetId, "ctmAssetId mismatch"); + require(l2Bridgehub.ctmAssetIdFromChainId(mintChainId) == ctmAssetId, "ctmAssetIdFromChainId mismatch"); + } + + function test_forwardToL3OnGateway() public { + // todo fix this test + finalizeDeposit(); + vm.prank(SETTLEMENT_LAYER_RELAY_SENDER); + l2Bridgehub.forwardTransactionOnGateway(mintChainId, bytes32(0), 0); + } + + function test_withdrawFromGateway() public { + // todo fix this test + finalizeDeposit(); + address newAdmin = address(0x1); + bytes memory newDiamondCut = abi.encode(); + BridgehubBurnCTMAssetData memory data = BridgehubBurnCTMAssetData({ + chainId: mintChainId, + ctmData: abi.encode(newAdmin, config.contracts.diamondCutData), + chainData: abi.encode(chainTypeManager.protocolVersion()) + }); + vm.prank(ownerWallet); + vm.mockCall( + address(L2_MESSENGER), + abi.encodeWithSelector(L2_MESSENGER.sendToL1.selector), + abi.encode(bytes("")) + ); + l2AssetRouter.withdraw(ctmAssetId, abi.encode(data)); + } + + function finalizeDeposit() public { + bytes memory chainData = exampleChainCommitment; + bytes memory ctmData = abi.encode( + baseTokenAssetId, + ownerWallet, + chainTypeManager.protocolVersion(), + config.contracts.diamondCutData + ); + BridgehubMintCTMAssetData memory data = BridgehubMintCTMAssetData({ + chainId: mintChainId, + baseTokenAssetId: baseTokenAssetId, + ctmData: ctmData, + chainData: chainData + }); + vm.prank(aliasedL1AssetRouter); + l2AssetRouter.finalizeDeposit(L1_CHAIN_ID, ctmAssetId, abi.encode(data)); + } +} diff --git a/l1-contracts/test/foundry/l2/unit/weth/WETH.t.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol similarity index 61% rename from l1-contracts/test/foundry/l2/unit/weth/WETH.t.sol rename to l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol index 6cbc44fa7..d2a43be2e 100644 --- a/l1-contracts/test/foundry/l2/unit/weth/WETH.t.sol +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol @@ -2,33 +2,38 @@ pragma solidity ^0.8.20; -import {Test} from "forge-std/Test.sol"; - -import {L2WrappedBaseToken} from "contracts/bridge/L2WrappedBaseToken.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import {Unauthorized, UnimplementedMessage, BridgeMintNotImplemented} from "contracts/common/L1ContractErrors.sol"; +// solhint-disable gas-custom-errors -contract WethTest is Test { - L2WrappedBaseToken internal weth; +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; - // The owner of the proxy - address internal ownerWallet = address(2); +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; - address internal l2BridgeAddress = address(3); - address internal l1Address = address(4); +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; - function setUp() public { - ownerWallet = makeAddr("owner"); - L2WrappedBaseToken impl = new L2WrappedBaseToken(); +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(impl), ownerWallet, ""); +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; - weth = L2WrappedBaseToken(payable(proxy)); +import {SharedL2ContractDeployer} from "./_SharedL2ContractDeployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; - weth.initializeV2("Wrapped Ether", "WETH", l2BridgeAddress, l1Address); - } +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {Unauthorized, UnimplementedMessage, BridgeMintNotImplemented} from "contracts/common/L1ContractErrors.sol"; +abstract contract L2WethTestAbstract is Test, SharedL2ContractDeployer { function test_shouldDepositWethByCallingDeposit() public { uint256 amount = 100; weth.deposit{value: amount}(); @@ -102,7 +107,7 @@ contract WethTest is Test { function test_revertWhenCallingBridgeMint() public { vm.expectRevert(abi.encodeWithSelector(BridgeMintNotImplemented.selector)); - vm.prank(l2BridgeAddress); + vm.prank(L2_ASSET_ROUTER_ADDR); weth.bridgeMint(address(1), 1); } diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol new file mode 100644 index 000000000..8892bd21f --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2WrappedBaseToken} from "contracts/bridge/L2WrappedBaseToken.sol"; +import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; + +// import {L2ContractL1Deployer} from "./_SharedL2ContractL1Deployer.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {SystemContractsArgs} from "./_SharedL2ContractL1DeployerUtils.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +abstract contract SharedL2ContractDeployer is Test, DeployUtils { + L2WrappedBaseToken internal weth; + address internal l1WethAddress = address(4); + + // The owner of the beacon and the native token vault + address internal ownerWallet = address(2); + + BridgedStandardERC20 internal standardErc20Impl; + + UpgradeableBeacon internal beacon; + BeaconProxy internal proxy; + + IL2AssetRouter l2AssetRouter = IL2AssetRouter(L2_ASSET_ROUTER_ADDR); + IBridgehub l2Bridgehub = IBridgehub(L2_BRIDGEHUB_ADDR); + + uint256 internal constant L1_CHAIN_ID = 10; // it cannot be 9, the default block.chainid + uint256 internal ERA_CHAIN_ID = 270; + uint256 internal mintChainId = 300; + address internal l1AssetRouter = makeAddr("l1AssetRouter"); + address internal aliasedL1AssetRouter = AddressAliasHelper.applyL1ToL2Alias(l1AssetRouter); + + // We won't actually deploy an L1 token in these tests, but we need some address for it. + address internal L1_TOKEN_ADDRESS = 0x1111100000000000000000000000000000011111; + + string internal constant TOKEN_DEFAULT_NAME = "TestnetERC20Token"; + string internal constant TOKEN_DEFAULT_SYMBOL = "TET"; + uint8 internal constant TOKEN_DEFAULT_DECIMALS = 18; + address internal l1CTMDeployer = makeAddr("l1CTMDeployer"); + address internal l1CTM = makeAddr("l1CTM"); + bytes32 internal ctmAssetId = keccak256(abi.encode(L1_CHAIN_ID, l1CTMDeployer, bytes32(uint256(uint160(l1CTM))))); + + bytes32 internal baseTokenAssetId = + keccak256(abi.encode(L1_CHAIN_ID, L2_NATIVE_TOKEN_VAULT_ADDR, abi.encode(ETH_TOKEN_ADDRESS))); + + bytes internal exampleChainCommitment; + + IChainTypeManager internal chainTypeManager; + + function setUp() public { + standardErc20Impl = new BridgedStandardERC20(); + beacon = new UpgradeableBeacon(address(standardErc20Impl)); + beacon.transferOwnership(ownerWallet); + + // One of the purposes of deploying it here is to publish its bytecode + BeaconProxy beaconProxy = new BeaconProxy(address(beacon), new bytes(0)); + proxy = beaconProxy; + bytes32 beaconProxyBytecodeHash; + assembly { + beaconProxyBytecodeHash := extcodehash(beaconProxy) + } + + address l2SharedBridge = deployL2SharedBridgeLegacy( + L1_CHAIN_ID, + ERA_CHAIN_ID, + ownerWallet, + l1AssetRouter, + beaconProxyBytecodeHash + ); + + L2WrappedBaseToken weth = deployL2Weth(); + + initSystemContracts( + SystemContractsArgs({ + l1ChainId: L1_CHAIN_ID, + eraChainId: ERA_CHAIN_ID, + l1AssetRouter: l1AssetRouter, + legacySharedBridge: l2SharedBridge, + l2TokenBeacon: address(beacon), + l2TokenProxyBytecodeHash: beaconProxyBytecodeHash, + aliasedOwner: ownerWallet, + contractsDeployedAlready: false, + l1CtmDeployer: l1CTMDeployer + }) + ); + deployL2Contracts(L1_CHAIN_ID); + + vm.prank(aliasedL1AssetRouter); + l2AssetRouter.setAssetHandlerAddress(L1_CHAIN_ID, ctmAssetId, L2_BRIDGEHUB_ADDR); + vm.prank(ownerWallet); + l2Bridgehub.addChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); + vm.prank(AddressAliasHelper.applyL1ToL2Alias(l1CTMDeployer)); + l2Bridgehub.setAssetHandlerAddress( + bytes32(uint256(uint160(l1CTM))), + address(addresses.stateTransition.chainTypeManagerProxy) + ); + chainTypeManager = IChainTypeManager(address(addresses.stateTransition.chainTypeManagerProxy)); + getExampleChainCommitment(); + } + + function getExampleChainCommitment() internal returns (bytes memory) { + vm.mockCall( + L2_ASSET_ROUTER_ADDR, + abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), + abi.encode(L2_ASSET_ROUTER_ADDR) + ); + vm.mockCall( + L2_ASSET_ROUTER_ADDR, + abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), + abi.encode(address(0)) + ); + vm.prank(L2_BRIDGEHUB_ADDR); + address chainAddress = chainTypeManager.createNewChain( + ERA_CHAIN_ID + 1, + baseTokenAssetId, + address(0x1), + abi.encode(config.contracts.diamondCutData, generatedData.forceDeploymentsData), + new bytes[](0) + ); + exampleChainCommitment = abi.encode(IZKChain(chainAddress).prepareChainCommitment()); + } + + /// @notice Encodes the token data. + /// @param name The name of the token. + /// @param symbol The symbol of the token. + /// @param decimals The decimals of the token. + function encodeTokenData( + string memory name, + string memory symbol, + uint8 decimals + ) internal pure returns (bytes memory) { + bytes memory encodedName = abi.encode(name); + bytes memory encodedSymbol = abi.encode(symbol); + bytes memory encodedDecimals = abi.encode(decimals); + + return abi.encode(encodedName, encodedSymbol, encodedDecimals); + } + + function deployL2SharedBridgeLegacy( + uint256 _l1ChainId, + uint256 _eraChainId, + address _aliasedOwner, + address _l1SharedBridge, + bytes32 _l2TokenProxyBytecodeHash + ) internal returns (address) { + bytes32 ethAssetId = DataEncoding.encodeNTVAssetId(_l1ChainId, ETH_TOKEN_ADDRESS); + + L2SharedBridgeLegacy bridge = new L2SharedBridgeLegacy(); + console.log("bridge", address(bridge)); + address proxyAdmin = address(0x1); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(bridge), + proxyAdmin, + abi.encodeWithSelector( + L2SharedBridgeLegacy.initialize.selector, + _l1SharedBridge, + _l2TokenProxyBytecodeHash, + _aliasedOwner + ) + ); + console.log("proxy", address(proxy)); + return address(proxy); + } + + function deployL2Weth() internal returns (L2WrappedBaseToken) { + L2WrappedBaseToken wethImpl = new L2WrappedBaseToken(); + TransparentUpgradeableProxy wethProxy = new TransparentUpgradeableProxy(address(wethImpl), ownerWallet, ""); + weth = L2WrappedBaseToken(payable(wethProxy)); + weth.initializeV2("Wrapped Ether", "WETH", L2_ASSET_ROUTER_ADDR, l1WethAddress); + return weth; + } + + function initSystemContracts(SystemContractsArgs memory _args) internal virtual; + function deployL2Contracts(uint256 _l1ChainId) public virtual; +} diff --git a/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol new file mode 100644 index 000000000..8c0e7e099 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {StdStorage, stdStorage, stdToml} from "forge-std/Test.sol"; +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +import {L2_MESSAGE_ROOT_ADDR, L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {L2NativeTokenVaultDev} from "contracts/dev-contracts/test/L2NativeTokenVaultDev.sol"; +import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; + +struct SystemContractsArgs { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + address legacySharedBridge; + address l2TokenBeacon; + bytes32 l2TokenProxyBytecodeHash; + address aliasedOwner; + bool contractsDeployedAlready; + address l1CtmDeployer; +} + +contract SharedL2ContractL1DeployerUtils is DeployUtils { + using stdToml for string; + using stdStorage for StdStorage; + + function initSystemContracts(SystemContractsArgs memory _args) internal virtual { + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(_args.l1ChainId, ETH_TOKEN_ADDRESS); + address wethToken = address(0x1); + + address messageRoot = address(new MessageRoot(IBridgehub(L2_BRIDGEHUB_ADDR))); + address bridgehub = address(new Bridgehub(_args.l1ChainId, _args.aliasedOwner, 100)); + address assetRouter = address( + new L2AssetRouter( + _args.l1ChainId, + _args.eraChainId, + _args.l1AssetRouter, + _args.legacySharedBridge, + baseTokenAssetId, + _args.aliasedOwner + ) + ); + address ntv = address( + new L2NativeTokenVaultDev( + _args.l1ChainId, + _args.aliasedOwner, + _args.l2TokenProxyBytecodeHash, + _args.legacySharedBridge, + _args.l2TokenBeacon, + _args.contractsDeployedAlready, + wethToken, + baseTokenAssetId + ) + ); + + vm.etch(L2_MESSAGE_ROOT_ADDR, messageRoot.code); + MessageRoot(L2_MESSAGE_ROOT_ADDR).initialize(); + + vm.etch(L2_BRIDGEHUB_ADDR, bridgehub.code); + uint256 prevChainId = block.chainid; + vm.chainId(_args.l1ChainId); + Bridgehub(L2_BRIDGEHUB_ADDR).initialize(_args.aliasedOwner); + vm.chainId(prevChainId); + vm.prank(_args.aliasedOwner); + Bridgehub(L2_BRIDGEHUB_ADDR).setAddresses( + L2_ASSET_ROUTER_ADDR, + ICTMDeploymentTracker(_args.l1CtmDeployer), + IMessageRoot(L2_MESSAGE_ROOT_ADDR) + ); + + vm.etch(L2_ASSET_ROUTER_ADDR, assetRouter.code); + stdstore.target(address(L2_ASSET_ROUTER_ADDR)).sig("l1AssetRouter()").checked_write(_args.l1AssetRouter); + + stdstore + .target(L2_ASSET_ROUTER_ADDR) + .sig("assetHandlerAddress(bytes32)") + .with_key(baseTokenAssetId) + .checked_write(bytes32(uint256(uint160(L2_NATIVE_TOKEN_VAULT_ADDR)))); + + vm.etch(L2_NATIVE_TOKEN_VAULT_ADDR, ntv.code); + + vm.store(L2_NATIVE_TOKEN_VAULT_ADDR, bytes32(uint256(251)), bytes32(uint256(_args.l2TokenProxyBytecodeHash))); + L2NativeTokenVaultDev(L2_NATIVE_TOKEN_VAULT_ADDR).deployBridgedStandardERC20(_args.aliasedOwner); + } + + function deployL2Contracts(uint256 _l1ChainId) public virtual { + string memory root = vm.projectRoot(); + string memory inputPath = string.concat( + root, + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml" + ); + initializeConfig(inputPath); + addresses.transparentProxyAdmin = address(0x1); + addresses.bridgehub.bridgehubProxy = L2_BRIDGEHUB_ADDR; + addresses.bridges.sharedBridgeProxy = L2_ASSET_ROUTER_ADDR; + addresses.vaults.l1NativeTokenVaultProxy = L2_NATIVE_TOKEN_VAULT_ADDR; + addresses.blobVersionedHashRetriever = address(0x1); + config.l1ChainId = _l1ChainId; + console.log("Deploying L2 contracts"); + instantiateCreate2Factory(); + deployGenesisUpgrade(); + deployVerifier(); + deployValidatorTimelock(); + deployChainTypeManagerContract(); + } + + // add this to be excluded from coverage report + function test() internal virtual override {} +} diff --git a/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml new file mode 100644 index 000000000..d48013384 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml @@ -0,0 +1,7 @@ +owner_address = "0x4e4943346848c4867f81dfb37c4ca9c5715a7828" + +[chain] +chain_id = 324 +diamond_proxy_address = "0x32400084c286cf3e17e7b677ea9583e60a000324" +validium_mode = false +permanent_rollup = true diff --git a/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml new file mode 100644 index 000000000..abf681e37 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml @@ -0,0 +1,37 @@ +era_chain_id = 324 +owner_address = "8f7a9912416e8adc4d9c21fae1415d3318a11897" +testnet_verifier = false + +[contracts] +max_number_of_chains = 100 +create2_factory_salt = "0xde6b9c610417de5c775c1601c947f482e4f4e30c0f7b848c6d2b0554d76f607e" +validator_timelock_execution_delay = 0 +genesis_root = "0xf9030b78c5bf5ac997a76962aa32c90a6d8e8ebce9838c8eeb388d73e1f7659a" +genesis_rollup_leaf_index = 64 +genesis_batch_commitment = "0x34c1b220363e0cde7eaf10fe95754d61de097e0f9d9a1dc56c8026562e395259" +latest_protocol_version = "0x1900000000" +recursion_node_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +recursion_leaf_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +recursion_circuits_set_vks_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +priority_tx_max_gas_limit = 72000000 +diamond_init_pubdata_pricing_mode = 0 +diamond_init_batch_overhead_l1_gas = 1000000 +diamond_init_max_pubdata_per_batch = 120000 +diamond_init_max_l2_gas_per_batch = 80000000 +diamond_init_priority_tx_max_pubdata = 99000 +diamond_init_minimal_l2_gas_price = 250000000 +bootloader_hash = "0x010008c753336bc8d1ddca235602b9f31d346412b2d463cd342899f7bfb73baf" +default_aa_hash = "0x0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e" +bridgehub_proxy_address = "0x303a465B659cBB0ab36eE643eA362c509EEb5213" +old_shared_bridge_proxy_address = "0xD7f9f54194C633F36CCD5F3da84ad4a1c38cB2cB" +state_transition_manager_address = "0xc2eE6b6af7d616f6e27ce7F4A451Aedc2b0F5f5C" +transparent_proxy_admin = "0xC2a36181fB524a6bEfE639aFEd37A67e77d62cf1" +era_diamond_proxy = "0x32400084c286cf3e17e7b677ea9583e60a000324" +blob_versioned_hash_retriever = "0x0000000000000000000000000000000000000001" +legacy_erc20_bridge_address = "0x57891966931eb4bb6fb81430e6ce0a03aabde063" +old_validator_timelock = "0x5D8ba173Dc6C3c90C8f7C04C9288BeF5FDbAd06E" +l2_bridge_proxy_owner_address = "0x0000000000000000000000000000000000000001" +l2_bridged_standard_erc20_proxy_owner_address = "0x0000000000000000000000000000000000000001" + +[tokens] +token_weth_address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" diff --git a/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/.gitkeep b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol index d9675912a..6ffbbc0e9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -439,9 +439,10 @@ contract ExperimentalBridgeTest is Test { ); if (randomAddress != address(testTokenAddress)) { + assetId = DataEncoding.encodeNTVAssetId(block.chainid, address(randomAddress)); + vm.assume(!bridgeHub.assetIdIsRegistered(assetId)); // Testing to see if a random address can also be added or not vm.prank(bridgeOwner); - assetId = DataEncoding.encodeNTVAssetId(block.chainid, address(randomAddress)); bridgeHub.addTokenAssetId(assetId); assertTrue(bridgeHub.assetIdIsRegistered(assetId)); } @@ -787,7 +788,6 @@ contract ExperimentalBridgeTest is Test { mockCTM.createNewChain.selector, chainId, tokenAssetId, - sharedBridgeAddress, admin, mockInitCalldata, factoryDeps diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol index 0131721a0..0603a34dc 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol @@ -8,6 +8,7 @@ import {L1AssetRouterTest} from "./_L1SharedBridge_Shared.t.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; @@ -20,7 +21,7 @@ import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IGetters} from "contracts/state-transition/chain-interfaces/IGetters.sol"; -import {AddressAlreadyUsed, WithdrawFailed, Unauthorized, AssetIdNotSupported, SharedBridgeKey, SharedBridgeValueNotSet, L2WithdrawalMessageWrongLength, InsufficientChainBalance, ZeroAddress, ValueMismatch, NonEmptyMsgValue, DepositExists, ValueMismatch, NonEmptyMsgValue, TokenNotSupported, EmptyDeposit, L2BridgeNotDeployed, DepositIncorrectAmount, InvalidProof, NoFundsTransferred, InsufficientFunds, DepositDoesNotExist, WithdrawalAlreadyFinalized, InsufficientFunds, MalformedMessage, InvalidSelector, TokensWithFeesNotSupported} from "contracts/common/L1ContractErrors.sol"; +import {AddressAlreadyUsed, WithdrawFailed, Unauthorized, AssetIdNotSupported, SharedBridgeKey, SharedBridgeValueNotSet, L2WithdrawalMessageWrongLength, InsufficientChainBalance, ZeroAddress, ValueMismatch, NonEmptyMsgValue, DepositExists, ValueMismatch, NonEmptyMsgValue, TokenNotSupported, EmptyDeposit, L2BridgeNotDeployed, InvalidProof, NoFundsTransferred, InsufficientFunds, DepositDoesNotExist, WithdrawalAlreadyFinalized, InsufficientFunds, MalformedMessage, InvalidSelector, TokensWithFeesNotSupported} from "contracts/common/L1ContractErrors.sol"; import {StdStorage, stdStorage} from "forge-std/Test.sol"; /// We are testing all the specified revert and require cases. @@ -96,37 +97,23 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.setNativeTokenVault(INativeTokenVault(address(0))); } - // function test_setAssetHandlerAddressOnCounterpart_notOwnerOrADT() public { - // uint256 l2TxGasLimit = 100000; - // uint256 l2TxGasPerPubdataByte = 100; - // address refundRecipient = address(0); - - // vm.prank(alice); - // vm.expectRevert("L1N: only ADT or owner"); - // sharedBridge.setAssetHandlerAddressOnCounterpart( - // eraChainId, - // mintValue, - // l2TxGasLimit, - // l2TxGasPerPubdataByte, - // refundRecipient, - // tokenAssetId, - // address(token) - // ); - // } - - // function test_transferFundsToSharedBridge_Eth_CallFailed() public { - // vm.mockCall(address(nativeTokenVault), "0x", abi.encode("")); - // vm.prank(address(nativeTokenVault)); - // vm.expectRevert("L1N: eth transfer failed"); - // nativeTokenVault.transferFundsFromSharedBridge(ETH_TOKEN_ADDRESS); - // } - - // function test_transferFundsToSharedBridge_Eth_CallFailed() public { - // vm.mockCall(address(nativeTokenVault), "0x", abi.encode("")); - // vm.prank(address(nativeTokenVault)); - // vm.expectRevert("L1N: eth transfer failed"); - // nativeTokenVault.transferFundsFromSharedBridge(ETH_TOKEN_ADDRESS); - // } + function test_setAssetHandlerAddressOnCounterpart_wrongCounterPartAddress() public { + bytes memory data = bytes.concat( + SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION, + abi.encode(tokenAssetId, address(token)) + ); + + vm.prank(bridgehubAddress); + vm.expectRevert("NTV: wrong counterpart"); + sharedBridge.bridgehubDeposit(eraChainId, owner, 0, data); + } + + function test_transferFundsToSharedBridge_Eth_CallFailed() public { + vm.mockCallRevert(address(nativeTokenVault), "", "eth transfer failed"); + vm.prank(address(nativeTokenVault)); + vm.expectRevert("L1N: eth transfer failed"); + l1Nullifier.transferTokenToNTV(ETH_TOKEN_ADDRESS); + } function test_transferFundsToSharedBridge_Eth_0_AmountTransferred() public { vm.deal(address(l1Nullifier), 0); @@ -155,12 +142,12 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.bridgehubDepositBaseToken{value: amount}(chainId, ETH_TOKEN_ASSET_ID, alice, amount); } - // function test_bridgehubDepositBaseToken_EthwrongMsgValue() public { - // vm.deal(bridgehubAddress, amount); - // vm.prank(bridgehubAddress); - // vm.expectRevert(abi.encodeWithSelector(ValueMismatch.selector, amount, uint256(1))); - // sharedBridge.bridgehubDepositBaseToken(chainId, ETH_TOKEN_ASSET_ID, alice, amount); - // } + function test_bridgehubDepositBaseToken_EthwrongMsgValue() public { + vm.deal(bridgehubAddress, amount); + vm.prank(bridgehubAddress); + vm.expectRevert(abi.encodeWithSelector(ValueMismatch.selector, amount, uint256(1))); + sharedBridge.bridgehubDepositBaseToken{value: 1}(chainId, ETH_TOKEN_ASSET_ID, alice, amount); + } function test_bridgehubDepositBaseToken_ErcWrongMsgValue() public { vm.deal(bridgehubAddress, amount); @@ -199,18 +186,18 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.bridgehubDeposit(chainId, alice, 0, abi.encode(ETH_TOKEN_ADDRESS, 0, bob)); } - // function test_bridgehubDeposit_Eth_wrongDepositAmount() public { - // _setBaseTokenAssetId(tokenAssetId); - // vm.prank(bridgehubAddress); - // vm.mockCall( - // bridgehubAddress, - // abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector), - // abi.encode(tokenAssetId) - // ); - // vm.expectRevert(abi.encodeWithSelector(DepositIncorrectAmount.selector, 0, amount)); - // // solhint-disable-next-line func-named-parameters - // sharedBridge.bridgehubDeposit(chainId, alice, 0, abi.encode(ETH_TOKEN_ADDRESS, amount, bob)); - // } + function test_bridgehubDeposit_Eth_wrongDepositAmount() public { + _setBaseTokenAssetId(tokenAssetId); + vm.prank(bridgehubAddress); + vm.mockCall( + bridgehubAddress, + abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector), + abi.encode(tokenAssetId) + ); + vm.expectRevert(abi.encodeWithSelector(ValueMismatch.selector, amount, 0)); + // solhint-disable-next-line func-named-parameters + sharedBridge.bridgehubDeposit(chainId, alice, 0, abi.encode(ETH_TOKEN_ADDRESS, amount, bob)); + } function test_bridgehubDeposit_Erc_msgValue() public { vm.prank(bridgehubAddress); @@ -623,27 +610,31 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { }); } - // function test_finalizeWithdrawal_TokenOnEth_legacyUpgradeFirstBatchNotSet() public { - // vm.store(address(sharedBridge), bytes32(isWithdrawalFinalizedStorageLocation - 6), bytes32(uint256(0))); - // vm.deal(address(sharedBridge), amount); - - // bytes memory message = abi.encodePacked( - // IL1ERC20Bridge.finalizeWithdrawal.selector, - // alice, - // address(token), - // amount - // ); - - // vm.expectRevert(abi.encodeWithSelector(SharedBridgeValueNotSet.selector, SharedBridgeKey.PostUpgradeFirstBatch)); - // sharedBridge.finalizeWithdrawal({ - // _chainId: eraChainId, - // _l2BatchNumber: l2BatchNumber, - // _l2MessageIndex: l2MessageIndex, - // _l2TxNumberInBatch: l2TxNumberInBatch, - // _message: message, - // _merkleProof: merkleProof - // }); - // } + function test_finalizeWithdrawal_TokenOnEth_legacyUpgradeFirstBatchNotSet() public { + vm.store(address(l1Nullifier), bytes32(isWithdrawalFinalizedStorageLocation - 7), bytes32(uint256(0))); + vm.deal(address(nativeTokenVault), amount); + + bytes memory message = abi.encodePacked( + IL1ERC20Bridge.finalizeWithdrawal.selector, + alice, + address(token), + amount + ); + + vm.mockCall(bridgehubAddress, abi.encode(IBridgehub.proveL2MessageInclusion.selector), abi.encode(true)); + + vm.expectRevert( + abi.encodeWithSelector(SharedBridgeValueNotSet.selector, SharedBridgeKey.PostUpgradeFirstBatch) + ); + sharedBridge.finalizeWithdrawal({ + _chainId: eraChainId, + _l2BatchNumber: l2BatchNumber, + _l2MessageIndex: l2MessageIndex, + _l2TxNumberInBatch: l2TxNumberInBatch, + _message: message, + _merkleProof: merkleProof + }); + } function test_finalizeWithdrawal_chainBalance() public { bytes memory message = abi.encodePacked(IMailbox.finalizeEthWithdrawal.selector, alice, amount); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol index 4645bcb2b..8823f20b9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol @@ -84,7 +84,6 @@ contract UpgradeLogicTest is DiamondCutTest { admin: admin, validatorTimelock: makeAddr("validatorTimelock"), baseTokenAssetId: DataEncoding.encodeNTVAssetId(1, (makeAddr("baseToken"))), - baseTokenBridge: makeAddr("baseTokenBridge"), storedBatchZero: bytes32(0), // genesisBatchHash: 0x02c775f0a90abf7a0e8043f2fdc38f0580ca9f9996a895d05a501bfeaa3b2e21, // genesisIndexRepeatedStorageChanges: 0, diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol index af6e9f3a5..9f4530cc4 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol @@ -72,60 +72,55 @@ contract ExecutorProofTest is Test { utilsFacet = UtilsFacet(diamondProxy); } // todo - /// This test is based on a block generated in a local system. - // function test_Hashes() public { - // utilsFacet.util_setL2DefaultAccountBytecodeHash( - // 0x0100065d134a862a777e50059f5e0fbe68b583f3617a67820f7edda0d7f253a0 - // ); - // utilsFacet.util_setL2BootloaderBytecodeHash(0x010009416e909e0819593a9806bbc841d25c5cdfed3f4a1523497c6814e5194a); - // utilsFacet.util_setZkPorterAvailability(false); - - // IExecutor.CommitBatchInfo memory nextBatch = IExecutor.CommitBatchInfo({ - // // ignored - // batchNumber: 1, - // // ignored - // timestamp: 100, - // indexRepeatedStorageChanges: 84, - // newStateRoot: 0x9cf7bb72401a56039ca097cabed20a72221c944ed9b0e515c083c04663ab45a6, - // // ignored - // numberOfLayer1Txs: 10, - // // ignored - // priorityOperationsHash: 0x167f4ca80269c9520ad951eeeda28dd3deb0715e9e2917461e81a60120a14183, - // bootloaderHeapInitialContentsHash: 0x540442e48142fa061a81822184f7790e7b69dea92153d38ef623802c6f0411c0, - // eventsQueueStateHash: 0xda42ab7994d4695a25f4ea8a9a485a592b7a31c20d5dae6363828de86d8826ea, - // systemLogs: abi.encodePacked( - // hex"00000000000000000000000000000000000000000000800b000000000000000000000000000000000000000000000000000000000000000416914ac26bb9cafa0f1dfaeaab10745a9094e1b60c7076fedf21651d6a25b5740000000a000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000651bcde0000000000000000000000000651bcde20001000a00000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000005167f4ca80269c9520ad951eeeda28dd3deb0715e9e2917461e81a60120a141830001000a00000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0001000a00000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000ee6ee8f50659bd8be3d86c32efb02baa5571cf3b46dd7ea3db733ae181747b8b0001000a0000000000000000000000000000000000008008000000000000000000000000000000000000000000000000000000000000000160fc5fb513ca8e6f6232a7410797954dcb6edbf9081768da24b483aca91c54db0001000a000000000000000000000000000000000000800800000000000000000000000000000000000000000000000000000000000000029a67073c2df8f53087fcfc32d82c98bba591da35df6ce1fb55a23b677d37f9fc000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080110000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801100000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000" - // ), - // operatorDAInput: abi.encodePacked( - // hex"000000000a000100000000000000000000000000000000000000008001760f6100ddbd86c4d5a58532923e7424d33ffb44145a26171d9b2595a349450b0000000000000000000000000000000000000000000000000000000000000001000100010000000000000000000000000000000000008001a789fe4e2a955eee45d44f408f86203c8f643910bf4888d1fd1465cdbc6376d800000000000000000000000000000000000000000000000000000000000000010001000200000000000000000000000000000000000080016ba43e7c7df11e5a655f22c9bce1b37434afd2bf8fcdb10100a460e6a2c0cc83000000000000000000000000000000000000000000000000000000000000000100010003000000000000000000000000000000000000800156e569838658c17c756aa9f6e40de8f1c41b1a67fea5214ec47869882ecda9bd0000000000000000000000000000000000000000000000000000000000000001000100040000000000000000000000000000000000008001ab5d064ba75c02635fd6e4de7fd8420eda54c4bda05bd61edabe201f2066d38f00000000000000000000000000000000000000000000000000000000000000010001000500000000000000000000000000000000000080015bcb6d7c735023e0884297db5016a6c704e3490ed0671417639313ecea86795b00000000000000000000000000000000000000000000000000000000000000010001000600000000000000000000000000000000000080015ee51b5b7d47fae5811a9f777174bb08d81d78098c8bd9430a7618756a0ceb8b00000000000000000000000000000000000000000000000000000000000000010001000700000000000000000000000000000000000080011ea63171021b9ab0846efbe0a06f7882d76e24a4900c74c14fa1e0bdf313ed560000000000000000000000000000000000000000000000000000000000000001000100080000000000000000000000000000000000008001574537f1665cd9c894d8d9834d32ed291f49ae1165a0e12a79a4937f2425bf70000000000000000000000000000000000000000000000000000000000000000100010009000000000000000000000000000000000000800190558033c8a3f7c20c81e613e00a9d0e678a7a14923e94e7cb99c8621c7918090000000000000000000000000000000000000000000000000000000000000001000000000000000001000c3104003d1291725c657fe486d0e626f562842175a705a9704c0980b40e3d716b95bbf9e8000100005dd96deb789fbc05264165795bf652190645bfae1ce253ce1db17087a898fb1e240ebf0d53563011198fddab33312923ba20f3c56cf1ba18ca5be9c053000100022bd65a924da61271d1dd5080fc640601185125830805e0ceb42f4185e5118fb454a12a3d9e0c1fbb89230f67044cc191e4f18459261233f659c9e2ba5e000100008b9feb52993729436da78b2863dd56d8d757e19c01a2cdcf1940e45ca9979941fa93f5f699afeab75e8b25cfea22004a8d2ea49f057741c2f2b910996d00010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4afee3cea48e96b9bddb544b4569e60736a1f1fe919e223fcc08f74acf3513be1200010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a8755061217b6a78f5d5f8af6e326e482ebdc57f7144108662d122252ddcc27e7000100045dddc527887dc39b9cd189d6f183f16217393a5d3d3165fead2daeaf4f2d6916280c572561a809555de4a87d7a56d5bcca2c246a389dbb2a24c5639bdb0001000153c0f36532563ba2a10f52b865e558cd1a5eef9a9edd01c1cb23b74aa772beb4f3e3b784609f4e205a09863c0587e63b4b47664022cb34896a1711416b00010003e7842b0b4f4fd8e665883fe9c158ba8d38347840f1da0a75aca1fc284ce2428454b48df9f5551500fc50b63af4741b1cd21d4cfddc69aa46cb78eff45b00010000f183703a165afed04326ad5786316f6fc65b27f1cf17459a52bd1f57f27f896b7429e070ca76e3e33165ec75f6c9f439ee37f3b58822494b1251c8247500010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a05ea3d0bb218598c42b2e25ae5f6cbc9369b273ee6610450cade89775646b2a08902000000000000000000000000000000008b71d4a184058d07fccac4348ae02a1f663403231b0a40fa2c8c0ff73bdca092890200000000000000000000000000000000ab63c4cebbd508a7d7184f0b9134453eea7a09ca749610d5576f8046241b9cde890200000000000000000000000000000000e58af14be53d8ac56f58ff3e5b07c239bfb549149f067597e9d028f35e3c2b77890200000000000000000000000000000000b78e94980fec3a5f68aa25d0d934084907688e537e82c2942af905aab21413ab890200000000000000000000000000000000c4db460819691e825328b532024bbecdc40394c74307a00bd245fc658b1bd34f0901908827f2052a14b24a10cae1f9e259ead06a89a1d74ff736a54f54ebcf05eeb30901d32d07305b87debd25698d4dfac4c2f986693a4e9d9baff7da37a7b5ca8d01cb0901e73042e5dacff2ce20a720c9c6d694576e4afa7bbbafdc4d409c63b7ca8027b70901760a7405795441aceea3be649a53d02785cb3487b7bd23e3b4888a935cee010d09011f3acf5d6d7bfeab8a7112771866e28c3714e0c315a81ec6a58ab4ad1c3d6eb10901c207b49d14deb3af9bc960d57074e27386285c73248abc5fa1d72aa6e8664fa40901644f0c4e15446d7e5ff363c944b55bd6801a1f38afd984c3427569530cb663210901743be0243628b8e7e8f04c00fc4f88efae001250a7482b31e6a0ec87ee3598e7090171e91721f9918576d760f02f03cac47c6f4003316031848e3c1d99e6e83a47434102d84e69f2f480002d5a6962cccee5d4adb48a36bbbf443a531721484381125937f3001ac5ff875b41022f496efbbdb2007b727eb806c926fb20c8ad087c57422977cebd06373e26d19b640e5fe32c85ff39a904faf736ce00a25420c1d9d705358c134cc601d9d184cb4dfdde7e1cac2bc3d4d38bf9ec44e6210ee6b280123bafc586f77764488cd24c6a77546e5a0fe8bdfb4fa203cfaffc36cce4dd5b8901000000000000000000000000651bcde08e7dd06ac5b73b473be6bc5a51030f4c7437657cb7b29bf376c564b8d1675a5e8903000000000000000000000000651bcde24ba84e1f37d041bc6e55ba396826cc494e84d4815b6db52690422eea7386314f00e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c3de2202ccb626ad387d70722e64fbe44562e2f231a290c08532b8d6aba402ff50025fe002039e87b424de2772b82d935f14e2b657429a1bcc04612391ea0330c90ebddefdda48eb2aa7f66ecf7940a280e9ef3fb2e95db0995538440a62af79861004434720529e816fd2e40f8031a8d7471ebcd00351db0787346bcfe8dfad8d2b479093588d0e847efa73a10ce20e4799fb1e46642d65617c7e5213fa04989d92d8903000000000000000000000000651bcde287ded247e1660f827071c7f1371934589751085384fc9f4462c1f1897c5c3eef890100000000000000000000000000000001911dd2ad743ff237d411648a0fe32c6d74eec060716a2a74352f6b1c435b5d670016914ac26bb9cafa0f1dfaeaab10745a9094e1b60c7076fedf21651d6a25b574686a068c708f1bdbefd9e6e454ac2b520fd41c8dcf23ecd4cee978c22f1c1f5f09ff974fe8b575175cefa919a5ba1c0ddf4409be4b16695dc7bd12f6701b99bd2e70a152312ad6f01657413b2eae9287f6b9adad93d5fed1a0dd5e13ec74ce1163146509bfe426f2315a69cb452bf388cccd321eca2746a1adf793b489e5c8f61c40688b7ef3e53defc56c78facf513e511f9f5ba0eb50dbcc745afea3b860da75b394d2d1627b6e2ef54fb7b187d0af61e4532c238f387ecf9f0b466f1d54414100018e519b65c8901b344a480638beadb923fbd3462e475d39acebe559d65ed5cb11a1b25279f1918477c35eec1332ff07001d3f85cf854b70d7552f93ba8e88d581064ca4c0df6ac456c00a0e83898ccd464c63e5008aa1a498cc0646b78eb216d9eeeec76ed0eb0ee6c352f35ca5f0b2edc2ca17d211cc5cb905ba10142f042a6ac836d9cef9a6916635c9a1c1d2dc62a9fe83e2230b506b98e0fded46249008fe28b813907a05ae0d773d8f31e330200e9336e0159034c137ed645fb67ccca8a152312ad6f01657413b2eae9287f6b9adad93d5fee5d8f810abde496ccbeb45a4f3c06af828975163a006257cbf18cefebbfb4cd409025f40404a3d37bba024799ce32d7c2a833aec8474288a26b246afa32b07b4a3ce00577261707065642045746865720000000000000000000000000000000000001a09cf14f266dfe87c4b33e6d934de01f8f7242199fa8783178117218fa033f7ab005745544800000000000000000000000000000000000000000000000000000008289026c5fa173652bd62774824698a6848c63031f853d0e275174552f35df33000577261707065642045746865720000000000000000000000000000000000001a1e59309944cbc900ae848855e10bc929f78e86c2179d6e96cf52bfd520f039200031000000000000000000000000000000000000000000000000000000000000021653a735395136e5494c5426ba972b45e34d36ebcb86ac104c724ab375fcce90a18580ba6aeebc6e6b89d226c79be8927257a436ad11d9c0305b18e9d78cab8f75a3aec2096302b67e3815939e29476fb36a0d8299a1b25279f1918477c35eec1332ff07001d3f85cf85688525f98e4859a9c6939f2d2f92e6b1950ed57e56137d717aca1ccf9754f719a1c7ebe9226d26524400a8959a08f411a727ae7bb68f8febecd89ffe9d84708d24544d452de3e22e62b3b2b872e430839a15115818a152312ad6f01657413b2eae9287f6b9adad93d5fe3fb60af355125687beeb90c066ace76c442b0f963a6afd0e3316fcdd673ad22c09ff30c8a03ec44e5337a1f9d66763cf1b319fdc6d8bc4981e1f47edbd86210614b909ff0cbdceb634b81192417b64d114d535ad3bdba97d6d7e90ee2a79bf1c132d3c2d09ff5cd85060f4ff26eb5b68a6687aee76c1b7a77575fdc86ba49b4faf5041377a79b14de8989f2385a6e23f6bd05a80e0d9231870c15a000142e50adc0d84bff439d0086d9fbab9984f8b27aa208935238a60cc62e7c9bb2ea1709e94c96366b3c40ea4854837c18733e5ac1193b8d8e4070d2eca4441b0378b572bd949ab764fd71c002b759613c3e29d425cf4000100012730c940a81021004e899c6ee4bec02f0667757b9d75a8f0714ce6c157f5940b7664e4f69f01fc530db36965e33599a1348629f07ae2d724007ac36a71a16baac84db583d88e0f3a8c082e3632fcc0e15757f0dcf5234b87af41fdee4c0999c4fe698a8d824415979ab839e6913a975a3055a152312ad6f01657413b2eae9287f6b9adad93d5fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - // ) - // }); - // LogProcessingOutput memory logOutput = executor.processL2Logs( - // nextBatch, - // 0x0000000000000000000000000000000000000000000000000000000000000000 - // ); - // assertEq( - // logOutput.stateDiffHash, - // 0x9a67073c2df8f53087fcfc32d82c98bba591da35df6ce1fb55a23b677d37f9fc, - // "stateDiffHash computation failed" - // ); - - // bytes32 nextCommitment = executor.createBatchCommitment( - // nextBatch, - // logOutput.stateDiffHash, - // new bytes32[](6), - // new bytes32[](6) - // ); - // assertEq( - // nextCommitment, - // 0xa1dcde434352cda8e331e721232ff2d457d4074efae1e3d06ef5b10ffada0c9a, - // "nextCommitment computation failed" - // ); - - // bytes32 prevCommitment = 0x6ebf945305689a8c3ac993df7f002d41d311a762cd6bf39bb054ead8d1f54404; - // uint256 result = executor.getBatchProofPublicInput(prevCommitment, nextCommitment); - // assertEq(result, 0xAC7931F2C11013FC24963E41B86E5325A79F1150350CB41E4F0876A7, "getBatchProofPublicInput"); - // } + // This test is based on a block generated in a local system. + function test_Hashes() public { + utilsFacet.util_setL2DefaultAccountBytecodeHash( + 0x0100065d134a862a777e50059f5e0fbe68b583f3617a67820f7edda0d7f253a0 + ); + utilsFacet.util_setL2BootloaderBytecodeHash(0x010009416e909e0819593a9806bbc841d25c5cdfed3f4a1523497c6814e5194a); + utilsFacet.util_setZkPorterAvailability(false); + + bytes[] memory mockSystemLogs = Utils.createSystemLogsWithEmptyDAValidator(); + + IExecutor.CommitBatchInfo memory nextBatch = IExecutor.CommitBatchInfo({ + // ignored + batchNumber: 1, + // ignored + timestamp: 100, + indexRepeatedStorageChanges: 84, + newStateRoot: 0x9cf7bb72401a56039ca097cabed20a72221c944ed9b0e515c083c04663ab45a6, + // ignored + numberOfLayer1Txs: 10, + // ignored + priorityOperationsHash: 0x167f4ca80269c9520ad951eeeda28dd3deb0715e9e2917461e81a60120a14183, + bootloaderHeapInitialContentsHash: 0x540442e48142fa061a81822184f7790e7b69dea92153d38ef623802c6f0411c0, + eventsQueueStateHash: 0xda42ab7994d4695a25f4ea8a9a485a592b7a31c20d5dae6363828de86d8826ea, + systemLogs: Utils.encodePacked(mockSystemLogs), + operatorDAInput: abi.encodePacked( + hex"000000000a000100000000000000000000000000000000000000008001760f6100ddbd86c4d5a58532923e7424d33ffb44145a26171d9b2595a349450b0000000000000000000000000000000000000000000000000000000000000001000100010000000000000000000000000000000000008001a789fe4e2a955eee45d44f408f86203c8f643910bf4888d1fd1465cdbc6376d800000000000000000000000000000000000000000000000000000000000000010001000200000000000000000000000000000000000080016ba43e7c7df11e5a655f22c9bce1b37434afd2bf8fcdb10100a460e6a2c0cc83000000000000000000000000000000000000000000000000000000000000000100010003000000000000000000000000000000000000800156e569838658c17c756aa9f6e40de8f1c41b1a67fea5214ec47869882ecda9bd0000000000000000000000000000000000000000000000000000000000000001000100040000000000000000000000000000000000008001ab5d064ba75c02635fd6e4de7fd8420eda54c4bda05bd61edabe201f2066d38f00000000000000000000000000000000000000000000000000000000000000010001000500000000000000000000000000000000000080015bcb6d7c735023e0884297db5016a6c704e3490ed0671417639313ecea86795b00000000000000000000000000000000000000000000000000000000000000010001000600000000000000000000000000000000000080015ee51b5b7d47fae5811a9f777174bb08d81d78098c8bd9430a7618756a0ceb8b00000000000000000000000000000000000000000000000000000000000000010001000700000000000000000000000000000000000080011ea63171021b9ab0846efbe0a06f7882d76e24a4900c74c14fa1e0bdf313ed560000000000000000000000000000000000000000000000000000000000000001000100080000000000000000000000000000000000008001574537f1665cd9c894d8d9834d32ed291f49ae1165a0e12a79a4937f2425bf70000000000000000000000000000000000000000000000000000000000000000100010009000000000000000000000000000000000000800190558033c8a3f7c20c81e613e00a9d0e678a7a14923e94e7cb99c8621c7918090000000000000000000000000000000000000000000000000000000000000001000000000000000001000c3104003d1291725c657fe486d0e626f562842175a705a9704c0980b40e3d716b95bbf9e8000100005dd96deb789fbc05264165795bf652190645bfae1ce253ce1db17087a898fb1e240ebf0d53563011198fddab33312923ba20f3c56cf1ba18ca5be9c053000100022bd65a924da61271d1dd5080fc640601185125830805e0ceb42f4185e5118fb454a12a3d9e0c1fbb89230f67044cc191e4f18459261233f659c9e2ba5e000100008b9feb52993729436da78b2863dd56d8d757e19c01a2cdcf1940e45ca9979941fa93f5f699afeab75e8b25cfea22004a8d2ea49f057741c2f2b910996d00010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4afee3cea48e96b9bddb544b4569e60736a1f1fe919e223fcc08f74acf3513be1200010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a8755061217b6a78f5d5f8af6e326e482ebdc57f7144108662d122252ddcc27e7000100045dddc527887dc39b9cd189d6f183f16217393a5d3d3165fead2daeaf4f2d6916280c572561a809555de4a87d7a56d5bcca2c246a389dbb2a24c5639bdb0001000153c0f36532563ba2a10f52b865e558cd1a5eef9a9edd01c1cb23b74aa772beb4f3e3b784609f4e205a09863c0587e63b4b47664022cb34896a1711416b00010003e7842b0b4f4fd8e665883fe9c158ba8d38347840f1da0a75aca1fc284ce2428454b48df9f5551500fc50b63af4741b1cd21d4cfddc69aa46cb78eff45b00010000f183703a165afed04326ad5786316f6fc65b27f1cf17459a52bd1f57f27f896b7429e070ca76e3e33165ec75f6c9f439ee37f3b58822494b1251c8247500010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a05ea3d0bb218598c42b2e25ae5f6cbc9369b273ee6610450cade89775646b2a08902000000000000000000000000000000008b71d4a184058d07fccac4348ae02a1f663403231b0a40fa2c8c0ff73bdca092890200000000000000000000000000000000ab63c4cebbd508a7d7184f0b9134453eea7a09ca749610d5576f8046241b9cde890200000000000000000000000000000000e58af14be53d8ac56f58ff3e5b07c239bfb549149f067597e9d028f35e3c2b77890200000000000000000000000000000000b78e94980fec3a5f68aa25d0d934084907688e537e82c2942af905aab21413ab890200000000000000000000000000000000c4db460819691e825328b532024bbecdc40394c74307a00bd245fc658b1bd34f0901908827f2052a14b24a10cae1f9e259ead06a89a1d74ff736a54f54ebcf05eeb30901d32d07305b87debd25698d4dfac4c2f986693a4e9d9baff7da37a7b5ca8d01cb0901e73042e5dacff2ce20a720c9c6d694576e4afa7bbbafdc4d409c63b7ca8027b70901760a7405795441aceea3be649a53d02785cb3487b7bd23e3b4888a935cee010d09011f3acf5d6d7bfeab8a7112771866e28c3714e0c315a81ec6a58ab4ad1c3d6eb10901c207b49d14deb3af9bc960d57074e27386285c73248abc5fa1d72aa6e8664fa40901644f0c4e15446d7e5ff363c944b55bd6801a1f38afd984c3427569530cb663210901743be0243628b8e7e8f04c00fc4f88efae001250a7482b31e6a0ec87ee3598e7090171e91721f9918576d760f02f03cac47c6f4003316031848e3c1d99e6e83a47434102d84e69f2f480002d5a6962cccee5d4adb48a36bbbf443a531721484381125937f3001ac5ff875b41022f496efbbdb2007b727eb806c926fb20c8ad087c57422977cebd06373e26d19b640e5fe32c85ff39a904faf736ce00a25420c1d9d705358c134cc601d9d184cb4dfdde7e1cac2bc3d4d38bf9ec44e6210ee6b280123bafc586f77764488cd24c6a77546e5a0fe8bdfb4fa203cfaffc36cce4dd5b8901000000000000000000000000651bcde08e7dd06ac5b73b473be6bc5a51030f4c7437657cb7b29bf376c564b8d1675a5e8903000000000000000000000000651bcde24ba84e1f37d041bc6e55ba396826cc494e84d4815b6db52690422eea7386314f00e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c3de2202ccb626ad387d70722e64fbe44562e2f231a290c08532b8d6aba402ff50025fe002039e87b424de2772b82d935f14e2b657429a1bcc04612391ea0330c90ebddefdda48eb2aa7f66ecf7940a280e9ef3fb2e95db0995538440a62af79861004434720529e816fd2e40f8031a8d7471ebcd00351db0787346bcfe8dfad8d2b479093588d0e847efa73a10ce20e4799fb1e46642d65617c7e5213fa04989d92d8903000000000000000000000000651bcde287ded247e1660f827071c7f1371934589751085384fc9f4462c1f1897c5c3eef890100000000000000000000000000000001911dd2ad743ff237d411648a0fe32c6d74eec060716a2a74352f6b1c435b5d670016914ac26bb9cafa0f1dfaeaab10745a9094e1b60c7076fedf21651d6a25b574686a068c708f1bdbefd9e6e454ac2b520fd41c8dcf23ecd4cee978c22f1c1f5f09ff974fe8b575175cefa919a5ba1c0ddf4409be4b16695dc7bd12f6701b99bd2e70a152312ad6f01657413b2eae9287f6b9adad93d5fed1a0dd5e13ec74ce1163146509bfe426f2315a69cb452bf388cccd321eca2746a1adf793b489e5c8f61c40688b7ef3e53defc56c78facf513e511f9f5ba0eb50dbcc745afea3b860da75b394d2d1627b6e2ef54fb7b187d0af61e4532c238f387ecf9f0b466f1d54414100018e519b65c8901b344a480638beadb923fbd3462e475d39acebe559d65ed5cb11a1b25279f1918477c35eec1332ff07001d3f85cf854b70d7552f93ba8e88d581064ca4c0df6ac456c00a0e83898ccd464c63e5008aa1a498cc0646b78eb216d9eeeec76ed0eb0ee6c352f35ca5f0b2edc2ca17d211cc5cb905ba10142f042a6ac836d9cef9a6916635c9a1c1d2dc62a9fe83e2230b506b98e0fded46249008fe28b813907a05ae0d773d8f31e330200e9336e0159034c137ed645fb67ccca8a152312ad6f01657413b2eae9287f6b9adad93d5fee5d8f810abde496ccbeb45a4f3c06af828975163a006257cbf18cefebbfb4cd409025f40404a3d37bba024799ce32d7c2a833aec8474288a26b246afa32b07b4a3ce00577261707065642045746865720000000000000000000000000000000000001a09cf14f266dfe87c4b33e6d934de01f8f7242199fa8783178117218fa033f7ab005745544800000000000000000000000000000000000000000000000000000008289026c5fa173652bd62774824698a6848c63031f853d0e275174552f35df33000577261707065642045746865720000000000000000000000000000000000001a1e59309944cbc900ae848855e10bc929f78e86c2179d6e96cf52bfd520f039200031000000000000000000000000000000000000000000000000000000000000021653a735395136e5494c5426ba972b45e34d36ebcb86ac104c724ab375fcce90a18580ba6aeebc6e6b89d226c79be8927257a436ad11d9c0305b18e9d78cab8f75a3aec2096302b67e3815939e29476fb36a0d8299a1b25279f1918477c35eec1332ff07001d3f85cf85688525f98e4859a9c6939f2d2f92e6b1950ed57e56137d717aca1ccf9754f719a1c7ebe9226d26524400a8959a08f411a727ae7bb68f8febecd89ffe9d84708d24544d452de3e22e62b3b2b872e430839a15115818a152312ad6f01657413b2eae9287f6b9adad93d5fe3fb60af355125687beeb90c066ace76c442b0f963a6afd0e3316fcdd673ad22c09ff30c8a03ec44e5337a1f9d66763cf1b319fdc6d8bc4981e1f47edbd86210614b909ff0cbdceb634b81192417b64d114d535ad3bdba97d6d7e90ee2a79bf1c132d3c2d09ff5cd85060f4ff26eb5b68a6687aee76c1b7a77575fdc86ba49b4faf5041377a79b14de8989f2385a6e23f6bd05a80e0d9231870c15a000142e50adc0d84bff439d0086d9fbab9984f8b27aa208935238a60cc62e7c9bb2ea1709e94c96366b3c40ea4854837c18733e5ac1193b8d8e4070d2eca4441b0378b572bd949ab764fd71c002b759613c3e29d425cf4000100012730c940a81021004e899c6ee4bec02f0667757b9d75a8f0714ce6c157f5940b7664e4f69f01fc530db36965e33599a1348629f07ae2d724007ac36a71a16baac84db583d88e0f3a8c082e3632fcc0e15757f0dcf5234b87af41fdee4c0999c4fe698a8d824415979ab839e6913a975a3055a152312ad6f01657413b2eae9287f6b9adad93d5fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) + }); + LogProcessingOutput memory logOutput = executor.processL2Logs( + nextBatch, + 0x0000000000000000000000000000000000000000000000000000000000000000 + ); + + bytes32 nextCommitment = executor.createBatchCommitment( + nextBatch, + logOutput.stateDiffHash, + new bytes32[](16), + new bytes32[](16) + ); + assertEq( + nextCommitment, + 0x81e46ea22cdb4a0a6cb30b6c02170394703e9bdd101275d542a7c6c23c789898, + "nextCommitment computation failed" + ); + + bytes32 prevCommitment = 0x6ebf945305689a8c3ac993df7f002d41d311a762cd6bf39bb054ead8d1f54404; + uint256 result = executor.getBatchProofPublicInput(prevCommitment, nextCommitment); + assertEq(result, 0x7C854720CBA105B9E34DA6A28770B93AD384C1BF98C497CCBFA4DADB, "getBatchProofPublicInput"); + } // add this to be excluded from coverage report function test() internal {} diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol index b4d46e842..f44a93e72 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol @@ -150,6 +150,8 @@ contract ExecutorTest is Test { dummyBridgehub.setMessageRoot(address(messageRoot)); sharedBridge = new DummyEraBaseTokenBridge(); + dummyBridgehub.setSharedBridge(address(sharedBridge)); + vm.mockCall( address(messageRoot), abi.encodeWithSelector(MessageRoot.addChainBatchRoot.selector, 9, 1, bytes32(0)), @@ -197,7 +199,6 @@ contract ExecutorTest is Test { admin: owner, validatorTimelock: validator, baseTokenAssetId: DataEncoding.encodeNTVAssetId(block.chainid, ETH_TOKEN_ADDRESS), - baseTokenBridge: address(sharedBridge), storedBatchZero: keccak256(abi.encode(genesisStoredBatchInfo)), verifier: IVerifier(testnetVerifier), // verifier verifierParams: VerifierParams({ diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index f97ecc08a..bcfe6ae2c 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -1,13 +1,15 @@ pragma solidity 0.8.24; import "@openzeppelin/contracts-v4/utils/Strings.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; -import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; +import {PermanentRestriction, MIN_GAS_FOR_FALLABLE_CALL} from "contracts/governance/PermanentRestriction.sol"; import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; -import {ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; +import {NotAllowed, NotEnoughGas, InvalidAddress, UnsupportedEncodingVersion, InvalidSelector, NotBridgehub, ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; import {Call} from "contracts/governance/Common.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {VerifierParams, FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; @@ -22,12 +24,16 @@ import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; contract PermanentRestrictionTest is ChainTypeManagerTest { ChainAdmin internal chainAdmin; AccessControlRestriction internal restriction; PermanentRestriction internal permRestriction; + address constant L2_FACTORY_ADDR = address(0); + address internal owner; address internal hyperchain; @@ -36,20 +42,38 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { createNewChainBridgehub(); - vm.stopPrank(); - owner = makeAddr("owner"); hyperchain = chainContractAddress.getHyperchain(chainId); - permRestriction = new PermanentRestriction(owner, bridgehub); + (permRestriction, ) = _deployPermRestriction(bridgehub, L2_FACTORY_ADDR, owner); restriction = new AccessControlRestriction(0, owner); address[] memory restrictions = new address[](1); restrictions[0] = address(restriction); chainAdmin = new ChainAdmin(restrictions); } + function _deployPermRestriction( + IBridgehub _bridgehub, + address _l2AdminFactory, + address _owner + ) internal returns (PermanentRestriction proxy, PermanentRestriction impl) { + impl = new PermanentRestriction(_bridgehub, _l2AdminFactory); + TransparentUpgradeableProxy tup = new TransparentUpgradeableProxy( + address(impl), + address(uint160(1)), + abi.encodeCall(PermanentRestriction.initialize, (_owner)) + ); + + proxy = PermanentRestriction(address(tup)); + } + function test_ownerAsAddressZero() public { + PermanentRestriction impl = new PermanentRestriction(bridgehub, L2_FACTORY_ADDR); vm.expectRevert(ZeroAddress.selector); - permRestriction = new PermanentRestriction(address(0), bridgehub); + new TransparentUpgradeableProxy( + address(impl), + address(uint160(1)), + abi.encodeCall(PermanentRestriction.initialize, (address(0))) + ); } function test_allowAdminImplementation(bytes32 implementationHash) public { @@ -197,6 +221,136 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { vm.stopPrank(); } + function _encodeMigraationCall( + bool correctTarget, + bool correctSelector, + bool correctSecondBridge, + bool correctEncodingVersion, + bool correctAssetId, + address l2Admin + ) internal returns (Call memory call) { + if (!correctTarget) { + call.target = address(0); + return call; + } + call.target = address(bridgehub); + + if (!correctSelector) { + call.data = hex"00000000"; + return call; + } + + L2TransactionRequestTwoBridgesOuter memory outer = L2TransactionRequestTwoBridgesOuter({ + chainId: chainId, + mintValue: 0, + l2Value: 0, + l2GasLimit: 0, + l2GasPerPubdataByteLimit: 0, + refundRecipient: address(0), + secondBridgeAddress: address(0), + secondBridgeValue: 0, + secondBridgeCalldata: hex"" + }); + if (!correctSecondBridge) { + call.data = abi.encodeCall(Bridgehub.requestL2TransactionTwoBridges, (outer)); + // 0 is not correct second bridge + return call; + } + outer.secondBridgeAddress = sharedBridge; + + uint8 encoding = correctEncodingVersion ? 1 : 12; + + bytes32 chainAssetId = correctAssetId ? bridgehub.ctmAssetIdFromChainId(chainId) : bytes32(0); + + bytes memory bridgehubData = abi.encode( + BridgehubBurnCTMAssetData({ + // Gateway chain id, we do not need it + chainId: 0, + ctmData: abi.encode(l2Admin, hex""), + chainData: abi.encode(IZKChain(IBridgehub(bridgehub).getZKChain(chainId)).getProtocolVersion()) + }) + ); + outer.secondBridgeCalldata = abi.encodePacked(bytes1(encoding), abi.encode(chainAssetId, bridgehubData)); + + call.data = abi.encodeCall(Bridgehub.requestL2TransactionTwoBridges, (outer)); + } + + function test_tryGetNewAdminFromMigrationRevertWhenInvalidSelector() public { + Call memory call = _encodeMigraationCall(false, true, true, true, true, address(0)); + + vm.expectRevert(abi.encodeWithSelector(NotBridgehub.selector, address(0))); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationRevertWhenNotBridgehub() public { + Call memory call = _encodeMigraationCall(true, false, true, true, true, address(0)); + + vm.expectRevert(abi.encodeWithSelector(InvalidSelector.selector, bytes4(0))); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationRevertWhenNotSharedBridge() public { + Call memory call = _encodeMigraationCall(true, true, false, true, true, address(0)); + + vm.expectRevert(abi.encodeWithSelector(InvalidAddress.selector, address(sharedBridge), address(0))); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationRevertWhenIncorrectEncoding() public { + Call memory call = _encodeMigraationCall(true, true, true, false, true, address(0)); + + vm.expectRevert(abi.encodeWithSelector(UnsupportedEncodingVersion.selector)); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationRevertWhenIncorrectAssetId() public { + Call memory call = _encodeMigraationCall(true, true, true, true, false, address(0)); + + vm.expectRevert(abi.encodeWithSelector(ZeroAddress.selector)); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationShouldWorkCorrectly() public { + address l2Addr = makeAddr("l2Addr"); + Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); + + address result = permRestriction.tryGetNewAdminFromMigration(call); + assertEq(result, l2Addr); + } + + function test_validateMigrationToL2RevertNotAllowed() public { + Call memory call = _encodeMigraationCall(true, true, true, true, true, address(0)); + + vm.expectRevert(abi.encodeWithSelector(NotAllowed.selector, address(0))); + permRestriction.validateCall(call, owner); + } + + function test_validateMigrationToL2() public { + address expectedAddress = L2ContractHelper.computeCreate2Address( + L2_FACTORY_ADDR, + bytes32(0), + bytes32(0), + bytes32(0) + ); + + vm.expectEmit(true, false, false, true); + emit IPermanentRestriction.AllowL2Admin(expectedAddress); + permRestriction.allowL2Admin(bytes32(0), bytes32(0), bytes32(0)); + + Call memory call = _encodeMigraationCall(true, true, true, true, true, expectedAddress); + + // Should not fail + permRestriction.validateCall(call, owner); + } + + function test_validateNotEnoughGas() public { + address l2Addr = makeAddr("l2Addr"); + Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); + + vm.expectRevert(abi.encodeWithSelector(NotEnoughGas.selector)); + permRestriction.validateCall{gas: MIN_GAS_FOR_FALLABLE_CALL}(call, address(0)); + } + function createNewChainBridgehub() internal { bytes[] memory factoryDeps = new bytes[](0); vm.stopPrank(); @@ -204,18 +358,23 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { bridgehub.addChainTypeManager(address(chainContractAddress)); bridgehub.addTokenAssetId(DataEncoding.encodeNTVAssetId(block.chainid, baseToken)); bridgehub.setAddresses(sharedBridge, ICTMDeploymentTracker(address(0)), new MessageRoot(bridgehub)); + vm.stopPrank(); + + // ctm deployer address is 0 in this test + vm.startPrank(address(0)); + bridgehub.setAssetHandlerAddress( + bytes32(uint256(uint160(address(chainContractAddress)))), + address(chainContractAddress) + ); + vm.stopPrank(); + address l1Nullifier = makeAddr("l1Nullifier"); - address l2LegacySharedBridge = makeAddr("l2LegacySharedBridge"); vm.mockCall( address(sharedBridge), abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), abi.encode(l1Nullifier) ); - vm.mockCall( - address(l1Nullifier), - abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), - abi.encode(l2LegacySharedBridge) - ); + vm.startPrank(governor); bridgehub.createNewChain({ _chainId: chainId, _chainTypeManager: address(chainContractAddress), @@ -225,5 +384,6 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { _initData: getCTMInitData(), _factoryDeps: factoryDeps }); + vm.stopPrank(); } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol b/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol index 8ab52c976..3347e19ef 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol @@ -108,6 +108,18 @@ library Utils { return logs; } + function createSystemLogsWithEmptyDAValidator() public returns (bytes[] memory) { + bytes[] memory systemLogs = createSystemLogs(bytes32(0)); + systemLogs[uint256(SystemLogKey.USED_L2_DA_VALIDATOR_ADDRESS_KEY)] = constructL2Log( + true, + L2_TO_L1_MESSENGER, + uint256(SystemLogKey.USED_L2_DA_VALIDATOR_ADDRESS_KEY), + bytes32(uint256(0)) + ); + + return systemLogs; + } + function createSystemLogsWithUpgradeTransaction( bytes32 _expectedSystemContractUpgradeTxHash ) public returns (bytes[] memory) { @@ -125,6 +137,30 @@ library Utils { return logs; } + function createSystemLogsWithUpgradeTransactionForCTM( + bytes32 _expectedSystemContractUpgradeTxHash, + bytes32 _outputHash + ) public returns (bytes[] memory) { + bytes[] memory logsWithoutUpgradeTx = createSystemLogs(_outputHash); + bytes[] memory logs = new bytes[](logsWithoutUpgradeTx.length + 1); + for (uint256 i = 0; i < logsWithoutUpgradeTx.length; i++) { + logs[i] = logsWithoutUpgradeTx[i]; + } + logs[uint256(SystemLogKey.PREV_BATCH_HASH_KEY)] = constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + uint256(SystemLogKey.PREV_BATCH_HASH_KEY), + bytes32(uint256(0x01)) + ); + logs[logsWithoutUpgradeTx.length] = constructL2Log( + true, + L2_BOOTLOADER_ADDRESS, + uint256(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY), + _expectedSystemContractUpgradeTxHash + ); + return logs; + } + function createStoredBatchInfo() public pure returns (IExecutor.StoredBatchInfo memory) { return IExecutor.StoredBatchInfo({ @@ -198,7 +234,7 @@ library Utils { } function getAdminSelectors() public pure returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](12); + bytes4[] memory selectors = new bytes4[](13); selectors[0] = AdminFacet.setPendingAdmin.selector; selectors[1] = AdminFacet.acceptAdmin.selector; selectors[2] = AdminFacet.setValidator.selector; @@ -211,6 +247,7 @@ library Utils { selectors[9] = AdminFacet.freezeDiamond.selector; selectors[10] = AdminFacet.unfreezeDiamond.selector; selectors[11] = AdminFacet.genesisUpgrade.selector; + selectors[12] = AdminFacet.setDAValidatorPair.selector; return selectors; } @@ -272,48 +309,47 @@ library Utils { } function getUtilsFacetSelectors() public pure returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](41); + bytes4[] memory selectors = new bytes4[](39); selectors[0] = UtilsFacet.util_setChainId.selector; selectors[1] = UtilsFacet.util_getChainId.selector; selectors[2] = UtilsFacet.util_setBridgehub.selector; selectors[3] = UtilsFacet.util_getBridgehub.selector; selectors[4] = UtilsFacet.util_setBaseToken.selector; selectors[5] = UtilsFacet.util_getBaseTokenAssetId.selector; - selectors[6] = UtilsFacet.util_setBaseTokenBridge.selector; - selectors[7] = UtilsFacet.util_getBaseTokenBridge.selector; - selectors[8] = UtilsFacet.util_setVerifier.selector; - selectors[9] = UtilsFacet.util_getVerifier.selector; - selectors[10] = UtilsFacet.util_setStoredBatchHashes.selector; - selectors[11] = UtilsFacet.util_getStoredBatchHashes.selector; - selectors[12] = UtilsFacet.util_setVerifierParams.selector; - selectors[13] = UtilsFacet.util_getVerifierParams.selector; - selectors[14] = UtilsFacet.util_setL2BootloaderBytecodeHash.selector; - selectors[15] = UtilsFacet.util_getL2BootloaderBytecodeHash.selector; - selectors[16] = UtilsFacet.util_setL2DefaultAccountBytecodeHash.selector; - selectors[17] = UtilsFacet.util_getL2DefaultAccountBytecodeHash.selector; - selectors[18] = UtilsFacet.util_setPendingAdmin.selector; - selectors[19] = UtilsFacet.util_getPendingAdmin.selector; - selectors[20] = UtilsFacet.util_setAdmin.selector; - selectors[21] = UtilsFacet.util_getAdmin.selector; - selectors[22] = UtilsFacet.util_setValidator.selector; - selectors[23] = UtilsFacet.util_getValidator.selector; - selectors[24] = UtilsFacet.util_setZkPorterAvailability.selector; - selectors[25] = UtilsFacet.util_getZkPorterAvailability.selector; - selectors[26] = UtilsFacet.util_setChainTypeManager.selector; - selectors[27] = UtilsFacet.util_getChainTypeManager.selector; - selectors[28] = UtilsFacet.util_setPriorityTxMaxGasLimit.selector; - selectors[29] = UtilsFacet.util_getPriorityTxMaxGasLimit.selector; - selectors[30] = UtilsFacet.util_setFeeParams.selector; - selectors[31] = UtilsFacet.util_getFeeParams.selector; - selectors[32] = UtilsFacet.util_setProtocolVersion.selector; - selectors[33] = UtilsFacet.util_getProtocolVersion.selector; - selectors[34] = UtilsFacet.util_setIsFrozen.selector; - selectors[35] = UtilsFacet.util_getIsFrozen.selector; - selectors[36] = UtilsFacet.util_setTransactionFilterer.selector; - selectors[37] = UtilsFacet.util_setBaseTokenGasPriceMultiplierDenominator.selector; - selectors[38] = UtilsFacet.util_setTotalBatchesExecuted.selector; - selectors[39] = UtilsFacet.util_setL2LogsRootHash.selector; - selectors[40] = UtilsFacet.util_setBaseTokenGasPriceMultiplierNominator.selector; + selectors[6] = UtilsFacet.util_setVerifier.selector; + selectors[7] = UtilsFacet.util_getVerifier.selector; + selectors[8] = UtilsFacet.util_setStoredBatchHashes.selector; + selectors[9] = UtilsFacet.util_getStoredBatchHashes.selector; + selectors[10] = UtilsFacet.util_setVerifierParams.selector; + selectors[11] = UtilsFacet.util_getVerifierParams.selector; + selectors[12] = UtilsFacet.util_setL2BootloaderBytecodeHash.selector; + selectors[13] = UtilsFacet.util_getL2BootloaderBytecodeHash.selector; + selectors[14] = UtilsFacet.util_setL2DefaultAccountBytecodeHash.selector; + selectors[15] = UtilsFacet.util_getL2DefaultAccountBytecodeHash.selector; + selectors[16] = UtilsFacet.util_setPendingAdmin.selector; + selectors[17] = UtilsFacet.util_getPendingAdmin.selector; + selectors[18] = UtilsFacet.util_setAdmin.selector; + selectors[19] = UtilsFacet.util_getAdmin.selector; + selectors[20] = UtilsFacet.util_setValidator.selector; + selectors[21] = UtilsFacet.util_getValidator.selector; + selectors[22] = UtilsFacet.util_setZkPorterAvailability.selector; + selectors[23] = UtilsFacet.util_getZkPorterAvailability.selector; + selectors[24] = UtilsFacet.util_setChainTypeManager.selector; + selectors[25] = UtilsFacet.util_getChainTypeManager.selector; + selectors[26] = UtilsFacet.util_setPriorityTxMaxGasLimit.selector; + selectors[27] = UtilsFacet.util_getPriorityTxMaxGasLimit.selector; + selectors[28] = UtilsFacet.util_setFeeParams.selector; + selectors[29] = UtilsFacet.util_getFeeParams.selector; + selectors[30] = UtilsFacet.util_setProtocolVersion.selector; + selectors[31] = UtilsFacet.util_getProtocolVersion.selector; + selectors[32] = UtilsFacet.util_setIsFrozen.selector; + selectors[33] = UtilsFacet.util_getIsFrozen.selector; + selectors[34] = UtilsFacet.util_setTransactionFilterer.selector; + selectors[35] = UtilsFacet.util_setBaseTokenGasPriceMultiplierDenominator.selector; + selectors[36] = UtilsFacet.util_setTotalBatchesExecuted.selector; + selectors[37] = UtilsFacet.util_setL2LogsRootHash.selector; + selectors[38] = UtilsFacet.util_setBaseTokenGasPriceMultiplierNominator.selector; + return selectors; } @@ -350,7 +386,6 @@ library Utils { admin: address(0x32149872498357874258787), validatorTimelock: address(0x85430237648403822345345), baseTokenAssetId: bytes32(uint256(0x923645439232223445)), - baseTokenBridge: address(0x23746765237749923040872834), storedBatchZero: bytes32(0), verifier: makeVerifier(testnetVerifier), verifierParams: makeVerifierParams(), diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol b/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol index 0d141ce1f..08afae8c9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol @@ -32,14 +32,6 @@ contract UtilsFacet is ZKChainBase { return s.baseTokenAssetId; } - function util_setBaseTokenBridge(address _baseTokenBridge) external { - s.baseTokenBridge = _baseTokenBridge; - } - - function util_getBaseTokenBridge() external view returns (address) { - return s.baseTokenBridge; - } - function util_setVerifier(IVerifier _verifier) external { s.verifier = _verifier; } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol index 81659b682..c422dca99 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol @@ -5,6 +5,7 @@ import {ChainTypeManagerTest} from "./_ChainTypeManager_Shared.t.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {Unauthorized, HashMismatch} from "contracts/common/L1ContractErrors.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; contract createNewChainTest is ChainTypeManagerTest { function setUp() public { @@ -32,10 +33,18 @@ contract createNewChainTest is ChainTypeManagerTest { chainContractAddress.createNewChain({ _chainId: chainId, _baseTokenAssetId: DataEncoding.encodeNTVAssetId(block.chainid, baseToken), - _assetRouter: sharedBridge, _admin: admin, _initData: abi.encode(abi.encode(initialDiamondCutData), bytes("")), _factoryDeps: new bytes[](0) }); } + + function test_SuccessfulCreationOfNewChain() public { + address newChainAddress = createNewChain(getDiamondCutData(diamondInit)); + + address admin = IZKChain(newChainAddress).getAdmin(); + + assertEq(newChainAdmin, admin); + assertNotEq(newChainAddress, address(0)); + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol index d92349a61..73a2dc498 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol @@ -5,22 +5,31 @@ import {ChainTypeManagerTest} from "./_ChainTypeManager_Shared.t.sol"; import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; import {FacetIsFrozen} from "contracts/common/L1ContractErrors.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; contract freezeChainTest is ChainTypeManagerTest { - // function test_FreezingChain() public { - // createNewChain(getDiamondCutData(diamondInit)); - // address newChainAddress = chainContractAddress.getZKChain(chainId); - // GettersFacet gettersFacet = GettersFacet(newChainAddress); - // bool isChainFrozen = gettersFacet.isDiamondStorageFrozen(); - // assertEq(isChainFrozen, false); - // vm.stopPrank(); - // vm.startPrank(governor); - // chainContractAddress.freezeChain(block.chainid); - // // Repeated call should revert - // vm.expectRevert(bytes("q1")); // storage frozen - // chainContractAddress.freezeChain(block.chainid); - // // Call fails as storage is frozen - // vm.expectRevert(bytes("q1")); - // isChainFrozen = gettersFacet.isDiamondStorageFrozen(); - // } + function setUp() public { + deploy(); + } + + function test_FreezingChain() public { + address newChainAddress = createNewChain(getDiamondCutData(diamondInit)); + vm.mockCall( + address(bridgehub), + abi.encodeWithSelector(IBridgehub.getZKChain.selector), + abi.encode(newChainAddress) + ); + GettersFacet gettersFacet = GettersFacet(newChainAddress); + bool isChainFrozen = gettersFacet.isDiamondStorageFrozen(); + assertEq(isChainFrozen, false); + vm.stopPrank(); + vm.startPrank(governor); + chainContractAddress.freezeChain(block.chainid); + // Repeated call should revert + vm.expectRevert(bytes("q1")); // storage frozen + chainContractAddress.freezeChain(block.chainid); + // Call fails as storage is frozen + vm.expectRevert(bytes("q1")); + isChainFrozen = gettersFacet.isDiamondStorageFrozen(); + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol index cdac3e776..610c6c1a3 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol @@ -3,15 +3,24 @@ pragma solidity 0.8.24; import {Vm} from "forge-std/Test.sol"; -import {Utils, L2_SYSTEM_CONTEXT_ADDRESS} from "../../Utils/Utils.sol"; +import {SafeCast} from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol"; + +import {Utils, L2_SYSTEM_CONTEXT_ADDRESS, L2_DA_VALIDATOR_ADDRESS} from "../../Utils/Utils.sol"; import {ChainTypeManagerTest} from "./_ChainTypeManager_Shared.t.sol"; -import {COMMIT_TIMESTAMP_NOT_OLDER, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK} from "contracts/common/Config.sol"; -import {IExecutor, SystemLogKey} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; +import {COMMIT_TIMESTAMP_NOT_OLDER, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK, POINT_EVALUATION_PRECOMPILE_ADDR, REQUIRED_L2_GAS_PRICE_PER_PUBDATA, SYSTEM_UPGRADE_L2_TX_TYPE, PRIORITY_TX_MAX_GAS_LIMIT} from "contracts/common/Config.sol"; +import {L2_FORCE_DEPLOYER_ADDR, L2_COMPLEX_UPGRADER_ADDR, L2_GENESIS_UPGRADE_ADDR} from "contracts/common/L2ContractAddresses.sol"; //, COMPLEX_UPGRADER_ADDR, GENESIS_UPGRADE_ADDR +import {SemVer} from "contracts/common/libraries/SemVer.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; +import {IExecutor, SystemLogKey, TOTAL_BLOBS_IN_COMMITMENT} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; import {IExecutor} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; +import {IL2GenesisUpgrade} from "contracts/state-transition/l2-deps/IL2GenesisUpgrade.sol"; +import {IComplexUpgrader} from "contracts/state-transition/l2-deps/IComplexUpgrader.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; contract revertBatchesTest is ChainTypeManagerTest { // Items for logs & commits @@ -20,129 +29,221 @@ contract revertBatchesTest is ChainTypeManagerTest { IExecutor.StoredBatchInfo internal newStoredBatchInfo; IExecutor.StoredBatchInfo internal genesisStoredBatchInfo; uint256[] internal proofInput; + bytes32 l2DAValidatorOutputHash; + bytes operatorDAInput; + bytes defaultBlobCommitment; + bytes32[] defaultBlobVersionedHashes; + bytes16 defaultBlobOpeningPoint = 0x7142c5851421a2dc03dde0aabdb0ffdb; + bytes32 defaultBlobClaimedValue = 0x1e5eea3bbb85517461c1d1c7b84c7c2cec050662a5e81a71d5d7e2766eaff2f0; + bytes l2Logs; + address newChainAddress; + + bytes32 constant EMPTY_PREPUBLISHED_COMMITMENT = 0x0000000000000000000000000000000000000000000000000000000000000000; + bytes constant POINT_EVALUATION_PRECOMPILE_RESULT = + hex"000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"; // Facets exposing the diamond AdminFacet internal adminFacet; ExecutorFacet internal executorFacet; GettersFacet internal gettersFacet; - // function test_SuccessfulBatchReverting() public { - // createNewChain(getDiamondCutData(diamondInit)); - - // address newChainAddress = chainContractAddress.getZKChain(chainId); - - // executorFacet = ExecutorFacet(address(newChainAddress)); - // gettersFacet = GettersFacet(address(newChainAddress)); - // adminFacet = AdminFacet(address(newChainAddress)); - - // // Initial setup for logs & commits - // vm.stopPrank(); - // vm.startPrank(newChainAdmin); - - // genesisStoredBatchInfo = IExecutor.StoredBatchInfo({ - // batchNumber: 0, - // batchHash: bytes32(uint256(0x01)), - // indexRepeatedStorageChanges: 1, - // numberOfLayer1Txs: 0, - // priorityOperationsHash: EMPTY_STRING_KECCAK, - // l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, - // timestamp: 0, - // commitment: bytes32(uint256(0x01)) - // }); - - // adminFacet.setTokenMultiplier(1, 1); - - // uint256[] memory recursiveAggregationInput; - // uint256[] memory serializedProof; - // proofInput = IExecutor.ProofInput(recursiveAggregationInput, serializedProof); - - // // foundry's default value is 1 for the block's timestamp, it is expected - // // that block.timestamp > COMMIT_TIMESTAMP_NOT_OLDER + 1 - // vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1 + 1); - // currentTimestamp = block.timestamp; - - // bytes memory l2Logs = Utils.encodePacked(Utils.createSystemLogs()); - // newCommitBatchInfo = IExecutor.CommitBatchInfo({ - // batchNumber: 1, - // timestamp: uint64(currentTimestamp), - // indexRepeatedStorageChanges: 1, - // newStateRoot: Utils.randomBytes32("newStateRoot"), - // numberOfLayer1Txs: 0, - // priorityOperationsHash: keccak256(""), - // bootloaderHeapInitialContentsHash: Utils.randomBytes32("bootloaderHeapInitialContentsHash"), - // eventsQueueStateHash: Utils.randomBytes32("eventsQueueStateHash"), - // systemLogs: l2Logs, - // pubdataCommitments: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - // }); - - // // Commit & prove batches - // vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1); - // currentTimestamp = block.timestamp; - - // bytes32 expectedSystemContractUpgradeTxHash = gettersFacet.getL2SystemContractsUpgradeTxHash(); - // bytes[] memory correctL2Logs = Utils.createSystemLogsWithUpgradeTransaction( - // expectedSystemContractUpgradeTxHash - // ); - - // correctL2Logs[uint256(uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY))] = Utils.constructL2Log( - // true, - // L2_SYSTEM_CONTEXT_ADDRESS, - // uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY), - // Utils.packBatchTimestampAndBlockTimestamp(currentTimestamp, currentTimestamp) - // ); - - // correctL2Logs[uint256(uint256(SystemLogKey.PREV_BATCH_HASH_KEY))] = Utils.constructL2Log( - // true, - // L2_SYSTEM_CONTEXT_ADDRESS, - // uint256(SystemLogKey.PREV_BATCH_HASH_KEY), - // bytes32(uint256(0x01)) - // ); - - // l2Logs = Utils.encodePacked(correctL2Logs); - // newCommitBatchInfo.timestamp = uint64(currentTimestamp); - // newCommitBatchInfo.systemLogs = l2Logs; - - // IExecutor.CommitBatchInfo[] memory commitBatchInfoArray = new IExecutor.CommitBatchInfo[](1); - // commitBatchInfoArray[0] = newCommitBatchInfo; - - // vm.stopPrank(); - // vm.startPrank(validator); - // vm.recordLogs(); - // executorFacet.commitBatchesSharedBridge(uint256(0), genesisStoredBatchInfo, commitBatchInfoArray); - // Vm.Log[] memory entries = vm.getRecordedLogs(); - - // newStoredBatchInfo = IExecutor.StoredBatchInfo({ - // batchNumber: 1, - // batchHash: entries[0].topics[2], - // indexRepeatedStorageChanges: 1, - // numberOfLayer1Txs: 0, - // priorityOperationsHash: keccak256(""), - // l2LogsTreeRoot: 0, - // timestamp: currentTimestamp, - // commitment: entries[0].topics[3] - // }); - - // IExecutor.StoredBatchInfo[] memory storedBatchInfoArray = new IExecutor.StoredBatchInfo[](1); - // storedBatchInfoArray[0] = newStoredBatchInfo; - - // executorFacet.proveBatches(genesisStoredBatchInfo, storedBatchInfoArray, proofInput); - - // // Test batch revert triggered from CTM - // vm.stopPrank(); - // vm.startPrank(governor); - - // uint256 totalBlocksCommittedBefore = gettersFacet.getTotalBlocksCommitted(); - // assertEq(totalBlocksCommittedBefore, 1, "totalBlocksCommittedBefore"); - - // uint256 totalBlocksVerifiedBefore = gettersFacet.getTotalBlocksVerified(); - // assertEq(totalBlocksVerifiedBefore, 1, "totalBlocksVerifiedBefore"); - - // chainContractAddress.revertBatches(chainId, 0); - - // uint256 totalBlocksCommitted = gettersFacet.getTotalBlocksCommitted(); - // assertEq(totalBlocksCommitted, 0, "totalBlocksCommitted"); - - // uint256 totalBlocksVerified = gettersFacet.getTotalBlocksVerified(); - // assertEq(totalBlocksVerified, 0, "totalBlocksVerified"); - // } + function setUp() public { + deploy(); + + defaultBlobCommitment = Utils.getDefaultBlobCommitment(); + defaultBlobVersionedHashes = new bytes32[](1); + defaultBlobVersionedHashes[0] = 0x01c024b4740620a5849f95930cefe298933bdf588123ea897cdf0f2462f6d2d5; + + bytes memory precompileInput = Utils.defaultPointEvaluationPrecompileInput(defaultBlobVersionedHashes[0]); + vm.mockCall(POINT_EVALUATION_PRECOMPILE_ADDR, precompileInput, POINT_EVALUATION_PRECOMPILE_RESULT); + + l2Logs = Utils.encodePacked(Utils.createSystemLogs(bytes32(0))); + genesisStoredBatchInfo = IExecutor.StoredBatchInfo({ + batchNumber: 0, + batchHash: bytes32(uint256(0x01)), + indexRepeatedStorageChanges: 0x01, + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, + timestamp: 0, + commitment: bytes32(uint256(0x01)) + }); + vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1 + 1); + currentTimestamp = block.timestamp; + newCommitBatchInfo = IExecutor.CommitBatchInfo({ + batchNumber: 1, + timestamp: uint64(currentTimestamp), + indexRepeatedStorageChanges: 0, + newStateRoot: Utils.randomBytes32("newStateRoot"), + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + bootloaderHeapInitialContentsHash: Utils.randomBytes32("bootloaderHeapInitialContentsHash"), + eventsQueueStateHash: Utils.randomBytes32("eventsQueueStateHash"), + systemLogs: l2Logs, + operatorDAInput: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + }); + + { + bytes memory complexUpgraderCalldata; + address l1CtmDeployer = address(bridgehub.l1CtmDeployer()); + { + bytes memory l2GenesisUpgradeCalldata = abi.encodeCall( + IL2GenesisUpgrade.genesisUpgrade, + (chainId, l1CtmDeployer, forceDeploymentsData, "0x") + ); + complexUpgraderCalldata = abi.encodeCall( + IComplexUpgrader.upgrade, + (L2_GENESIS_UPGRADE_ADDR, l2GenesisUpgradeCalldata) + ); + } + + // slither-disable-next-line unused-return + (, uint32 minorVersion, ) = SemVer.unpackSemVer(SafeCast.toUint96(0)); + } + + newChainAddress = createNewChain(getDiamondCutData(diamondInit)); + vm.mockCall( + address(bridgehub), + abi.encodeWithSelector(IBridgehub.getZKChain.selector), + abi.encode(newChainAddress) + ); + + executorFacet = ExecutorFacet(address(newChainAddress)); + gettersFacet = GettersFacet(address(newChainAddress)); + adminFacet = AdminFacet(address(newChainAddress)); + + vm.stopPrank(); + vm.prank(newChainAdmin); + adminFacet.setDAValidatorPair(address(rollupL1DAValidator), L2_DA_VALIDATOR_ADDRESS); + } + + function test_SuccessfulBatchReverting() public { + vm.startPrank(governor); + + bytes32 uncompressedStateDiffHash = Utils.randomBytes32("uncompressedStateDiffHash"); + bytes32 totalL2PubdataHash = Utils.randomBytes32("totalL2PubdataHash"); + uint8 numberOfBlobs = 1; + bytes32[] memory blobsLinearHashes = new bytes32[](1); + blobsLinearHashes[0] = Utils.randomBytes32("blobsLinearHashes"); + + operatorDAInput = abi.encodePacked( + uncompressedStateDiffHash, + totalL2PubdataHash, + numberOfBlobs, + blobsLinearHashes, + bytes1(0x01), + defaultBlobCommitment, + EMPTY_PREPUBLISHED_COMMITMENT + ); + + l2DAValidatorOutputHash = Utils.constructRollupL2DAValidatorOutputHash( + uncompressedStateDiffHash, + totalL2PubdataHash, + uint8(numberOfBlobs), + blobsLinearHashes + ); + + vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1); + currentTimestamp = block.timestamp; + bytes32 expectedSystemContractUpgradeTxHash = gettersFacet.getL2SystemContractsUpgradeTxHash(); + bytes[] memory correctL2Logs = Utils.createSystemLogsWithUpgradeTransactionForCTM( + expectedSystemContractUpgradeTxHash, + l2DAValidatorOutputHash + ); + correctL2Logs[uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY)] = Utils.constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY), + Utils.packBatchTimestampAndBlockTimestamp(currentTimestamp, currentTimestamp) + ); + + IExecutor.CommitBatchInfo memory correctNewCommitBatchInfo = newCommitBatchInfo; + correctNewCommitBatchInfo.timestamp = uint64(currentTimestamp); + correctNewCommitBatchInfo.systemLogs = Utils.encodePacked(correctL2Logs); + correctNewCommitBatchInfo.operatorDAInput = operatorDAInput; + + bytes32[] memory blobHashes = new bytes32[](TOTAL_BLOBS_IN_COMMITMENT); + blobHashes[0] = blobsLinearHashes[0]; + + bytes32[] memory blobCommitments = new bytes32[](TOTAL_BLOBS_IN_COMMITMENT); + blobCommitments[0] = keccak256( + abi.encodePacked( + defaultBlobVersionedHashes[0], + abi.encodePacked(defaultBlobOpeningPoint, defaultBlobClaimedValue) + ) + ); + + bytes32 expectedBatchCommitment = Utils.createBatchCommitment( + correctNewCommitBatchInfo, + uncompressedStateDiffHash, + blobCommitments, + blobHashes + ); + + IExecutor.CommitBatchInfo[] memory correctCommitBatchInfoArray = new IExecutor.CommitBatchInfo[](1); + correctCommitBatchInfoArray[0] = correctNewCommitBatchInfo; + correctCommitBatchInfoArray[0].operatorDAInput = operatorDAInput; + + vm.stopPrank(); + vm.startPrank(validator); + vm.blobhashes(defaultBlobVersionedHashes); + vm.recordLogs(); + (uint256 commitBatchFrom, uint256 commitBatchTo, bytes memory commitData) = Utils.encodeCommitBatchesData( + genesisStoredBatchInfo, + correctCommitBatchInfoArray + ); + executorFacet.commitBatchesSharedBridge(uint256(0), commitBatchFrom, commitBatchTo, commitData); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + + assertEq(entries.length, 1); + assertEq(entries[0].topics[0], keccak256("BlockCommit(uint256,bytes32,bytes32)")); + assertEq(entries[0].topics[1], bytes32(uint256(1))); // batchNumber + assertEq(entries[0].topics[2], correctNewCommitBatchInfo.newStateRoot); // batchHash + + uint256 totalBatchesCommitted = gettersFacet.getTotalBatchesCommitted(); + assertEq(totalBatchesCommitted, 1); + + newStoredBatchInfo = IExecutor.StoredBatchInfo({ + batchNumber: 1, + batchHash: entries[0].topics[2], + indexRepeatedStorageChanges: 0, + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, + timestamp: currentTimestamp, + commitment: entries[0].topics[3] + }); + + IExecutor.StoredBatchInfo[] memory storedBatchInfoArray = new IExecutor.StoredBatchInfo[](1); + storedBatchInfoArray[0] = newStoredBatchInfo; + + (uint256 proveBatchFrom, uint256 proveBatchTo, bytes memory proveData) = Utils.encodeProveBatchesData( + genesisStoredBatchInfo, + storedBatchInfoArray, + proofInput + ); + + executorFacet.proveBatchesSharedBridge(uint256(0), proveBatchFrom, proveBatchTo, proveData); + + // Test batch revert triggered from CTM + vm.stopPrank(); + vm.prank(address(chainContractAddress)); + adminFacet.setValidator(address(chainContractAddress), true); + vm.startPrank(governor); + + uint256 totalBlocksCommittedBefore = gettersFacet.getTotalBlocksCommitted(); + assertEq(totalBlocksCommittedBefore, 1, "totalBlocksCommittedBefore"); + + uint256 totalBlocksVerifiedBefore = gettersFacet.getTotalBlocksVerified(); + assertEq(totalBlocksVerifiedBefore, 1, "totalBlocksVerifiedBefore"); + + chainContractAddress.revertBatches(chainId, 0); + + uint256 totalBlocksCommitted = gettersFacet.getTotalBlocksCommitted(); + assertEq(totalBlocksCommitted, 0, "totalBlocksCommitted"); + + uint256 totalBlocksVerified = gettersFacet.getTotalBlocksVerified(); + assertEq(totalBlocksVerified, 0, "totalBlocksVerified"); + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol index 99d8c9859..5ecaa7407 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.21; import {Test} from "forge-std/Test.sol"; +import {console2 as console} from "forge-std/Script.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -10,6 +11,8 @@ import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {Utils} from "foundry-test/l1/unit/concrete/Utils/Utils.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {UtilsFacet} from "foundry-test/l1/unit/concrete/Utils/UtilsFacet.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; @@ -24,18 +27,24 @@ import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; import {DummyBridgehub} from "contracts/dev-contracts/test/DummyBridgehub.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {ZeroAddress} from "contracts/common/L1ContractErrors.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {RollupL1DAValidator} from "da-contracts/RollupL1DAValidator.sol"; contract ChainTypeManagerTest is Test { ChainTypeManager internal chainTypeManager; ChainTypeManager internal chainContractAddress; L1GenesisUpgrade internal genesisUpgradeContract; Bridgehub internal bridgehub; + RollupL1DAValidator internal rollupL1DAValidator; address internal diamondInit; address internal constant governor = address(0x1010101); address internal constant admin = address(0x2020202); address internal constant baseToken = address(0x3030303); address internal constant sharedBridge = address(0x4040404); address internal constant validator = address(0x5050505); + address internal constant l1Nullifier = address(0x6060606); address internal newChainAdmin; uint256 chainId = 112; address internal testnetVerifier = address(new TestnetVerifier()); @@ -46,6 +55,15 @@ contract ChainTypeManagerTest is Test { function deploy() public { bridgehub = new Bridgehub(block.chainid, governor, type(uint256).max); + vm.prank(governor); + bridgehub.setAddresses(sharedBridge, ICTMDeploymentTracker(address(0)), IMessageRoot(address(0))); + + vm.mockCall( + address(sharedBridge), + abi.encodeCall(L1AssetRouter.l2BridgeAddress, (chainId)), + abi.encode(makeAddr("l2BridgeAddress")) + ); + newChainAdmin = makeAddr("chainadmin"); vm.startPrank(address(bridgehub)); @@ -123,6 +141,8 @@ contract ChainTypeManagerTest is Test { ); chainContractAddress = ChainTypeManager(address(transparentUpgradeableProxy)); + rollupL1DAValidator = new RollupL1DAValidator(); + vm.stopPrank(); vm.startPrank(governor); } @@ -143,11 +163,22 @@ contract ChainTypeManagerTest is Test { vm.stopPrank(); vm.startPrank(address(bridgehub)); + vm.mockCall( + address(sharedBridge), + abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), + abi.encode(l1Nullifier) + ); + + vm.mockCall( + address(l1Nullifier), + abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), + abi.encode(l1Nullifier) + ); + return chainContractAddress.createNewChain({ _chainId: chainId, _baseTokenAssetId: DataEncoding.encodeNTVAssetId(block.chainid, baseToken), - _assetRouter: sharedBridge, _admin: newChainAdmin, _initData: abi.encode(abi.encode(_diamondCut), bytes("")), _factoryDeps: new bytes[](0) diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol index cfc826fa5..0b7dfbbe9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol @@ -86,7 +86,6 @@ contract InitializeTest is DiamondInitTest { assertEq(utilsFacet.util_getBridgehub(), initializeData.bridgehub); assertEq(utilsFacet.util_getChainTypeManager(), initializeData.chainTypeManager); assertEq(utilsFacet.util_getBaseTokenAssetId(), initializeData.baseTokenAssetId); - assertEq(utilsFacet.util_getBaseTokenBridge(), initializeData.baseTokenBridge); assertEq(utilsFacet.util_getProtocolVersion(), initializeData.protocolVersion); assertEq(address(utilsFacet.util_getVerifier()), address(initializeData.verifier)); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol deleted file mode 100644 index db32ca6bd..000000000 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -import {GettersFacetTest} from "./_Getters_Shared.t.sol"; - -contract GetBaseTokenBridgeTest is GettersFacetTest { - function test() public { - address expected = makeAddr("baseTokenBride"); - gettersFacetWrapper.util_setBaseTokenBridge(expected); - - address received = gettersFacet.getBaseTokenBridge(); - - assertEq(expected, received, "BaseTokenBridge address is incorrect"); - } -} diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol index 557378c63..9f66926e7 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol @@ -36,10 +36,6 @@ contract GettersFacetWrapper is GettersFacet { s.baseTokenAssetId = _baseTokenAssetId; } - function util_setBaseTokenBridge(address _baseTokenBridge) external { - s.baseTokenBridge = _baseTokenBridge; - } - function util_setTotalBatchesCommitted(uint256 _totalBatchesCommitted) external { s.totalBatchesCommitted = _totalBatchesCommitted; } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol index c71721c79..5e7fa27f6 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol @@ -9,6 +9,7 @@ import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {DummySharedBridge} from "contracts/dev-contracts/test/DummySharedBridge.sol"; import {OnlyEraSupported} from "contracts/common/L1ContractErrors.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; contract MailboxFinalizeWithdrawal is MailboxTest { bytes32[] proof; @@ -22,6 +23,8 @@ contract MailboxFinalizeWithdrawal is MailboxTest { L1AssetRouter = new DummySharedBridge(keccak256("dummyDepositHash")); baseTokenBridgeAddress = address(L1AssetRouter); + vm.mockCall(bridgehub, abi.encodeCall(Bridgehub.sharedBridge, ()), abi.encode(baseTokenBridgeAddress)); + proof = new bytes32[](0); message = "message"; } @@ -40,9 +43,7 @@ contract MailboxFinalizeWithdrawal is MailboxTest { } function test_success_withdrawal(uint256 amount) public { - address baseTokenBridge = makeAddr("baseTokenBridge"); utilsFacet.util_setChainId(eraChainId); - utilsFacet.util_setBaseTokenBridge(baseTokenBridgeAddress); address l1Receiver = makeAddr("receiver"); address l1Token = address(1); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol index 85bcd8be8..0ea16b46c 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol @@ -11,6 +11,7 @@ import {FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-de import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {DummySharedBridge} from "contracts/dev-contracts/test/DummySharedBridge.sol"; import {OnlyEraSupported, TooManyFactoryDeps, MsgValueTooLow, GasPerPubdataMismatch} from "contracts/common/L1ContractErrors.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; contract MailboxRequestL2TransactionTest is MailboxTest { address tempAddress; @@ -24,6 +25,7 @@ contract MailboxRequestL2TransactionTest is MailboxTest { l1SharedBridge = new DummySharedBridge(keccak256("dummyDepositHash")); baseTokenBridgeAddress = address(l1SharedBridge); + vm.mockCall(bridgehub, abi.encodeCall(Bridgehub.sharedBridge, ()), abi.encode(baseTokenBridgeAddress)); tempAddress = makeAddr("temp"); tempBytesArr = new bytes[](0); @@ -122,7 +124,6 @@ contract MailboxRequestL2TransactionTest is MailboxTest { function test_RevertWhen_bridgePaused(uint256 randomValue) public { utilsFacet.util_setBaseTokenGasPriceMultiplierDenominator(1); utilsFacet.util_setPriorityTxMaxGasLimit(100000000); - utilsFacet.util_setBaseTokenBridge(baseTokenBridgeAddress); uint256 l2GasLimit = 1000000; uint256 baseCost = mailboxFacet.l2TransactionBaseCost(10000000, l2GasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA); @@ -137,7 +138,6 @@ contract MailboxRequestL2TransactionTest is MailboxTest { function test_success_requestL2Transaction(uint256 randomValue) public { utilsFacet.util_setBaseTokenGasPriceMultiplierDenominator(1); utilsFacet.util_setPriorityTxMaxGasLimit(100000000); - utilsFacet.util_setBaseTokenBridge(baseTokenBridgeAddress); uint256 l2GasLimit = 1000000; uint256 baseCost = mailboxFacet.l2TransactionBaseCost(10000000, l2GasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol index b1ef215d8..37755b08e 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol @@ -21,9 +21,11 @@ contract MailboxTest is Test { uint256 constant eraChainId = 9; address internal testnetVerifier = address(new TestnetVerifier()); address diamondProxy; + address bridgehub; function setupDiamondProxy() public virtual { sender = makeAddr("sender"); + bridgehub = makeAddr("bridgehub"); vm.deal(sender, 100 ether); Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](3); @@ -50,6 +52,8 @@ contract MailboxTest is Test { mailboxFacet = IMailbox(diamondProxy); utilsFacet = UtilsFacet(diamondProxy); gettersFacet = IGetters(diamondProxy); + + utilsFacet.util_setBridgehub(bridgehub); } // add this to be excluded from coverage report diff --git a/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol b/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol new file mode 100644 index 000000000..9b308fd9e --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/L2ERC20BridgeTest.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {L2Utils, SystemContractsArgs} from "./L2Utils.sol"; +import {SharedL2ContractL2DeployerUtils} from "./_SharedL2ContractL2DeployerUtils.sol"; +import {L2Erc20TestAbstract} from "../../l1/integration/l2-dummy-tests/L2Erc20TestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; + +contract L2Erc20Test is Test, L2Erc20TestAbstract, SharedL2ContractL2DeployerUtils { + function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal override(SharedL2ContractDeployer, SharedL2ContractL2DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override(DeployUtils, SharedL2ContractL2DeployerUtils) returns (address) { + return super.deployViaCreate2(creationCode, constructorArgs); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public override(SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol new file mode 100644 index 000000000..f9518d213 --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/L2GatewayTests.t.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +// solhint-disable gas-custom-errors + +import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; + +// import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; +import {IL2NativeTokenVault} from "contracts/bridge/ntv/IL2NativeTokenVault.sol"; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +// import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; + +import {L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {ETH_TOKEN_ADDRESS, SETTLEMENT_LAYER_RELAY_SENDER} from "contracts/common/Config.sol"; + +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {BridgehubMintCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {IL2AssetRouter} from "contracts/bridge/asset-router/IL2AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; + +import {L2Utils, SystemContractsArgs} from "./L2Utils.sol"; + +import {SharedL2ContractL2DeployerUtils} from "./_SharedL2ContractL2DeployerUtils.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {L2GatewayTestAbstract} from "../../l1/integration/l2-dummy-tests/L2GatewayTestAbstract.t.sol"; +import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; + +contract L2GatewayTests is Test, L2GatewayTestAbstract, SharedL2ContractL2DeployerUtils { + // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. + // It is a bit easier to use EOA and it is sufficient for the tests. + function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal override(SharedL2ContractDeployer, SharedL2ContractL2DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override(DeployUtils, SharedL2ContractL2DeployerUtils) returns (address) { + return super.deployViaCreate2(creationCode, constructorArgs); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public override(SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol b/l1-contracts/test/foundry/l2/integration/L2Utils.sol similarity index 55% rename from l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol rename to l1-contracts/test/foundry/l2/integration/L2Utils.sol index 9da81fe5d..9a0afb563 100644 --- a/l1-contracts/test/foundry/l2/unit/utils/L2Utils.sol +++ b/l1-contracts/test/foundry/l2/integration/L2Utils.sol @@ -5,15 +5,28 @@ pragma solidity ^0.8.20; import {Vm} from "forge-std/Vm.sol"; import "forge-std/console.sol"; -import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {BeaconProxy} from "@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol"; +import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_BRIDGEHUB_ADDR, L2_MESSAGE_ROOT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IContractDeployer, L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {L2AssetRouter} from "contracts/bridge/asset-router/L2AssetRouter.sol"; import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; +import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {Bridgehub, IBridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; + +import {SystemContractsCaller} from "contracts/common/libraries/SystemContractsCaller.sol"; +import {DeployFailed} from "contracts/common/L1ContractErrors.sol"; +import {SystemContractsArgs} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; library L2Utils { address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); @@ -23,6 +36,7 @@ library L2Utils { string internal constant L2_ASSET_ROUTER_PATH = "./zkout/L2AssetRouter.sol/L2AssetRouter.json"; string internal constant L2_NATIVE_TOKEN_VAULT_PATH = "./zkout/L2NativeTokenVault.sol/L2NativeTokenVault.json"; + string internal constant BRIDGEHUB_PATH = "./zkout/Bridgehub.sol/Bridgehub.json"; /// @notice Returns the bytecode of a given era contract from a `zkout` folder. function readEraBytecode(string memory _filename) internal returns (bytes memory bytecode) { @@ -54,9 +68,61 @@ library L2Utils { * @dev Initializes the system contracts. * @dev It is a hack needed to make the tests be able to call system contracts directly. */ - function initSystemContracts() internal { + function initSystemContracts(SystemContractsArgs memory _args) internal { bytes memory contractDeployerBytecode = readSystemContractsBytecode("ContractDeployer"); vm.etch(DEPLOYER_SYSTEM_CONTRACT, contractDeployerBytecode); + forceDeploySystemContracts(_args); + } + + function forceDeploySystemContracts(SystemContractsArgs memory _args) internal { + forceDeployMessageRoot(); + forceDeployBridgehub( + _args.l1ChainId, + _args.eraChainId, + _args.aliasedOwner, + _args.l1AssetRouter, + _args.legacySharedBridge, + _args.l1CtmDeployer + ); + forceDeployAssetRouter( + _args.l1ChainId, + _args.eraChainId, + _args.aliasedOwner, + _args.l1AssetRouter, + _args.legacySharedBridge + ); + forceDeployNativeTokenVault( + _args.l1ChainId, + _args.aliasedOwner, + _args.l2TokenProxyBytecodeHash, + _args.legacySharedBridge, + _args.l2TokenBeacon, + _args.contractsDeployedAlready + ); + } + + function forceDeployMessageRoot() internal { + new MessageRoot(IBridgehub(L2_BRIDGEHUB_ADDR)); + forceDeployWithConstructor("MessageRoot", L2_MESSAGE_ROOT_ADDR, abi.encode(L2_BRIDGEHUB_ADDR)); + } + + function forceDeployBridgehub( + uint256 _l1ChainId, + uint256 _eraChainId, + address _aliasedOwner, + address _l1AssetRouter, + address _legacySharedBridge, + address _l1CtmDeployer + ) internal { + new Bridgehub(_l1ChainId, _aliasedOwner, 100); + forceDeployWithConstructor("Bridgehub", L2_BRIDGEHUB_ADDR, abi.encode(_l1ChainId, _aliasedOwner, 100)); + Bridgehub bridgehub = Bridgehub(L2_BRIDGEHUB_ADDR); + vm.prank(_aliasedOwner); + bridgehub.setAddresses( + L2_ASSET_ROUTER_ADDR, + ICTMDeploymentTracker(_l1CtmDeployer), + IMessageRoot(L2_MESSAGE_ROOT_ADDR) + ); } /// @notice Deploys the L2AssetRouter contract. @@ -76,22 +142,11 @@ library L2Utils { { new L2AssetRouter(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner); } - - bytes memory bytecode = readEraBytecode("L2AssetRouter"); - - bytes32 bytecodehash = L2ContractHelper.hashL2Bytecode(bytecode); - - IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); - deployments[0] = IContractDeployer.ForceDeployment({ - bytecodeHash: bytecodehash, - newAddress: L2_ASSET_ROUTER_ADDR, - callConstructor: true, - value: 0, - input: abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner) - }); - - vm.prank(L2_FORCE_DEPLOYER_ADDR); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); + forceDeployWithConstructor( + "L2AssetRouter", + L2_ASSET_ROUTER_ADDR, + abi.encode(_l1ChainId, _eraChainId, _l1AssetRouter, _legacySharedBridge, ethAssetId, _aliasedOwner) + ); } /// @notice Deploys the L2NativeTokenVault contract. @@ -123,19 +178,10 @@ library L2Utils { _baseTokenAssetId: ethAssetId }); } - - bytes memory bytecode = readEraBytecode("L2NativeTokenVault"); - - bytes32 bytecodehash = L2ContractHelper.hashL2Bytecode(bytecode); - - IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); - deployments[0] = IContractDeployer.ForceDeployment({ - bytecodeHash: bytecodehash, - newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode( + forceDeployWithConstructor( + "L2NativeTokenVault", + L2_NATIVE_TOKEN_VAULT_ADDR, + abi.encode( _l1ChainId, _aliasedOwner, _l2TokenProxyBytecodeHash, @@ -145,25 +191,48 @@ library L2Utils { address(0), ethAssetId ) + ); + } + + function forceDeployWithConstructor( + string memory _contractName, + address _address, + bytes memory _constructorArgs + ) public { + bytes memory bytecode = readEraBytecode(_contractName); + + bytes32 bytecodehash = L2ContractHelper.hashL2Bytecode(bytecode); + + IContractDeployer.ForceDeployment[] memory deployments = new IContractDeployer.ForceDeployment[](1); + deployments[0] = IContractDeployer.ForceDeployment({ + bytecodeHash: bytecodehash, + newAddress: _address, + callConstructor: true, + value: 0, + input: _constructorArgs }); vm.prank(L2_FORCE_DEPLOYER_ADDR); IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses(deployments); } - /// @notice Encodes the token data. - /// @param name The name of the token. - /// @param symbol The symbol of the token. - /// @param decimals The decimals of the token. - function encodeTokenData( - string memory name, - string memory symbol, - uint8 decimals - ) internal pure returns (bytes memory) { - bytes memory encodedName = abi.encode(name); - bytes memory encodedSymbol = abi.encode(symbol); - bytes memory encodedDecimals = abi.encode(decimals); - - return abi.encode(encodedName, encodedSymbol, encodedDecimals); + function deployViaCreat2L2( + bytes memory creationCode, + bytes memory constructorargs, + bytes32 create2salt + ) internal returns (address) { + bytes memory bytecode = abi.encodePacked(creationCode, constructorargs); + address contractAddress; + assembly { + contractAddress := create2(0, add(bytecode, 0x20), mload(bytecode), create2salt) + } + uint32 size; + assembly { + size := extcodesize(contractAddress) + } + if (size == 0) { + revert DeployFailed(); + } + return contractAddress; } } diff --git a/l1-contracts/test/foundry/l2/integration/WETH.t.sol b/l1-contracts/test/foundry/l2/integration/WETH.t.sol new file mode 100644 index 000000000..5a0b8e246 --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/WETH.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +import {SharedL2ContractDeployer} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractDeployer.sol"; +import {SharedL2ContractL1DeployerUtils} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; +import {L2WethTestAbstract} from "../../l1/integration/l2-dummy-tests/L2WethTestAbstract.t.sol"; + +import {SharedL2ContractL2DeployerUtils, SystemContractsArgs} from "./_SharedL2ContractL2DeployerUtils.sol"; + +contract WethTest is Test, L2WethTestAbstract, SharedL2ContractL2DeployerUtils { + function test() internal virtual override(DeployUtils, SharedL2ContractL2DeployerUtils) {} + + function initSystemContracts( + SystemContractsArgs memory _args + ) internal override(SharedL2ContractDeployer, SharedL2ContractL2DeployerUtils) { + super.initSystemContracts(_args); + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal override(DeployUtils, SharedL2ContractL2DeployerUtils) returns (address) { + return super.deployViaCreate2(creationCode, constructorArgs); + } + + function deployL2Contracts( + uint256 _l1ChainId + ) public override(SharedL2ContractL1DeployerUtils, SharedL2ContractDeployer) { + super.deployL2Contracts(_l1ChainId); + } +} diff --git a/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol new file mode 100644 index 000000000..61f51d0cd --- /dev/null +++ b/l1-contracts/test/foundry/l2/integration/_SharedL2ContractL2DeployerUtils.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {StdStorage, stdStorage, stdToml} from "forge-std/Test.sol"; +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {DeployedAddresses, Config} from "deploy-scripts/DeployUtils.s.sol"; + +import {DeployUtils} from "deploy-scripts/DeployUtils.s.sol"; + +import {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "contracts/common/L2ContractAddresses.sol"; + +import {L2Utils} from "./L2Utils.sol"; +import {SharedL2ContractL1DeployerUtils, SystemContractsArgs} from "../../l1/integration/l2-dummy-tests/_SharedL2ContractL1DeployerUtils.sol"; + +contract SharedL2ContractL2DeployerUtils is DeployUtils, SharedL2ContractL1DeployerUtils { + using stdToml for string; + + function initSystemContracts(SystemContractsArgs memory _args) internal virtual override { + L2Utils.initSystemContracts(_args); + } + + function deployViaCreate2( + bytes memory creationCode, + bytes memory constructorArgs + ) internal virtual override returns (address) { + console.log("Deploying via create2 L2"); + return L2Utils.deployViaCreat2L2(creationCode, constructorArgs, config.contracts.create2FactorySalt); + } + + // add this to be excluded from coverage report + function test() internal virtual override(DeployUtils, SharedL2ContractL1DeployerUtils) {} +} diff --git a/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol new file mode 100644 index 000000000..7b85a8c54 --- /dev/null +++ b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2AdminFactory} from "contracts/governance/L2AdminFactory.sol"; +import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; +import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; + +contract L2AdminFactoryTest is Test { + function testL2AdminFactory() public { + address[] memory requiredRestrictions = new address[](1); + requiredRestrictions[0] = makeAddr("required"); + + L2AdminFactory factory = new L2AdminFactory(requiredRestrictions); + + address[] memory additionalRestrictions = new address[](1); + additionalRestrictions[0] = makeAddr("additional"); + + address[] memory allRestrictions = new address[](2); + allRestrictions[0] = requiredRestrictions[0]; + allRestrictions[1] = additionalRestrictions[0]; + + bytes32 salt = keccak256("salt"); + + address admin = factory.deployAdmin(additionalRestrictions, salt); + + // Now, we need to check whether it would be able to accept such an admin + PermanentRestriction restriction = new PermanentRestriction(IBridgehub(address(0)), address(factory)); + + bytes32 codeHash; + assembly { + codeHash := extcodehash(admin) + } + + vm.expectEmit(true, false, false, true); + emit IPermanentRestriction.AllowL2Admin(admin); + restriction.allowL2Admin(salt, codeHash, keccak256(abi.encode(allRestrictions))); + } +} diff --git a/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol b/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol index c54367295..3374e1acc 100644 --- a/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol +++ b/l2-contracts/test/foundry/unit/data-availability/ValidiumL2DAValidator.t.sol @@ -8,7 +8,7 @@ import {Test} from "forge-std/Test.sol"; import {ValidiumL2DAValidator} from "contracts/data-availability/ValidiumL2DAValidator.sol"; -contract L2Erc20BridgeTest is Test { +contract L2ValidiumDAValidatorTest is Test { function test_callValidiumDAValidator(address depositor, address receiver, uint256 amount) internal { ValidiumL2DAValidator validator = new ValidiumL2DAValidator(); diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 8b0feaf78..3f351ca43 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,49 +3,49 @@ "contractName": "AccountCodeStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", - "bytecodeHash": "0x0100005d7a6c264bffa97a66a5ba8daff43b8a34e5ab7fab4e2ddf74f0a10e4c", + "bytecodeHash": "0x0100005d3ae95fb62791ed4693e614755bd780011ffc3d2dea8344fb1284f9df", "sourceCodeHash": "0x2e0e09d57a04bd1e722d8bf8c6423fdf3f8bca44e5e8c4f6684f987794be066e" }, { "contractName": "BootloaderUtilities", "bytecodePath": "artifacts-zk/contracts-preprocessed/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", - "bytecodeHash": "0x010007c7b6bd43d607e55f594e743394b7ae6288ac7f6caad8a7904b6c990e32", + "bytecodeHash": "0x010007c7daac9a547e1e20ed650b09b21668c3fb49e23cea5113dd8cb224b9ac", "sourceCodeHash": "0x0f1213c4b95acb71f4ab5d4082cc1aeb2bd5017e1cccd46afc66e53268609d85" }, { "contractName": "ComplexUpgrader", "bytecodePath": "artifacts-zk/contracts-preprocessed/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", - "bytecodeHash": "0x0100004d8c1520bc212778de21b5df751968cbbf1690ddd362ea7ab844ec0b1d", + "bytecodeHash": "0x0100004de3ddc8c5296fed145323ba1d6874af80469e40657bf0ff79d113ccb5", "sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614" }, { "contractName": "Compressor", "bytecodePath": "artifacts-zk/contracts-preprocessed/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", - "bytecodeHash": "0x0100014ba4bf3056bc1d10e64986c71972db9056c879eed689163f7c91bb596f", + "bytecodeHash": "0x0100014b3784efd0fbc6825fa84f3dcf9fc1dcbed37a681c57098c347527ba21", "sourceCodeHash": "0x7240b5fb2ea8e184522e731fb14f764ebae52b8a69d1870a55daedac9a3ed617" }, { "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x010004e5ad1de716de961c9e52e3b1d6219709891024e5b28d8cde96fe7fdc69", + "bytecodeHash": "0x010004e522c95733920e0a1f072b5dc36dd3d6a1b30515de48423575c10a8f7e", "sourceCodeHash": "0x92bc09da23ed9d86ba7a84f0dbf48503c99582ae58cdbebbdcc5f14ea1fcf014" }, { "contractName": "Create2Factory", "bytecodePath": "artifacts-zk/contracts-preprocessed/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", - "bytecodeHash": "0x01000049bae223f356480d01ad05099bad8cfc3e0a91e206ae5dd72abb187cb1", + "bytecodeHash": "0x01000049a4e4beb07895adcdcf34186af3d28a9f3c1d9b56c72c8464730755f1", "sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3" }, { "contractName": "DefaultAccount", "bytecodePath": "artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", - "bytecodeHash": "0x0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e", + "bytecodeHash": "0x0100055d3993e14104994ca4d8cfa91beb9b544ee86894b45708b4824d832ff2", "sourceCodeHash": "0xebffe840ebbd9329edb1ebff8ca50f6935e7dabcc67194a896fcc2e968d46dfb" }, { @@ -59,63 +59,77 @@ "contractName": "ImmutableSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", - "bytecodeHash": "0x01000039fcb0afdc4480b9a83e0a5cfc2dbc55dce4f6f1d6363778b4e9371ca9", + "bytecodeHash": "0x010000394846da43b9adfe72f0820c19d39daaf861e2eae55d6fe248840f641e", "sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98" }, { "contractName": "KnownCodesStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", - "bytecodeHash": "0x0100006f2889f3200b41f87f8e0835f970e47e513548bbf68239577f8bd97816", + "bytecodeHash": "0x0100006f1fa761c40d5b3325482c8bc9a577ac65278b624523b67eb99cf7e51c", "sourceCodeHash": "0xb39b5b81168653e0c5062f7b8e1d6d15a4e186df3317f192f0cb2fc3a74f5448" }, { "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/contracts-preprocessed/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", - "bytecodeHash": "0x010001f735b209b666e9e3d377cf7b5c0483a57dff7b1cd035b58ac10c2e0771", + "bytecodeHash": "0x010001f74edfe69d83816cc586cbb42b2a37d2649dcd1cab88052a37dffaadf3", "sourceCodeHash": "0x8d22a4019347a45cb0c27bed9e98f7033637a7bdcd90fafb1922caa48f2b05de" }, { "contractName": "L2BaseToken", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", - "bytecodeHash": "0x010001036ff04ddfde50fac4cf41bf9a34df472373e8b2769938cb35d293f7a7", + "bytecodeHash": "0x0100010395d69e52583bf981c6eb16a7f45a4e930671c69df04e24c58dba3648", "sourceCodeHash": "0x8bdd2b4d0b53dba84c9f0af250bbaa2aad10b3de6747bba957f0bd3721090dfa" }, + { + "contractName": "L2GatewayUpgrade", + "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GatewayUpgrade.sol/L2GatewayUpgrade.json", + "sourceCodePath": "contracts-preprocessed/L2GatewayUpgrade.sol", + "bytecodeHash": "0x0100019d6f999ca8393f86be0b24f494bf647cc3ec56e01eed6aa233c6a0e6c0", + "sourceCodeHash": "0x3b85c44fc4fdc422c43d8bf8a2b8229665698996a034bf37c895e963f6c839bf" + }, + { + "contractName": "L2GatewayUpgradeHelper", + "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GatewayUpgradeHelper.sol/L2GatewayUpgradeHelper.json", + "sourceCodePath": "contracts-preprocessed/L2GatewayUpgradeHelper.sol", + "bytecodeHash": "0x01000007a010cd0e6cd1a9fcb802ccc92eb7a1acfb6566864ff6429a2c0ddf0a", + "sourceCodeHash": "0xe7bdc91f70cd5c395435423c25dd9cd27a99e19e12965854106cdc1385bcf14d" + }, { "contractName": "L2GenesisUpgrade", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GenesisUpgrade.sol/L2GenesisUpgrade.json", "sourceCodePath": "contracts-preprocessed/L2GenesisUpgrade.sol", - "bytecodeHash": "0x01000109946aaf60780aa88277365bdd6bc2719f5a2592079a91aae7058cee58", - "sourceCodeHash": "0xbfe430d992d5740c4befdc7adbac2bb9a33c25a45c30ed9fe86c2b4e0263778a" + "bytecodeHash": "0x010000fb0d40ab01a12ce7bad289cb44ab49563e51e6e1d889d431492a4b320f", + "sourceCodeHash": "0xdd7a89c11b624282aed07ef9d985d2fd006015a0aa0b11aec8e445695f7dd689" }, { "contractName": "MsgValueSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", - "bytecodeHash": "0x0100005dc9890cf9aca1c56e823e2147619ea5058c70e123a6a76e51bcee8956", + "bytecodeHash": "0x0100005d895eb5ad625c93a99c3796c18e8fda2e34e9af6997c5208aea197cc2", "sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71" }, { "contractName": "NonceHolder", "bytecodePath": "artifacts-zk/contracts-preprocessed/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", - "bytecodeHash": "0x010000d9ff72782330c259f50fc3ff63e5f8b0a2277e6d652c2a60c56e60efec", + "bytecodeHash": "0x010000d9515266d6a161d41fe0789fe8e75a31a3d8c0ce915ed09fa4ffcd7c61", "sourceCodeHash": "0xcd0c0366effebf2c98c58cf96322cc242a2d1c675620ef5514b7ed1f0a869edc" }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "artifacts-zk/contracts-preprocessed/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", - "bytecodeHash": "0x0100004994ca7f560b82e531240c2bac414d02180c33f75363de4edc00796c15", + "bytecodeHash": "0x010000491ab9335e1a112a136580f1f1b2c2929d11be12aed00fd94925bc3fc1", "sourceCodeHash": "0x04d3d2e4019081c87aae5c22a060d84ae2e9d631ebce59801ecce37b9c87e4c7" }, { "contractName": "SystemContext", "bytecodePath": "artifacts-zk/contracts-preprocessed/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", - "bytecodeHash": "0x010001a7e10cfa64a3b326897067f582f992db9fbb9bf50304d5db6525de961d", + "bytecodeHash": "0x010001a7baaabeaf80186c0dec134f606186fa9f73f91e753806b424ca8f171f", "sourceCodeHash": "0xb3b8c1f57928938ac590984442bc96c2c888282793014845d5ce2f90bbf2677f" }, { diff --git a/system-contracts/contracts/L2GatewayUpgrade.sol b/system-contracts/contracts/L2GatewayUpgrade.sol new file mode 100644 index 000000000..59d96102d --- /dev/null +++ b/system-contracts/contracts/L2GatewayUpgrade.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {DEPLOYER_SYSTEM_CONTRACT} from "./Constants.sol"; +import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer.sol"; +import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; +import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; + +import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol"; +import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + +/// @custom:security-contact security@matterlabs.dev +/// @author Matter Labs +/// @notice The contract that is used for facilitating the upgrade of the L2 +/// to the protocol version that supports gateway +/// @dev This contract is neither predeployed nor a system contract. It is located +/// in this folder due to very overlapping functionality with `L2GenesisUpgrade` and +/// facilitating reusage of the code. +/// @dev During the upgrade, it will be delegate-called by the `ComplexUpgrader` contract. +contract L2GatewayUpgrade { + function upgrade( + ForceDeployment[] calldata _forceDeployments, + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) external payable { + // Firstly, we force deploy the main set of contracts. + // Those will be deployed without any contract invocation. + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(_forceDeployments); + + // Secondly, we perform the more complex deployment of the gateway contracts. + L2GatewayUpgradeHelper.performGatewayContractsInit( + _ctmDeployer, + _fixedForceDeploymentsData, + _additionalForceDeploymentsData + ); + + ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( + _additionalForceDeploymentsData, + (ZKChainSpecificForceDeploymentsData) + ); + + address l2LegacyBridgeAddress = additionalForceDeploymentsData.l2LegacySharedBridge; + + if (l2LegacyBridgeAddress != address(0)) { + FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( + _fixedForceDeploymentsData, + (FixedForceDeploymentsData) + ); + + // Firstly, upgrade the legacy L2SharedBridge + bytes memory bridgeUpgradeData = abi.encodeCall( + ITransparentUpgradeableProxy.upgradeTo, + (fixedForceDeploymentsData.l2SharedBridgeLegacyImpl) + ); + SystemContractHelper.mimicCallWithPropagatedRevert( + l2LegacyBridgeAddress, + fixedForceDeploymentsData.l2BridgeProxyOwnerAddress, + bridgeUpgradeData + ); + + // Secondly, upgrade the tokens + UpgradeableBeacon upgradableBeacon = IL2SharedBridgeLegacy(l2LegacyBridgeAddress).l2TokenBeacon(); + bytes memory beaconUpgradeData = abi.encodeCall( + UpgradeableBeacon.upgradeTo, + (fixedForceDeploymentsData.l2BridgedStandardERC20Impl) + ); + SystemContractHelper.mimicCallWithPropagatedRevert( + address(upgradableBeacon), + fixedForceDeploymentsData.l2BridgedStandardERC20ProxyOwnerAddress, + beaconUpgradeData + ); + } + } +} diff --git a/system-contracts/contracts/L2GatewayUpgradeHelper.sol b/system-contracts/contracts/L2GatewayUpgradeHelper.sol new file mode 100644 index 000000000..e5004bc8a --- /dev/null +++ b/system-contracts/contracts/L2GatewayUpgradeHelper.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {DEPLOYER_SYSTEM_CONTRACT, L2_BRIDGE_HUB, L2_ASSET_ROUTER, L2_MESSAGE_ROOT, L2_NATIVE_TOKEN_VAULT_ADDR} from "./Constants.sol"; +import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer.sol"; +import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; +import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; + +library L2GatewayUpgradeHelper { + function performGatewayContractsInit( + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) internal { + ForceDeployment[] memory forceDeployments = _getForceDeploymentsData( + _fixedForceDeploymentsData, + _additionalForceDeploymentsData + ); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(forceDeployments); + + // It is expected that either via to the force deployments above + // or upon init both the L2 deployment of Bridgehub, AssetRouter and MessageRoot are deployed. + // (The comment does not mention the exact order in case it changes) + // However, there is still some follow up finalization that needs to be done. + + address bridgehubOwner = L2_BRIDGE_HUB.owner(); + + bytes memory data = abi.encodeCall( + L2_BRIDGE_HUB.setAddresses, + (L2_ASSET_ROUTER, _ctmDeployer, address(L2_MESSAGE_ROOT)) + ); + + (bool success, bytes memory returnData) = SystemContractHelper.mimicCall( + address(L2_BRIDGE_HUB), + bridgehubOwner, + data + ); + if (!success) { + // Progapatate revert reason + assembly { + revert(add(returnData, 0x20), returndatasize()) + } + } + } + + function _getForceDeploymentsData( + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) internal view returns (ForceDeployment[] memory forceDeployments) { + FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( + _fixedForceDeploymentsData, + (FixedForceDeploymentsData) + ); + ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( + _additionalForceDeploymentsData, + (ZKChainSpecificForceDeploymentsData) + ); + + forceDeployments = new ForceDeployment[](4); + + forceDeployments[0] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.messageRootBytecodeHash, + newAddress: address(L2_MESSAGE_ROOT), + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: abi.encode(address(L2_BRIDGE_HUB)) + }); + + forceDeployments[1] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.bridgehubBytecodeHash, + newAddress: address(L2_BRIDGE_HUB), + callConstructor: true, + value: 0, + input: abi.encode( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + fixedForceDeploymentsData.maxNumberOfZKChains + ) + }); + + forceDeployments[2] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.l2AssetRouterBytecodeHash, + newAddress: address(L2_ASSET_ROUTER), + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: abi.encode( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.eraChainId, + fixedForceDeploymentsData.l1AssetRouter, + additionalForceDeploymentsData.l2LegacySharedBridge, + additionalForceDeploymentsData.baseTokenAssetId, + fixedForceDeploymentsData.aliasedL1Governance + ) + }); + + forceDeployments[3] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.l2NtvBytecodeHash, + newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: abi.encode( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + fixedForceDeploymentsData.l2TokenProxyBytecodeHash, + additionalForceDeploymentsData.l2LegacySharedBridge, + address(0), // this is used if the contract were already deployed, so for the migration of Era. + false, + additionalForceDeploymentsData.l2Weth, + additionalForceDeploymentsData.baseTokenAssetId + ) + }); + } +} diff --git a/system-contracts/contracts/L2GenesisUpgrade.sol b/system-contracts/contracts/L2GenesisUpgrade.sol index 35d03b648..4d5592529 100644 --- a/system-contracts/contracts/L2GenesisUpgrade.sol +++ b/system-contracts/contracts/L2GenesisUpgrade.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.24; -import {DEPLOYER_SYSTEM_CONTRACT, SYSTEM_CONTEXT_CONTRACT, L2_BRIDGE_HUB, L2_ASSET_ROUTER, L2_MESSAGE_ROOT, L2_NATIVE_TOKEN_VAULT_ADDR} from "./Constants.sol"; -import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer.sol"; -import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; +import {SYSTEM_CONTEXT_CONTRACT} from "./Constants.sol"; import {ISystemContext} from "./interfaces/ISystemContext.sol"; -import {IL2GenesisUpgrade, FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; +import {IL2GenesisUpgrade} from "./interfaces/IL2GenesisUpgrade.sol"; + +import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol"; /// @custom:security-contact security@matterlabs.dev /// @author Matter Labs @@ -21,107 +21,13 @@ contract L2GenesisUpgrade is IL2GenesisUpgrade { // solhint-disable-next-line gas-custom-errors require(_chainId != 0, "Invalid chainId"); ISystemContext(SYSTEM_CONTEXT_CONTRACT).setChainId(_chainId); - ForceDeployment[] memory forceDeployments = _getForceDeploymentsData( + + L2GatewayUpgradeHelper.performGatewayContractsInit( + _ctmDeployer, _fixedForceDeploymentsData, _additionalForceDeploymentsData ); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(forceDeployments); - - // It is expected that either via to the force deployments above - // or upon init both the L2 deployment of Bridgehub, AssetRouter and MessageRoot are deployed. - // (The comment does not mention the exact order in case it changes) - // However, there is still some follow up finalization that needs to be done. - - address bridgehubOwner = L2_BRIDGE_HUB.owner(); - - bytes memory data = abi.encodeCall( - L2_BRIDGE_HUB.setAddresses, - (L2_ASSET_ROUTER, _ctmDeployer, address(L2_MESSAGE_ROOT)) - ); - - (bool success, bytes memory returnData) = SystemContractHelper.mimicCall( - address(L2_BRIDGE_HUB), - bridgehubOwner, - data - ); - if (!success) { - // Progapatate revert reason - assembly { - revert(add(returnData, 0x20), returndatasize()) - } - } emit UpgradeComplete(_chainId); } - - function _getForceDeploymentsData( - bytes calldata _fixedForceDeploymentsData, - bytes calldata _additionalForceDeploymentsData - ) internal view returns (ForceDeployment[] memory forceDeployments) { - FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( - _fixedForceDeploymentsData, - (FixedForceDeploymentsData) - ); - ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( - _additionalForceDeploymentsData, - (ZKChainSpecificForceDeploymentsData) - ); - - forceDeployments = new ForceDeployment[](4); - - forceDeployments[0] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.messageRootBytecodeHash, - newAddress: address(L2_MESSAGE_ROOT), - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode(address(L2_BRIDGE_HUB)) - }); - - forceDeployments[1] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.bridgehubBytecodeHash, - newAddress: address(L2_BRIDGE_HUB), - callConstructor: true, - value: 0, - input: abi.encode( - fixedForceDeploymentsData.l1ChainId, - fixedForceDeploymentsData.aliasedL1Governance, - fixedForceDeploymentsData.maxNumberOfZKChains - ) - }); - - forceDeployments[2] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.l2AssetRouterBytecodeHash, - newAddress: address(L2_ASSET_ROUTER), - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode( - fixedForceDeploymentsData.l1ChainId, - fixedForceDeploymentsData.eraChainId, - fixedForceDeploymentsData.l1AssetRouter, - additionalForceDeploymentsData.l2LegacySharedBridge, - additionalForceDeploymentsData.baseTokenAssetId, - fixedForceDeploymentsData.aliasedL1Governance - ) - }); - - forceDeployments[3] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.l2NtvBytecodeHash, - newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode( - fixedForceDeploymentsData.l1ChainId, - fixedForceDeploymentsData.aliasedL1Governance, - fixedForceDeploymentsData.l2TokenProxyBytecodeHash, - additionalForceDeploymentsData.l2LegacySharedBridge, - address(0), // this is used if the contract were already deployed, so for the migration of Era. - false, - additionalForceDeploymentsData.l2Weth, - additionalForceDeploymentsData.baseTokenAssetId - ) - }); - } } diff --git a/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol b/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol index 88566d5d8..8752202a5 100644 --- a/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol +++ b/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol @@ -8,6 +8,7 @@ struct ZKChainSpecificForceDeploymentsData { address l2Weth; } +// solhint-disable-next-line gas-struct-packing struct FixedForceDeploymentsData { uint256 l1ChainId; uint256 eraChainId; @@ -19,6 +20,10 @@ struct FixedForceDeploymentsData { bytes32 l2AssetRouterBytecodeHash; bytes32 l2NtvBytecodeHash; bytes32 messageRootBytecodeHash; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; } interface IL2GenesisUpgrade { diff --git a/system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol b/system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol new file mode 100644 index 000000000..05d86757e --- /dev/null +++ b/system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +interface IL2SharedBridgeLegacy { + event FinalizeDeposit( + address indexed l1Sender, + address indexed l2Receiver, + address indexed l2Token, + uint256 amount + ); + + function l2TokenBeacon() external returns (UpgradeableBeacon); + + function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; + + function l1TokenAddress(address _l2Token) external view returns (address); + + function l2TokenAddress(address _l1Token) external view returns (address); + + function l1Bridge() external view returns (address); + + function l1SharedBridge() external view returns (address); + + function deployBeaconProxy(bytes32 _salt) external returns (address); + + function sendMessageToL1(bytes calldata _message) external; +} diff --git a/system-contracts/contracts/libraries/SystemContractHelper.sol b/system-contracts/contracts/libraries/SystemContractHelper.sol index 77407b2cd..d3d5e7536 100644 --- a/system-contracts/contracts/libraries/SystemContractHelper.sol +++ b/system-contracts/contracts/libraries/SystemContractHelper.sol @@ -401,4 +401,19 @@ library SystemContractHelper { returndatacopy(add(returndata, 0x20), 0, rtSize) } } + + /// @notice Performs a `mimicCall` to an address, while ensuring that the call + /// was successful + /// @param _to The address to call. + /// @param _whoToMimic The address to mimic. + /// @param _data The data to pass to the call. + function mimicCallWithPropagatedRevert(address _to, address _whoToMimic, bytes memory _data) internal { + (bool success, bytes memory returnData) = mimicCall(_to, _whoToMimic, _data); + if (!success) { + // Propagate revert reason + assembly { + revert(add(returnData, 0x20), returndatasize()) + } + } + } } diff --git a/system-contracts/package.json b/system-contracts/package.json index ab9f8d60e..b1010c7ab 100644 --- a/system-contracts/package.json +++ b/system-contracts/package.json @@ -15,7 +15,9 @@ "fast-glob": "^3.3.2", "hardhat": "=2.22.2", "preprocess": "^3.2.0", - "zksync-ethers": "^5.9.0" + "zksync-ethers": "^5.9.0", + "@openzeppelin/contracts-upgradeable-v4": "npm:@openzeppelin/contracts-upgradeable@4.9.5", + "@openzeppelin/contracts-v4": "npm:@openzeppelin/contracts@4.9.5" }, "devDependencies": { "@matterlabs/hardhat-zksync-chai-matchers": "^0.2.0", diff --git a/system-contracts/test/L2GenesisUpgrade.spec.ts b/system-contracts/test/L2GenesisUpgrade.spec.ts index 916807c99..a7914f705 100644 --- a/system-contracts/test/L2GenesisUpgrade.spec.ts +++ b/system-contracts/test/L2GenesisUpgrade.spec.ts @@ -90,7 +90,7 @@ describe("L2GenesisUpgrade tests", function () { fixedForceDeploymentsData = ethers.utils.defaultAbiCoder.encode( [ - "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash)", + "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash, address l2SharedBridgeLegacyImpl, address l2BridgedStandardERC20Impl, address l2BridgeProxyOwnerAddress, address l2BridgedStandardERC20ProxyOwnerAddress)", ], [ { @@ -104,6 +104,11 @@ describe("L2GenesisUpgrade tests", function () { l2AssetRouterBytecodeHash: l2AssetRouterBytecodeHash, l2NtvBytecodeHash: ntvBytecodeHash, messageRootBytecodeHash: messageRootBytecodeHash, + // For genesis upgrade these values will always be zero + l2SharedBridgeLegacyImpl: ethers.constants.AddressZero, + l2BridgedStandardERC20Impl: ethers.constants.AddressZero, + l2BridgeProxyOwnerAddress: ethers.constants.AddressZero, + l2BridgedStandardERC20ProxyOwnerAddress: ethers.constants.AddressZero, }, ] ); From ec73c3f8e830829176e954b09845013b56b012c3 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 2 Oct 2024 17:10:28 +0400 Subject: [PATCH 30/42] (fix): formatting --- l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 78c5ea82c..71dfb3d43 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -623,7 +623,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { ) external view returns (bool) { return L1_NULLIFIER.isWithdrawalFinalized(_chainId, _l2BatchNumber, _l2MessageIndex); } - + /// @notice Legacy function to get the L2 shared bridge address for a chain. /// @dev In case the chain has been deployed after the gateway release, /// the returned value is 0. From 885605eb4c048a26176a22afafad7dd514bc8800 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 2 Oct 2024 17:13:09 +0400 Subject: [PATCH 31/42] (test) --- l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 71dfb3d43..4ac4040c5 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -621,6 +621,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { uint256 _l2BatchNumber, uint256 _l2MessageIndex ) external view returns (bool) { + return L1_NULLIFIER.isWithdrawalFinalized(_chainId, _l2BatchNumber, _l2MessageIndex); } From d79b32b4b0cdad3312527d01ef515a116dea7b10 Mon Sep 17 00:00:00 2001 From: Raid Ateir Date: Wed, 2 Oct 2024 17:52:27 +0400 Subject: [PATCH 32/42] (fix): typo --- l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index cad270607..37f15cecd 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -386,7 +386,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { if (assetId != bytes32(0)) { return assetId; } - assetId = DataEncoding.encodeNTVAssetId(block.chainid, _token);======= + assetId = DataEncoding.encodeNTVAssetId(block.chainid, _token); if (nativeTokenVault.tokenAddress(assetId) == address(0)) { nativeTokenVault.registerToken(_token); } @@ -621,7 +621,6 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { uint256 _l2BatchNumber, uint256 _l2MessageIndex ) external view returns (bool) { - return L1_NULLIFIER.isWithdrawalFinalized(_chainId, _l2BatchNumber, _l2MessageIndex); } From 37dc6cd76b5fc9d338fea89d4d01eb223e53ecdf Mon Sep 17 00:00:00 2001 From: kelemeno Date: Wed, 2 Oct 2024 18:54:01 +0100 Subject: [PATCH 33/42] missing broadcast --- l1-contracts/deploy-scripts/GatewayPreparation.s.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index cf6220d5b..212b96055 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -347,6 +347,7 @@ contract GatewayPreparation is Script { { IBridgehub bridgehub = IBridgehub(config.bridgehub); bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); + vm.broadcast(); l1Nullifier.finalizeDeposit( FinalizeL1DepositParams({ chainId: gatewayChainId, From 340d50636160dad82878af3bfb73fb6ea24aec07 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 3 Oct 2024 19:05:52 +0100 Subject: [PATCH 34/42] contract ad vm.broadcast --- l1-contracts/deploy-scripts/DeployL1.s.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 3916cd7f3..8a10c0017 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -388,8 +388,11 @@ contract DeployL1Script is Script, DeployUtils { } function deployBridgedTokenBeacon() internal { + /// Note we cannot use create2 as the deployer is the owner. + vm.broadcast(); UpgradeableBeacon beacon = new UpgradeableBeacon(addresses.bridges.bridgedStandardERC20Implementation); address contractAddress = address(beacon); + vm.broadcast(); beacon.transferOwnership(config.ownerAddress); console.log("BridgedTokenBeacon deployed at:", contractAddress); addresses.bridges.bridgedTokenBeacon = contractAddress; From 8e7af17e17bfd8cde15765b1d9b8f793e7360fca Mon Sep 17 00:00:00 2001 From: kelemeno Date: Thu, 3 Oct 2024 19:52:59 +0100 Subject: [PATCH 35/42] zks fmt --- .../bridge/asset-router/L1AssetRouter.sol | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 37f15cecd..a66394810 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -340,24 +340,24 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { uint256 _chainId, address _depositSender, bytes32 _assetId, - bytes memory _assetData, + bytes calldata _assetData, bytes32 _l2TxHash, uint256 _l2BatchNumber, uint256 _l2MessageIndex, uint16 _l2TxNumberInBatch, bytes32[] calldata _merkleProof ) external { - L1_NULLIFIER.bridgeRecoverFailedTransfer( - _chainId, - _depositSender, - _assetId, - _assetData, - _l2TxHash, - _l2BatchNumber, - _l2MessageIndex, - _l2TxNumberInBatch, - _merkleProof - ); + L1_NULLIFIER.bridgeRecoverFailedTransfer({ + _chainId: _chainId, + _depositSender: _depositSender, + _assetId: _assetId, + _assetData: _assetData, + _l2TxHash: _l2TxHash, + _l2BatchNumber: _l2BatchNumber, + _l2MessageIndex: _l2MessageIndex, + _l2TxNumberInBatch: _l2TxNumberInBatch, + _merkleProof: _merkleProof + }); } /*////////////////////////////////////////////////////////////// From 5d60926e3e23bc1d86577737ceb0bc11e69a6de1 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 4 Oct 2024 11:17:00 +0100 Subject: [PATCH 36/42] hopefully final cleanup --- l1-contracts/contracts/bridge/L1Nullifier.sol | 3 +- .../bridge/asset-router/L2AssetRouter.sol | 5 +-- .../test/L2NativeTokenVaultDev.sol | 2 ++ .../deploy-scripts/GatewayPreparation.s.sol | 34 ++++++++----------- .../_L1SharedBridge_Shared.t.sol | 5 --- .../contracts/L2GatewayUpgrade.sol | 4 +-- .../contracts/L2GenesisUpgrade.sol | 4 +-- ...eHelper.sol => L2GenesisUpgradeHelper.sol} | 4 +-- 8 files changed, 24 insertions(+), 37 deletions(-) rename system-contracts/contracts/{L2GatewayUpgradeHelper.sol => L2GenesisUpgradeHelper.sol} (98%) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 82347dcf2..4ae111d7f 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -16,7 +16,6 @@ import {IL1NativeTokenVault} from "./ntv/IL1NativeTokenVault.sol"; import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol"; import {IL1AssetRouter} from "./asset-router/IL1AssetRouter.sol"; import {IAssetRouterBase} from "./asset-router/IAssetRouterBase.sol"; -import {INativeTokenVault} from "./ntv/INativeTokenVault.sol"; import {IL1Nullifier, FinalizeL1DepositParams} from "./interfaces/IL1Nullifier.sol"; @@ -650,7 +649,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, ) external override { bytes32 assetId = l1NativeTokenVault.assetId(_l1Token); if (assetId == bytes32(0)) { - assetId = INativeTokenVault(address(l1NativeTokenVault)).calculateAssetId(block.chainid, _l1Token); + assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Token); } // For legacy deposits, the l2 receiver is not required to check tx data hash // bytes memory transferData = abi.encode(_amount, _depositSender); diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index b2919d5f8..bd07667bd 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -130,13 +130,10 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { Internal & Helpers //////////////////////////////////////////////////////////////*/ - // kl todo add handle Legaacy data here, which calls esureTokenRegisteredWithNTV - // have handleLegacyData called from somewhere. - /// @inheritdoc AssetRouterBase function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); - assetId = nativeTokenVault.calculateAssetId(block.chainid, _token); + assetId = DataEncoding.encodeNTVAssetId(block.chainid, _token); if (nativeTokenVault.tokenAddress(assetId) == address(0)) { nativeTokenVault.registerToken(_token); } diff --git a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol index 7d4c01577..fc1991503 100644 --- a/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol +++ b/l1-contracts/contracts/dev-contracts/test/L2NativeTokenVaultDev.sol @@ -12,6 +12,8 @@ import {NativeTokenVault} from "contracts/bridge/ntv/NativeTokenVault.sol"; import {L2NativeTokenVault} from "contracts/bridge/ntv/L2NativeTokenVault.sol"; import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; +/// @author Matter Labs +/// @notice This is used for fast debugging of the L2NTV by running it in L1 context, i.e. normal foundry instead of foundry --zksync. contract L2NativeTokenVaultDev is L2NativeTokenVault { constructor( uint256 _l1ChainId, diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol index 212b96055..1c68dd82d 100644 --- a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -312,7 +312,6 @@ contract GatewayPreparation is Script { bytes32 ctmAssetId = bridgehub.ctmAssetIdFromChainId(chainId); L2AssetRouter l2AssetRouter = L2AssetRouter(L2_ASSET_ROUTER_ADDR); bytes memory l2Calldata = abi.encodeCall(IL2AssetRouter.withdraw, (ctmAssetId, bridgehubBurnData)); - // vm.startBroadcast(); bytes32 l2TxHash = Utils.runAdminL1L2DirectTransaction( _getL1GasPrice(), chainAdmin, @@ -340,26 +339,21 @@ contract GatewayPreparation is Script { ) public { initializeConfig(); - // TODO(EVM-746): Use L2-based chain admin contract - // address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); - L1Nullifier l1Nullifier = L1Nullifier(config.l1NullifierProxy); - { - IBridgehub bridgehub = IBridgehub(config.bridgehub); - bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); - vm.broadcast(); - l1Nullifier.finalizeDeposit( - FinalizeL1DepositParams({ - chainId: gatewayChainId, - l2BatchNumber: l2BatchNumber, - l2MessageIndex: l2MessageIndex, - l2Sender: L2_ASSET_ROUTER_ADDR, - l2TxNumberInBatch: l2TxNumberInBatch, - message: message, - merkleProof: merkleProof - }) - ); - } + IBridgehub bridgehub = IBridgehub(config.bridgehub); + bytes32 assetId = bridgehub.ctmAssetIdFromChainId(migratingChainId); + vm.broadcast(); + l1Nullifier.finalizeDeposit( + FinalizeL1DepositParams({ + chainId: gatewayChainId, + l2BatchNumber: l2BatchNumber, + l2MessageIndex: l2MessageIndex, + l2Sender: L2_ASSET_ROUTER_ADDR, + l2TxNumberInBatch: l2TxNumberInBatch, + message: message, + merkleProof: merkleProof + }) + ); } /// @dev Calling this function requires private key to the admin of the chain diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol index abbf05a8c..de655e7ee 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/_L1SharedBridge_Shared.t.sol @@ -208,11 +208,6 @@ contract L1AssetRouterTest is Test { abi.encodeWithSelector(IBridgehub.requestL2TransactionDirect.selector), abi.encode(txHash) ); - // vm.mockCall( - // address(bridgehubAddress), - // abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector, address(token)), - // abi.encode(nativeTokenVault.calculateAssetId(address(token))) - // ); token.mint(address(nativeTokenVault), amount); diff --git a/system-contracts/contracts/L2GatewayUpgrade.sol b/system-contracts/contracts/L2GatewayUpgrade.sol index 59d96102d..bef6009ae 100644 --- a/system-contracts/contracts/L2GatewayUpgrade.sol +++ b/system-contracts/contracts/L2GatewayUpgrade.sol @@ -7,7 +7,7 @@ import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; -import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol"; +import {L2GenesisUpgradeHelper} from "./L2GenesisUpgradeHelper.sol"; import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; @@ -32,7 +32,7 @@ contract L2GatewayUpgrade { IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(_forceDeployments); // Secondly, we perform the more complex deployment of the gateway contracts. - L2GatewayUpgradeHelper.performGatewayContractsInit( + L2GenesisUpgradeHelper.performForceDeployedContractsInit( _ctmDeployer, _fixedForceDeploymentsData, _additionalForceDeploymentsData diff --git a/system-contracts/contracts/L2GenesisUpgrade.sol b/system-contracts/contracts/L2GenesisUpgrade.sol index 4d5592529..585ff14f1 100644 --- a/system-contracts/contracts/L2GenesisUpgrade.sol +++ b/system-contracts/contracts/L2GenesisUpgrade.sol @@ -6,7 +6,7 @@ import {SYSTEM_CONTEXT_CONTRACT} from "./Constants.sol"; import {ISystemContext} from "./interfaces/ISystemContext.sol"; import {IL2GenesisUpgrade} from "./interfaces/IL2GenesisUpgrade.sol"; -import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol"; +import {L2GenesisUpgradeHelper} from "./L2GenesisUpgradeHelper.sol"; /// @custom:security-contact security@matterlabs.dev /// @author Matter Labs @@ -22,7 +22,7 @@ contract L2GenesisUpgrade is IL2GenesisUpgrade { require(_chainId != 0, "Invalid chainId"); ISystemContext(SYSTEM_CONTEXT_CONTRACT).setChainId(_chainId); - L2GatewayUpgradeHelper.performGatewayContractsInit( + L2GenesisUpgradeHelper.performForceDeployedContractsInit( _ctmDeployer, _fixedForceDeploymentsData, _additionalForceDeploymentsData diff --git a/system-contracts/contracts/L2GatewayUpgradeHelper.sol b/system-contracts/contracts/L2GenesisUpgradeHelper.sol similarity index 98% rename from system-contracts/contracts/L2GatewayUpgradeHelper.sol rename to system-contracts/contracts/L2GenesisUpgradeHelper.sol index e5004bc8a..30752c948 100644 --- a/system-contracts/contracts/L2GatewayUpgradeHelper.sol +++ b/system-contracts/contracts/L2GenesisUpgradeHelper.sol @@ -7,8 +7,8 @@ import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; -library L2GatewayUpgradeHelper { - function performGatewayContractsInit( +library L2GenesisUpgradeHelper { + function performForceDeployedContractsInit( address _ctmDeployer, bytes calldata _fixedForceDeploymentsData, bytes calldata _additionalForceDeploymentsData From 4c9b96cdf848b72e45fecfd09a49b2bead1782a1 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 4 Oct 2024 11:32:57 +0100 Subject: [PATCH 37/42] sys hashes --- system-contracts/SystemContractsHashes.json | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 3f351ca43..163458f1f 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -87,22 +87,22 @@ "contractName": "L2GatewayUpgrade", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GatewayUpgrade.sol/L2GatewayUpgrade.json", "sourceCodePath": "contracts-preprocessed/L2GatewayUpgrade.sol", - "bytecodeHash": "0x0100019d6f999ca8393f86be0b24f494bf647cc3ec56e01eed6aa233c6a0e6c0", - "sourceCodeHash": "0x3b85c44fc4fdc422c43d8bf8a2b8229665698996a034bf37c895e963f6c839bf" - }, - { - "contractName": "L2GatewayUpgradeHelper", - "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GatewayUpgradeHelper.sol/L2GatewayUpgradeHelper.json", - "sourceCodePath": "contracts-preprocessed/L2GatewayUpgradeHelper.sol", - "bytecodeHash": "0x01000007a010cd0e6cd1a9fcb802ccc92eb7a1acfb6566864ff6429a2c0ddf0a", - "sourceCodeHash": "0xe7bdc91f70cd5c395435423c25dd9cd27a99e19e12965854106cdc1385bcf14d" + "bytecodeHash": "0x0100019db8d039fb6ef02a7aea5eb8cbfd917afdf3dc88659f28e18b6a43e5df", + "sourceCodeHash": "0xc69d0c1819a366fc91a43a9ad2d747540a7b8ee2071b48e4adfd301ec40e8600" }, { "contractName": "L2GenesisUpgrade", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GenesisUpgrade.sol/L2GenesisUpgrade.json", "sourceCodePath": "contracts-preprocessed/L2GenesisUpgrade.sol", - "bytecodeHash": "0x010000fb0d40ab01a12ce7bad289cb44ab49563e51e6e1d889d431492a4b320f", - "sourceCodeHash": "0xdd7a89c11b624282aed07ef9d985d2fd006015a0aa0b11aec8e445695f7dd689" + "bytecodeHash": "0x010000fbf45473bbd4c0ef708beb28c5f40c10b22b0a289feb4ccbce9b7ae825", + "sourceCodeHash": "0xe21a58d96b2727020958e95605764b84f7f2f3cc4e0c01c4cdf2854f882d41ca" + }, + { + "contractName": "L2GenesisUpgradeHelper", + "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GenesisUpgradeHelper.sol/L2GenesisUpgradeHelper.json", + "sourceCodePath": "contracts-preprocessed/L2GenesisUpgradeHelper.sol", + "bytecodeHash": "0x01000007e61dc52f7aff33c2c209b51ab7fbaf9b4668980f076fdabb24934f97", + "sourceCodeHash": "0xc2fd787a23e7935f8109f3eb35b71853fe3a9acf5cd96138afcb0e5b65002ba3" }, { "contractName": "MsgValueSimulator", From fa030cde37d24a07f00eaa9757b40e7fdc0d6472 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 4 Oct 2024 17:53:53 +0100 Subject: [PATCH 38/42] stas issue fixes --- l1-contracts/contracts/bridge/L1Nullifier.sol | 1 + .../contracts/bridge/L2WrappedBaseToken.sol | 10 ++++--- .../bridge/asset-router/L1AssetRouter.sol | 5 +--- .../bridge/asset-router/L2AssetRouter.sol | 10 ++++--- .../bridge/ntv/INativeTokenVault.sol | 5 ++-- .../bridge/ntv/L1NativeTokenVault.sol | 26 ++++++++++++++++--- .../contracts/bridge/ntv/NativeTokenVault.sol | 23 ++++++++++------ .../contracts/common/L1ContractErrors.sol | 5 ++++ 8 files changed, 61 insertions(+), 24 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 4ae111d7f..18455ed79 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -603,6 +603,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, // slither-disable-next-line unused-return (amount, ) = UnsafeBytes.readUint256(_l2ToL1message, offset); + l1NativeTokenVault.ensureTokenIsRegistered(l1Token); assetId = DataEncoding.encodeNTVAssetId(block.chainid, l1Token); transferData = DataEncoding.encodeBridgeMintData({ _originalCaller: address(0), diff --git a/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol b/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol index 7af7b6cd3..1c430cacd 100644 --- a/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol +++ b/l1-contracts/contracts/bridge/L2WrappedBaseToken.sol @@ -33,8 +33,8 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri /// @dev Address of the native token vault. address public override nativeTokenVault; - /// @dev The assetId of the token. The wrapped token does not have its own assetId. - bytes32 public override assetId; + /// @dev The assetId of the base token. The wrapped token does not have its own assetId. + bytes32 public baseTokenAssetId; modifier onlyBridge() { if (msg.sender != l2Bridge) { @@ -82,7 +82,7 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri l2Bridge = _l2Bridge; l1Address = _l1Address; nativeTokenVault = L2_NATIVE_TOKEN_VAULT_ADDR; - assetId = _baseTokenAssetId; + baseTokenAssetId = _baseTokenAssetId; // Set decoded values for name and symbol. __ERC20_init_unchained(name_, symbol_); @@ -144,4 +144,8 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IBri function originToken() external view override returns (address) { return l1Address; } + + function assetId() external view override returns (bytes32) { + return baseTokenAssetId; + } } diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index a66394810..ea32ec863 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -386,10 +386,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { if (assetId != bytes32(0)) { return assetId; } - assetId = DataEncoding.encodeNTVAssetId(block.chainid, _token); - if (nativeTokenVault.tokenAddress(assetId) == address(0)) { - nativeTokenVault.registerToken(_token); - } + nativeTokenVault.ensureTokenIsRegistered(_token); } /// @inheritdoc IL1AssetRouter diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index bd07667bd..8fbd8097e 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -133,10 +133,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { /// @inheritdoc AssetRouterBase function _ensureTokenRegisteredWithNTV(address _token) internal override returns (bytes32 assetId) { IL2NativeTokenVault nativeTokenVault = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR); - assetId = DataEncoding.encodeNTVAssetId(block.chainid, _token); - if (nativeTokenVault.tokenAddress(assetId) == address(0)) { - nativeTokenVault.registerToken(_token); - } + nativeTokenVault.ensureTokenIsRegistered(_token); } /*////////////////////////////////////////////////////////////// @@ -153,6 +150,11 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { } function withdrawToken(address _l2NativeToken, bytes memory _assetData) public returns (bytes32) { + bytes32 recordedAssetId = INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).assetId(_l2NativeToken); + bytes32 recordedOriginChainId = INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).originChainId(recordedAssetId); + if (recordedOriginChainId == L1_CHAIN_ID) { + revert AssetIdNotSupported(recordedAssetId); + } bytes32 assetId = _ensureTokenRegisteredWithNTV(_l2NativeToken); return _withdrawSender(assetId, _assetData, msg.sender, true); } diff --git a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol index 05b641c43..12718bd6f 100644 --- a/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/INativeTokenVault.sol @@ -29,8 +29,9 @@ interface INativeTokenVault { /// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration. function registerToken(address _l1Token) external; - /// @notice Used to calculate the assetId of a token - function calculateAssetId(uint256 _chainId, address _tokenAddress) external view returns (bytes32); + /// @notice Ensures that the native token is registered with the NTV. + /// @dev This function is used to ensure that the token is registered with the NTV. + function ensureTokenIsRegistered(address _nativeToken) external; /// @notice Used to get the the ERC20 data for a token function getERC20Getters(address _token, uint256 _originChainId) external view returns (bytes memory); diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index 65b2e8e39..811530a25 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -96,6 +96,7 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken /// @dev Calling second time for the same token will revert. /// @param _token The address of token to be transferred (address(1) for ether and contract address for ERC20). function transferFundsFromSharedBridge(address _token) external { + ensureTokenIsRegistered(_token); if (_token == ETH_TOKEN_ADDRESS) { uint256 balanceBefore = address(this).balance; L1_NULLIFIER.transferTokenToNTV(_token); @@ -136,6 +137,24 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken require(_assetHandlerAddressOnCounterpart == L2_NATIVE_TOKEN_VAULT_ADDR, "NTV: wrong counterpart"); } + function getOriginChainId(bytes32 _assetId) external view override returns (uint256) { + uint256 chainId = originChainId[_assetId]; + if (chainId != 0) { + return chainId; + } else { + address token = tokenAddress[_assetId]; + if (token == ETH_TOKEN_ADDRESS) { + return block.chainid; + } else if (IERC20(token).balanceOf(address(this)) > 0) { + return block.chainid; + } else if (IERC20(token).balanceOf(L1_NULLIFIER) > 0) { + return block.chainid; + } else { + return 0; + } + } + } + /*////////////////////////////////////////////////////////////// Start transaction Functions //////////////////////////////////////////////////////////////*/ @@ -191,10 +210,11 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken } require(callSuccess, "NTV: claimFailedDeposit failed, no funds or cannot transfer to receiver"); } else { - if (originChainId[_assetId] != block.chainid) { - IBridgedStandardToken(l1Token).bridgeMint(_depositSender, _amount); - } else { + uint256 originChainId = getOriginChainId(_assetId); + if (originChainId == block.chainid) { IERC20(l1Token).safeTransfer(_depositSender, _amount); + } else if (originChainId != 0) { + IBridgedStandardToken(l1Token).bridgeMint(_depositSender, _amount); } // Note we don't allow weth deposits anymore, but there might be legacy weth deposits. // until we add Weth bridging capabilities, we don't wrap/unwrap weth to ether. diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 644669f18..47cbafb7a 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -21,7 +21,7 @@ import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; import {BridgedStandardERC20} from "../BridgedStandardERC20.sol"; import {BridgeHelper} from "../BridgeHelper.sol"; -import {EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress} from "../../common/L1ContractErrors.sol"; +import {EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress, L1TokenDeploymentWithZeroChainId, } from "../../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -95,6 +95,13 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _unsafeRegisterNativeToken(_nativeToken); } + /// @inheritdoc INativeTokenVault + function ensureTokenIsRegistered(address _nativeToken) public { + if (assetId[_nativeToken] == bytes32(0)) { + _registerToken(_nativeToken); + } + } + /*////////////////////////////////////////////////////////////// FINISH TRANSACTION FUNCTIONS //////////////////////////////////////////////////////////////*/ @@ -322,13 +329,6 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 return BridgeHelper.getERC20Getters(_token, _originChainId); } - /// @notice Returns the parsed assetId. - /// @param _nativeToken The address of the token to be parsed. - /// @dev Shows the assetId for a given chain and token address - function calculateAssetId(uint256 _chainId, address _nativeToken) external pure override returns (bytes32) { - return DataEncoding.encodeNTVAssetId(_chainId, _nativeToken); - } - /// @notice Registers a native token address for the vault. /// @dev It does not perform any checks for the correctnesss of the token contract. /// @param _nativeToken The address of the token to be registered. @@ -429,7 +429,14 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _originToken, _erc20Data ); + // an extre check for legacy tokens on L1, they might not be registered i.e. + if (block.chainid == L1_CHAIN_ID && tokenOriginChainId == 0) { + revert L1TokenDeploymentWithZeroChainId(_assetId); + } tokenOriginChainId = tokenOriginChainId == 0 ? L1_CHAIN_ID : tokenOriginChainId; + if (tokenOriginChainId == block.chainid) { + revert DeployingBridgedTokenForNativeToken(); + } originChainId[DataEncoding.encodeNTVAssetId(tokenOriginChainId, _originToken)] = tokenOriginChainId; return address(l2Token); } diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 00812e606..2325d53f9 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -89,6 +89,8 @@ error DelegateCallFailed(bytes returnData); error DenominatorIsZero(); // error DeployFailed(); +// +error DeployingBridgedTokenForNativeToken(); // 0xc7c9660f error DepositDoesNotExist(); // 0xad2fa98e @@ -119,6 +121,7 @@ error FailedToTransferTokens(address tokenContract, address to, uint256 amount); error FacetExists(bytes4 selector, address); // 0x79e12cc3 error FacetIsFrozen(bytes4 func); +/// error FunctionNotSupported(); // 0xc91cf3b1 error GasPerPubdataMismatch(); @@ -180,6 +183,8 @@ error InvalidTxType(uint256 txType); error InvalidUpgradeTxn(UpgradeTxVerifyParam); // 0xaa7feadc error InvalidValue(); +// 0x00000000 +error L1TokenDeploymentWithZeroChainId(bytes32 assetId); // 0xa4f62e33 error L2BridgeNotDeployed(uint256 chainId); // 0xff8811ff From 6afd80175245ef6a7cbe02426f646ac5270fdace Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 4 Oct 2024 18:18:14 +0100 Subject: [PATCH 39/42] compilation and test --- l1-contracts/contracts/bridge/L1Nullifier.sol | 4 ++-- .../contracts/bridge/asset-router/L1AssetRouter.sol | 1 + .../contracts/bridge/asset-router/L2AssetRouter.sol | 3 ++- l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol | 6 +++--- l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 18455ed79..6e624d723 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -510,7 +510,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, /// @return transferData The transfer data used to finalize withdawal. function _verifyWithdrawal( FinalizeL1DepositParams calldata _finalizeWithdrawalParams - ) internal view returns (bytes32 assetId, bytes memory transferData) { + ) internal returns (bytes32 assetId, bytes memory transferData) { (assetId, transferData) = _parseL2WithdrawalMessage( _finalizeWithdrawalParams.chainId, _finalizeWithdrawalParams.message @@ -559,7 +559,7 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, function _parseL2WithdrawalMessage( uint256 _chainId, bytes memory _l2ToL1message - ) internal view returns (bytes32 assetId, bytes memory transferData) { + ) internal returns (bytes32 assetId, bytes memory transferData) { // Please note that there are three versions of the message: // 1. The message that is sent from `L2BaseToken` to withdraw base token. // 2. The message that is sent from L2 Legacy Shared Bridge to withdraw ERC20 tokens or base token. diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index ea32ec863..ff94d0db5 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -387,6 +387,7 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { return assetId; } nativeTokenVault.ensureTokenIsRegistered(_token); + assetId = nativeTokenVault.assetId(_token); } /// @inheritdoc IL1AssetRouter diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index 8fbd8097e..0a10822f4 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -7,6 +7,7 @@ import {IAssetRouterBase} from "./IAssetRouterBase.sol"; import {AssetRouterBase} from "./AssetRouterBase.sol"; import {IL2NativeTokenVault} from "../ntv/IL2NativeTokenVault.sol"; +import {INativeTokenVault} from "../ntv/INativeTokenVault.sol"; import {IL2SharedBridgeLegacy} from "../interfaces/IL2SharedBridgeLegacy.sol"; import {IAssetHandler} from "../interfaces/IAssetHandler.sol"; import {IBridgedStandardToken} from "../interfaces/IBridgedStandardToken.sol"; @@ -151,7 +152,7 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { function withdrawToken(address _l2NativeToken, bytes memory _assetData) public returns (bytes32) { bytes32 recordedAssetId = INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).assetId(_l2NativeToken); - bytes32 recordedOriginChainId = INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).originChainId(recordedAssetId); + uint256 recordedOriginChainId = INativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).originChainId(recordedAssetId); if (recordedOriginChainId == L1_CHAIN_ID) { revert AssetIdNotSupported(recordedAssetId); } diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index 811530a25..85c06c351 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -137,7 +137,7 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken require(_assetHandlerAddressOnCounterpart == L2_NATIVE_TOKEN_VAULT_ADDR, "NTV: wrong counterpart"); } - function getOriginChainId(bytes32 _assetId) external view override returns (uint256) { + function _getOriginChainId(bytes32 _assetId) internal view returns (uint256) { uint256 chainId = originChainId[_assetId]; if (chainId != 0) { return chainId; @@ -147,7 +147,7 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken return block.chainid; } else if (IERC20(token).balanceOf(address(this)) > 0) { return block.chainid; - } else if (IERC20(token).balanceOf(L1_NULLIFIER) > 0) { + } else if (IERC20(token).balanceOf(address(L1_NULLIFIER)) > 0) { return block.chainid; } else { return 0; @@ -210,7 +210,7 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken } require(callSuccess, "NTV: claimFailedDeposit failed, no funds or cannot transfer to receiver"); } else { - uint256 originChainId = getOriginChainId(_assetId); + uint256 originChainId = _getOriginChainId(_assetId); if (originChainId == block.chainid) { IERC20(l1Token).safeTransfer(_depositSender, _amount); } else if (originChainId != 0) { diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 47cbafb7a..5996e821a 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -21,7 +21,7 @@ import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; import {BridgedStandardERC20} from "../BridgedStandardERC20.sol"; import {BridgeHelper} from "../BridgeHelper.sol"; -import {EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress, L1TokenDeploymentWithZeroChainId, } from "../../common/L1ContractErrors.sol"; +import {EmptyDeposit, Unauthorized, TokensWithFeesNotSupported, TokenNotSupported, NonEmptyMsgValue, ValueMismatch, AddressMismatch, AssetIdMismatch, AmountMustBeGreaterThanZero, ZeroAddress, L1TokenDeploymentWithZeroChainId, DeployingBridgedTokenForNativeToken} from "../../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev From 1d1e732bfa90752e9515741878d418ac8b15d846 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Fri, 4 Oct 2024 18:18:26 +0100 Subject: [PATCH 40/42] fmt --- l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 5996e821a..1f85aad36 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -429,7 +429,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _originToken, _erc20Data ); - // an extre check for legacy tokens on L1, they might not be registered i.e. + // an extre check for legacy tokens on L1, they might not be registered i.e. if (block.chainid == L1_CHAIN_ID && tokenOriginChainId == 0) { revert L1TokenDeploymentWithZeroChainId(_assetId); } From 24f6464698d0e33ca3ed7684c8d8f5494ad9c231 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 7 Oct 2024 18:31:29 +0100 Subject: [PATCH 41/42] stas issues 2 --- .../bridge/ntv/L1NativeTokenVault.sol | 4 ++- .../contracts/bridge/ntv/NativeTokenVault.sol | 2 +- .../contracts/common/L1ContractErrors.sol | 28 ++++++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index 85c06c351..d5b059ae6 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -24,7 +24,7 @@ import {ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/L2ContractAddresses.sol"; import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; -import {Unauthorized, ZeroAddress, NoFundsTransferred, InsufficientChainBalance, WithdrawFailed} from "../../common/L1ContractErrors.sol"; +import {Unauthorized, ZeroAddress, NoFundsTransferred, InsufficientChainBalance, WithdrawFailed, OriginChainIdNotFound} from "../../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -215,6 +215,8 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken IERC20(l1Token).safeTransfer(_depositSender, _amount); } else if (originChainId != 0) { IBridgedStandardToken(l1Token).bridgeMint(_depositSender, _amount); + } else { + revert OriginChainIdNotFound(); } // Note we don't allow weth deposits anymore, but there might be legacy weth deposits. // until we add Weth bridging capabilities, we don't wrap/unwrap weth to ether. diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 1f85aad36..c4d11e827 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -429,7 +429,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 _originToken, _erc20Data ); - // an extre check for legacy tokens on L1, they might not be registered i.e. + // an extra check for legacy tokens on L1, they might not be registered i.e. if (block.chainid == L1_CHAIN_ID && tokenOriginChainId == 0) { revert L1TokenDeploymentWithZeroChainId(_assetId); } diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 2325d53f9..bf35341a3 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -39,7 +39,7 @@ error AmountMustBeGreaterThanZero(); error AssetHandlerDoesNotExist(bytes32 assetId); // 0x1294e9e1 error AssetIdMismatch(bytes32 expected, bytes32 supplied); -// +// 0xfe919e28 error AssetIdAlreadyRegistered(); // 0x0bfcef28 error AlreadyWhitelisted(address); @@ -59,7 +59,7 @@ error BatchNumberMismatch(uint256 expectedBatchNumber, uint256 providedBatchNumb error BlobHashCommitmentError(uint256 index, bool blobHashEmpty, bool blobCommitmentEmpty); // 0x6cf12312 error BridgeHubAlreadyRegistered(); -// +// 0xdb538614 error BridgeMintNotImplemented(); // 0xcf102c5a error CalldataLengthTooBig(); @@ -75,9 +75,9 @@ error ChainIdAlreadyExists(); error ChainIdCantBeCurrentChain(); // 0xa179f8c9 error ChainIdMismatch(); -// +// 0x23f3c357 error ChainIdNotRegistered(uint256 chainId); -// +// 0x5de72107 error ChainNotLegacy(); // 0x78d2ed02 error ChainAlreadyLive(); @@ -87,9 +87,9 @@ error ChainIdTooBig(); error DelegateCallFailed(bytes returnData); // 0x0a8ed92c error DenominatorIsZero(); -// +// 0xb4f54111 error DeployFailed(); -// +// 0x138ee1a3 error DeployingBridgedTokenForNativeToken(); // 0xc7c9660f error DepositDoesNotExist(); @@ -103,17 +103,17 @@ error DiamondAlreadyFrozen(); error DiamondFreezeIncorrectState(); // 0xa7151b9a error DiamondNotFrozen(); -// +// 0x7138356f error EmptyAddress(); // 0x2d4d012f error EmptyAssetId(); // 0xfc7ab1d3 error EmptyBlobVersionHash(uint256 index); -// +// 0x1c25715b error EmptyBytes32(); // 0x95b66fe9 error EmptyDeposit(); -// +// 0x627e0872 error ETHDepositNotSupported(); // error FailedToTransferTokens(address tokenContract, address to, uint256 amount); @@ -147,7 +147,7 @@ error IncorrectBridgeHubAddress(address bridgehub); error InsufficientChainBalance(); // 0x356680b7 error InsufficientFunds(); -// +// 0xcbd9d2e0 error InvalidCaller(address); // 0x7a47c9a2 error InvalidChainId(); @@ -215,7 +215,7 @@ error MerkleIndexOutOfBounds(); error MerklePathEmpty(); // 0x1c500385 error MerklePathOutOfBounds(); -// +// 0x3312a450 error MigrationPaused(); // 0xfa44b527 error MissingSystemLogs(uint256 expected, uint256 actual); @@ -241,7 +241,7 @@ error NonEmptyMsgValue(); error NonIncreasingTimestamp(); // 0x0105f9c0 error NonSequentialBatch(); -// +// 0x0ac76f01 error NonSequentialVersion(); // 0x4ef79e5a error NonZeroAddress(address); @@ -259,6 +259,8 @@ error OperationExists(); error OperationMustBePending(); // 0xe1c1ff37 error OperationMustBeReady(); +// 0xb926450e +error OriginChainIdNotFound(); // 0xd7f50a9d error PatchCantSetUpgradeTxn(); // 0x962fd7d0 @@ -365,7 +367,7 @@ error UnexpectedSystemLog(uint256 logKey); error UnimplementedMessage(string); // 0xf093c2e5 error UpgradeBatchNumberIsNotZero(); -// +// 0x084a1449 error UnsupportedEncodingVersion(); // error UnsupportedPaymasterFlow(); From 28c91ad203822486d44b247142467b5391454c92 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 7 Oct 2024 18:57:03 +0100 Subject: [PATCH 42/42] error selector typo --- l1-contracts/contracts/common/L1ContractErrors.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index bf35341a3..ab1320968 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -183,7 +183,7 @@ error InvalidTxType(uint256 txType); error InvalidUpgradeTxn(UpgradeTxVerifyParam); // 0xaa7feadc error InvalidValue(); -// 0x00000000 +// 0x888b2f09 error L1TokenDeploymentWithZeroChainId(bytes32 assetId); // 0xa4f62e33 error L2BridgeNotDeployed(uint256 chainId);