Skip to content

Commit

Permalink
Merge branch 'kl/sync-layer-reorg' of ssh://github.com/matter-labs/er…
Browse files Browse the repository at this point in the history
…a-contracts into kl/interop
  • Loading branch information
kelemeno committed Oct 3, 2024
2 parents 37e2290 + c3030f5 commit 5b4eff0
Show file tree
Hide file tree
Showing 77 changed files with 3,317 additions and 577 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/*
6 changes: 6 additions & 0 deletions l1-contracts/contracts/bridge/L1Nullifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
44 changes: 43 additions & 1 deletion l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected]
/// @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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ pragma solidity ^0.8.21;
import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol";
import {INativeTokenVault} from "../ntv/INativeTokenVault.sol";
import {IAssetRouterBase} from "./IAssetRouterBase.sol";
import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol";

Check failure on line 8 in l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol

View workflow job for this annotation

GitHub Actions / lint

imported name L2TransactionRequestTwoBridgesInner is not used

Check failure on line 8 in l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol

View workflow job for this annotation

GitHub Actions / lint

imported name L2TransactionRequestTwoBridgesInner is not used

Check failure on line 8 in l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol

View workflow job for this annotation

GitHub Actions / lint

imported name L2TransactionRequestTwoBridgesInner is not used
import {IL1SharedBridgeLegacy} from "../interfaces/IL1SharedBridgeLegacy.sol";

/// @title L1 Bridge contract interface
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IL1AssetRouter is IAssetRouterBase {
interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy {
event BridgehubMintData(bytes bridgeMintData);

event BridgehubDepositFinalized(
Expand Down
8 changes: 8 additions & 0 deletions l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ interface IL2AssetRouter is IAssetRouterBase {

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;
Expand Down
7 changes: 7 additions & 0 deletions l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -491,4 +491,11 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard {
_merkleProof: _merkleProof
});
}

/// @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);
}
}
34 changes: 33 additions & 1 deletion l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,39 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter {
uint256 _amount,
bytes calldata _data
) external {
// onlyAssetRouterCounterpart(L1_CHAIN_ID) {
// 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,4 @@ pragma solidity 0.8.24;
/// @custom:security-contact [email protected]
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
);
}
11 changes: 11 additions & 0 deletions l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected]
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);
Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 [email protected]
/// @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);

Expand Down
12 changes: 12 additions & 0 deletions l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {IL1Nullifier} from "../interfaces/IL1Nullifier.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";
Expand Down Expand Up @@ -123,6 +124,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
//////////////////////////////////////////////////////////////*/
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,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
Expand Down
15 changes: 12 additions & 3 deletions l1-contracts/contracts/bridgehub/Bridgehub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,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;
}
Expand Down Expand Up @@ -366,7 +376,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
Expand Down
4 changes: 4 additions & 0 deletions l1-contracts/contracts/bridgehub/IBridgehub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,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);
}
8 changes: 6 additions & 2 deletions l1-contracts/contracts/common/L1ContractErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ error DepositDoesNotExist();
error DepositExists();
// 0x79cacff1
error DepositFailed();
// 0xae08e4af
error DepositIncorrectAmount(uint256 expectedAmt, uint256 providedAmt);
// 0x0e7ee319
error DiamondAlreadyFrozen();
// 0x682dabb4
Expand Down Expand Up @@ -421,6 +419,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,
Expand Down
6 changes: 6 additions & 0 deletions l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ contract DummyBridgehub {

address public zkChain;

address public sharedBridge;

// add this to be excluded from coverage report
function test() internal virtual {}

Expand All @@ -41,4 +43,8 @@ contract DummyBridgehub {
function getZKChain(uint256) external view returns (address) {
return address(0);
}

function setSharedBridge(address addr) external {
sharedBridge = addr;
}
}
3 changes: 3 additions & 0 deletions l1-contracts/contracts/governance/IPermanentRestriction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
42 changes: 42 additions & 0 deletions l1-contracts/contracts/governance/L2AdminFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import {ChainAdmin} from "./ChainAdmin.sol";

/// @author Matter Labs
/// @custom:security-contact [email protected]
/// @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));
}
}
Loading

0 comments on commit 5b4eff0

Please sign in to comment.