From 065c8e46bf7cee268956309385d471f5717e76ce Mon Sep 17 00:00:00 2001 From: agusduha Date: Mon, 17 Feb 2025 15:52:38 -0300 Subject: [PATCH] feat: update L2 genesis specs --- specs/experimental/standard-l2-genesis.md | 177 ++++++++++++++-------- 1 file changed, 113 insertions(+), 64 deletions(-) diff --git a/specs/experimental/standard-l2-genesis.md b/specs/experimental/standard-l2-genesis.md index 6ed220f23..b6c6d0834 100644 --- a/specs/experimental/standard-l2-genesis.md +++ b/specs/experimental/standard-l2-genesis.md @@ -12,19 +12,23 @@ - [Initialization](#initialization) - [Interface](#interface) - [Fee Vault Config](#fee-vault-config) - - [`setBaseFeeVaultConfig`](#setbasefeevaultconfig) - - [`setL1FeeVaultConfig`](#setl1feevaultconfig) - - [`setSequencerFeeVaultConfig`](#setsequencerfeevaultconfig) + - [`setFeeVaultConfig`](#setfeevaultconfig) + - [Fee Admin](#fee-admin) + - [`feeAdmin`](#feeadmin) + - [`setFeeVaultAdmin`](#setfeevaultadmin) + - [Invariants](#invariants) - [`OptimismPortal`](#optimismportal) - [Interface](#interface-1) - [`setConfig`](#setconfig) - [`upgrade`](#upgrade) + - [Invariants](#invariants-1) - [SuperchainConfig](#superchainconfig) - [Constants](#constants-1) - [Interface](#interface-2) - [Initialization](#initialization-1) - [Predeploys](#predeploys) -- [Constants](#constants-2) + - [L1Block values](#l1block-values) + - [L1Block slots](#l1block-slots) - [Predeploys](#predeploys-1) - [ProxyAdmin](#proxyadmin) - [Rationale](#rationale) @@ -38,12 +42,10 @@ - [Interface](#interface-4) - [`config`](#config) - [L2CrossDomainMessenger](#l2crossdomainmessenger) - - [Interface](#interface-5) - [L2ERC721Bridge](#l2erc721bridge) - - [Interface](#interface-6) - [L2StandardBridge](#l2standardbridge) - - [Interface](#interface-7) - [OptimismMintableERC721Factory](#optimismmintableerc721factory) + - [OptimismMintableERC20Factory](#optimismmintableerc20factory) - [Security Considerations](#security-considerations) - [GovernanceToken](#governancetoken) @@ -60,16 +62,16 @@ configurability. The `ConfigType` enum represents configuration that can be modified. -| Name | Value | Description | -| ---- | ----- | --- | -| `GAS_PAYING_TOKEN` | `uint8(0)` | Modifies the gas paying token for the chain | -| `BASE_FEE_VAULT_CONFIG` | `uint8(1)` | Sets the Fee Vault Config for the `BaseFeeVault` | -| `L1_FEE_VAULT_CONFIG` | `uint8(2)` | Sets the Fee Vault Config for the `L1FeeVault` | -| `SEQUENCER_FEE_VAULT_CONFIG` | `uint8(3)` | Sets the Fee Vault Config for the `SequencerFeeVault` | -| `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` | `uint8(4)` | Sets the `L1CrossDomainMessenger` address | -| `L1_ERC_721_BRIDGE_ADDRESS` | `uint8(5)` | Sets the `L1ERC721Bridge` address | -| `L1_STANDARD_BRIDGE_ADDRESS` | `uint8(6)` | Sets the `L1StandardBridge` address | -| `REMOTE_CHAIN_ID` | `uint8(7)` | Sets the chain id of the base chain | +| Name | Value | Description | +| ----------------------------------- | ---------- | ----------------------------------------------------- | +| `GAS_PAYING_TOKEN` | `uint8(0)` | Modifies the gas paying token for the chain | +| `BASE_FEE_VAULT_CONFIG` | `uint8(1)` | Sets the Fee Vault Config for the `BaseFeeVault` | +| `L1_FEE_VAULT_CONFIG` | `uint8(2)` | Sets the Fee Vault Config for the `L1FeeVault` | +| `SEQUENCER_FEE_VAULT_CONFIG` | `uint8(3)` | Sets the Fee Vault Config for the `SequencerFeeVault` | +| `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` | `uint8(4)` | Sets the `L1CrossDomainMessenger` address | +| `L1_ERC_721_BRIDGE_ADDRESS` | `uint8(5)` | Sets the `L1ERC721Bridge` address | +| `L1_STANDARD_BRIDGE_ADDRESS` | `uint8(6)` | Sets the `L1StandardBridge` address | +| `REMOTE_CHAIN_ID` | `uint8(7)` | Sets the chain id of the base chain | ## `SystemConfig` @@ -77,13 +79,14 @@ The `ConfigType` enum represents configuration that can be modified. The following `ConfigUpdate` event is defined where the `CONFIG_VERSION` is `uint256(0)`: -| Name | Value | Definition | Usage | -| ---- | ----- | --- | -- | -| `BATCHER` | `uint8(0)` | `abi.encode(address)` | Modifies the account that is authorized to progress the safe chain | -| `FEE_SCALARS` | `uint8(1)` | `(uint256(0x01) << 248) \| (uint256(_blobbasefeeScalar) << 32) \| _basefeeScalar` | Modifies the fee scalars | -| `GAS_LIMIT` | `uint8(2)` | `abi.encode(uint64 _gasLimit)` | Modifies the L2 gas limit | -| `UNSAFE_BLOCK_SIGNER` | `uint8(3)` | `abi.encode(address)` | Modifies the account that is authorized to progress the unsafe chain | -| `EIP_1559_PARAMS` | `uint8(4)` | `uint256(uint64(uint32(_denominator))) << 32 \| uint64(uint32(_elasticity))` | Modifies the EIP-1559 denominator and elasticity | +| Name | Value | Definition | Usage | +| --------------------- | ---------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `BATCHER` | `uint8(0)` | `abi.encode(address)` | Modifies the account that is authorized to progress the safe chain | +| `FEE_SCALARS` | `uint8(1)` | `(uint256(0x01) << 248) \| (uint256(_blobbasefeeScalar) << 32) \| _basefeeScalar` | Modifies the fee scalars | +| `GAS_LIMIT` | `uint8(2)` | `abi.encode(uint64 _gasLimit)` | Modifies the L2 gas limit | +| `UNSAFE_BLOCK_SIGNER` | `uint8(3)` | `abi.encode(address)` | Modifies the account that is authorized to progress the unsafe chain | +| `EIP_1559_PARAMS` | `uint8(4)` | `uint256(uint64(uint32(_denominator))) << 32 \| uint64(uint32(_elasticity))` | Modifies the EIP-1559 denominator and elasticity | +| `FEE_VAULT_ADMIN` | `uint8(5)` | `abi.encode(address)` | Modifies the fee vault admin | ### Initialization @@ -94,6 +97,7 @@ The following actions should happen during the initialization of the `SystemConf - `emit ConfigUpdate.GAS_LIMIT` - `emit ConfigUpdate.UNSAFE_BLOCK_SIGNER` - `emit ConfigUpdate.EIP_1559_PARAMS` +- `emit ConfigUpdate.FEE_VAULT_ADMIN` - `setConfig(SET_GAS_PAYING_TOKEN)` - `setConfig(SET_BASE_FEE_VAULT_CONFIG)` - `setConfig(SET_L1_FEE_VAULT_CONFIG)` @@ -111,28 +115,42 @@ These actions MAY only be triggered if there is a diff to the value. For each `FeeVault`, there is a setter for its config. The arguments to the setter include the `RECIPIENT`, the `MIN_WITHDRAWAL_AMOUNT` and the `WithdrawalNetwork`. -Each of these functions should be `public` and only callable by the chain governor. +This function should be `public` and only callable by the fee admin. Each function calls `OptimismPortal.setConfig(ConfigType,bytes)` with its corresponding `ConfigType`. -##### `setBaseFeeVaultConfig` +##### `setFeeVaultConfig` ```solidity -function setBaseFeeVaultConfig(address,uint256,WithdrawalNetwork) +function setFeeVaultConfig(ConfigType,address,uint256,WithdrawalNetwork) ``` -##### `setL1FeeVaultConfig` +#### Fee Admin + +A new role is introduced to call the vault config setters. This role is updated at the `initialize` function. + +##### `feeAdmin` ```solidity -function setL1FeeVaultConfig(address,uint256,WithdrawalNetwork) +function feeAdmin() returns (address) ``` -##### `setSequencerFeeVaultConfig` +##### `setFeeVaultAdmin` + +The `setFeeVaultAdmin` function MUST only be callable by the system config owner. ```solidity -function setSequencerFeeVaultConfig(address,uint256,WithdrawalNetwork) +function setFeeVaultAdmin(address) ``` +### Invariants + +- Only the fee admin MUST be able to update a fee vault config + +- Updating a fee vault config MUST emit a system deposit tx through the `OptimismPortal` + +- Only the fee admin MUST be able to update the fee vault admin + ## `OptimismPortal` The `OptimismPortal` is updated to emit a special system `TransactionDeposited` event. @@ -160,7 +178,7 @@ The following fields are included: - `to` is `Predeploys.L1Block` - `version` is `uint256(0)` - `opaqueData` is the tightly packed transaction data where `mint` is `0`, `value` is `0`, the `gasLimit` - is `200_000`, `isCreation` is `false` and the `data` is `abi.encodeCall(L1Block.setConfig, (_type, _value))` + is `200_000`, `isCreation` is `false` and the `data` is `abi.encodeCall(L1Block.setConfig, (_type, _value))` #### `upgrade` @@ -168,7 +186,7 @@ The `upgrade` function MUST only be callable by the `UPGRADER` role as defined in the [`SuperchainConfig`](#superchainconfig). ```solidity -function upgrade(bytes memory _data) external +function upgrade(uint32 _gasLimit, bytes memory _data) external ``` This function emits a `TransactionDeposited` event. @@ -180,10 +198,18 @@ event TransactionDeposited(address indexed from, address indexed to, uint256 ind The following fields are included: - `from` is the `DEPOSITOR_ACCOUNT` -- `to` is `Predeploys.ProxyAdmin` +- `to` is `Predeploys.L2_PROXY_ADMIN` - `version` is `uint256(0)` - `opaqueData` is the tightly packed transaction data where `mint` is `0`, `value` is `0`, the `gasLimit` - is `200_000`, `isCreation` is `false` and the `data` is the data passed into `upgrade`. + is `200_000`, `isCreation` is `false` and the `data` is the data passed into `upgrade`. + +### Invariants + +- Only the `SystemConfig` MUST be able to call `setConfig` + +- Only the `UPGRADER` role MUST be able to call `upgrade` + +- `setConfig` and `upgrade` MUST emit a system deposit tx through `TransactionDeposited` event ## SuperchainConfig @@ -193,8 +219,8 @@ that call the L2 `ProxyAdmin`. ### Constants -| Name | Value | Definition | -| --------- | ------------------------- | -- | +| Name | Value | Definition | +| --------------- | -------------------------------------------------------------- | ----------------------------------------- | | `UPGRADER_SLOT` | `bytes32(uint256(keccak256("superchainConfig.upgrader")) - 1)` | Account that can call the L2 `ProxyAdmin` | ### Interface @@ -214,22 +240,27 @@ specific configuration out of the initial L2 genesis state. All network specific configuration is sourced from deposit transactions during the initialization of the `SystemConfig`. -## Constants +### L1Block values + +| Name | Value | Definition | +| ----------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `ConfigType` | `uint8` | An enum representing the type of config being set | +| `WithdrawalNetwork` | `uint8(0)` or `uint8(1)` | `0` means withdraw to L1, `1` means withdraw to L2 | +| `RECIPIENT` | `address` | The account that will receive funds sent out of the `FeeVault` | +| `MIN_WITHDRAWAL_AMOUNT` | `uint256` | The minimum amount of native asset held in the `FeeVault` before withdrawal is authorized | +| Fee Vault Config | `bytes32` | `bytes32((WithdrawalNetwork << 248) \|\| uint256(uint88(MIN_WITHDRAWAL_AMOUNT)) \|\| uint256(uint160(RECIPIENT)))` | -| Name | Value | Definition | -| --------- | ------------------------- | -- | -| `ConfigType` | `uint8` | An enum representing the type of config being set | -| `WithdrawalNetwork` | `uint8(0)` or `uint8(1)` | `0` means withdraw to L1, `1` means withdraw to L2 | -| `RECIPIENT` | `address` | The account that will receive funds sent out of the `FeeVault` | -| `MIN_WITHDRAWAL_AMOUNT` | `uint256` | The minimum amount of native asset held in the `FeeVault` before withdrawal is authorized | -| Fee Vault Config | `bytes32` | `bytes32((WithdrawalNetwork << 248) \|\| uint256(uint88(MIN_WITHDRAWAL_AMOUNT)) \|\| uint256(uint160(RECIPIENT)))` | -| `BASE_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.basefeevaultconfig")) - 1)` | The Fee Vault Config for the `BaseFeeVault` | -| `L1_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.l1feevaultconfig")) - 1)` | The Fee Vault Config for the `L1FeeVault` | -| `SEQUENCER_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.sequencerfeevaultconfig")) - 1)` | The Fee Vault Config for the `SequencerFeeVault` | +### L1Block slots + +| Name | Value | Definition | +| ----------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------- | +| `BASE_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.basefeevaultconfig")) - 1)` | The Fee Vault Config for the `BaseFeeVault` | +| `L1_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.l1feevaultconfig")) - 1)` | The Fee Vault Config for the `L1FeeVault` | +| `SEQUENCER_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.sequencerfeevaultconfig")) - 1)` | The Fee Vault Config for the `SequencerFeeVault` | | `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1crossdomainmessengeraddress")) - 1)` | `abi.encode(address(L1CrossDomainMessengerProxy))` | -| `L1_ERC_721_BRIDGE_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1erc721bridgeaddress")) - 1)` | `abi.encode(address(L1ERC721BridgeProxy))` | -| `L1_STANDARD_BRIDGE_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1standardbridgeaddress")) - 1)` | `abi.encode(address(L1StandardBridgeProxy))` | -| `REMOTE_CHAIN_ID` | `bytes32(uint256(keccak256("opstack.remotechainid")) - 1)` | Chain ID of the remote chain | +| `L1_ERC_721_BRIDGE_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1erc721bridgeaddress")) - 1)` | `abi.encode(address(L1ERC721BridgeProxy))` | +| `L1_STANDARD_BRIDGE_ADDRESS` | `bytes32(uint256(keccak256("opstack.l1standardbridgeaddress")) - 1)` | `abi.encode(address(L1StandardBridgeProxy))` | +| `REMOTE_CHAIN_ID` | `bytes32(uint256(keccak256("opstack.remotechainid")) - 1)` | Chain ID of the remote chain | ## Predeploys @@ -290,9 +321,9 @@ via a deposit transaction from the `DEPOSITOR_ACCOUNT`. ##### `setIsthmus` -This function is meant to be called once on the activation block of the holocene network upgrade. +This function is meant to be called once on the activation block of the Isthmus network upgrade. It MUST only be callable by the `DEPOSITOR_ACCOUNT` once. When it is called, it MUST call -call each getter for the network specific config and set the returndata into storage. +each getter for the network specific config and set the returndata into storage. ##### `setConfig` @@ -331,11 +362,11 @@ The following functions are updated to read from the `L1Block` contract: - `minWithdrawalAmount()(uint256)` - `withdraw()` -| Name | Call | -| ---- | -------- | -| `BaseFeeVault` | `L1Block.getConfig(ConfigType.BASE_FEE_VAULT_CONFIG)` | +| Name | Call | +| ------------------- | ---------------------------------------------------------- | +| `BaseFeeVault` | `L1Block.getConfig(ConfigType.BASE_FEE_VAULT_CONFIG)` | | `SequencerFeeVault` | `L1Block.getConfig(ConfigType.SEQUENCER_FEE_VAULT_CONFIG)` | -| `L1FeeVault` | `L1Block.getConfig(ConfigType.L1_FEE_VAULT_CONFIG)` | +| `L1FeeVault` | `L1Block.getConfig(ConfigType.L1_FEE_VAULT_CONFIG)` | ##### `config` @@ -347,16 +378,18 @@ function config()(address,uint256,WithdrawalNetwork) ### L2CrossDomainMessenger -#### Interface - -The following functions are updated to read from the `L1Block` contract by calling `L1Block.getConfig(ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS)`: +To make this contract not initializable, the universal `CrossDomainMessenger` contract is updated to no longer be initializable. +However, the L1 version continues to be. -- `otherMessenger()(address)` -- `OTHER_MESSENGER()(address)` +The `otherMessenger()(address)` function is updated to read from the `L1Block` contract +by calling `L1Block.getConfig(ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS)`. ### L2ERC721Bridge -#### Interface +To make this contract not initializable, the universal `ERC721Bridge` contract is updated to no longer be initializable. +However, the L1 version continues to be. + +The `messenger()` function is updated to return `Predeploys.L2_CROSS_DOMAIN_MESSENGER`. The following functions are updated to read from the `L1Block` contract by calling `L1Block.getConfig(ConfigType.L1_ERC721_BRIDGE_ADDRESS)`: @@ -365,7 +398,10 @@ The following functions are updated to read from the `L1Block` contract by calli ### L2StandardBridge -#### Interface +To make this contract not initializable, the universal `StandardBridge` contract is updated to no longer be initializable. +However, the L1 version continues to be. + +The `messenger()` function is updated to return `Predeploys.L2_CROSS_DOMAIN_MESSENGER`. The following functions are updated to read from the `L1Block` contract by calling `L1Block.getConfig(ConfigType.L1_STANDARD_BRIDGE_ADDRESS)`: @@ -374,9 +410,22 @@ The following functions are updated to read from the `L1Block` contract by calli ### OptimismMintableERC721Factory +This contract is updated to remove its constructor. + The chain id is no longer read from storage but instead is read from the `L1Block` contract by calling `L1Block.getConfig(ConfigType.REMOTE_CHAIN_ID)` +The bridge is no longer set in the constructor but instead it uses the `Predeploys.L2_ERC721_BRIDGE` + +### OptimismMintableERC20Factory + +The universal contract is updated to be abstract and no longer initializable. +Two new contracts are derived from it: `L1OptimismMintableERC20Factory` and `L2OptimismMintableERC20Factory` + +In L2, the bridge is no longer set in the constructor but instead it uses the `Predeploys.L2_STANDARD_BRIDGE` + +The L1 version continues to be initializable. + ## Security Considerations ### GovernanceToken