diff --git a/specs/SUMMARY.md b/specs/SUMMARY.md index 182e9a37a..69b2d33e7 100644 --- a/specs/SUMMARY.md +++ b/specs/SUMMARY.md @@ -55,8 +55,13 @@ - [Execution Engine](./protocol/holocene/exec-engine.md) - [System Config](./protocol/holocene/system-config.md) - [Isthmus](./protocol/isthmus/overview.md) + - [Configurability](./protocol/isthmus/configurability.md) + - [Derivation](./protocol/isthmus/derivation.md) - [Execution Engine](./protocol/isthmus/exec-engine.md) + - [L1 Attributes](./protocol/isthmus/l1-attributes.md) + - [Predeploys](./protocol/isthmus/predeploys.md) - [Superchain Config](./protocol/isthmus/superchain-config.md) + - [System Config](./protocol/isthmus/system-config.md) - [Governance]() - [Governance Token](./governance/gov-token.md) - [Experimental]() diff --git a/specs/protocol/configurability.md b/specs/protocol/configurability.md index 267cb3521..9ba1a1d4a 100644 --- a/specs/protocol/configurability.md +++ b/specs/protocol/configurability.md @@ -30,6 +30,8 @@ - [Start block](#start-block) - [Superchain target](#superchain-target) - [Governance Token](#governance-token) + - [Operator Fee Scalar](#operator-fee-scalar) + - [Operator Fee Constant](#operator-fee-constant) - [Resource Config](#resource-config) - [Policy Parameters](#policy-parameters) - [Data Availability Type](#data-availability-type) @@ -47,6 +49,7 @@ - [Guardian address](#guardian-address) - [Proposer address](#proposer-address) - [Sequencer P2P / Unsafe head signer](#sequencer-p2p--unsafe-head-signer) + - [Operator Fee Manager](#operator-fee-manager) @@ -283,6 +286,21 @@ contracts deployed on layer 1.
**Requirement:** Disabled
**Notes:** Simple clear restriction.
+### [Operator Fee Scalar](exec-engine.md#operator-fees) + +**Description:** Operator fee scalar -- used to calculate the operator fee
+**Administrator:** [Operator Fee Manager](#operator-fee-manager)
+**Requirement:** 0
+ +### [Operator Fee Constant](exec-engine.md#operator-fees) + +**Description:** Operator fee constant -- used to calculate the operator fee
+**Administrator:** [Operator Fee Manager](#operator-fee-manager)
+**Requirement:** 0
+ +Note that the operator fee scalar and constant are primarily used for non-standard configurations, +like op-succinct, so their standard values are 0. + [^chain-id-uniqueness]: The chain ID must be globally unique among all EVM chains. ### Resource Config @@ -441,4 +459,11 @@ configuration of the permissioned dispute game.
**Requirement:** No requirement
**Notes:**
+### Operator Fee Manager + +**Description:** Account authorized to modify the operator fee scalar.
+**Administrator:** [System Config Owner](#admin-roles)
+**Requirement:** `address(0)`
+**Notes:** For standard configurations, the operator fee manager is not used, so it is set to the null address.
+ [^of-gnosis-safe-l1]: 5 of 7 GnosisSafe controlled by Optimism Foundation (OF). Mainnet and Sepolia addresses can be found at [privileged roles](https://docs.optimism.io/chain/security/privileged-roles). diff --git a/specs/protocol/isthmus/configurability.md b/specs/protocol/isthmus/configurability.md index ed16365e9..63e56e39f 100644 --- a/specs/protocol/isthmus/configurability.md +++ b/specs/protocol/isthmus/configurability.md @@ -10,11 +10,17 @@ - [`SystemConfig`](#systemconfig) - [`ConfigUpdate`](#configupdate) - [Initialization](#initialization) + - [Modifying Operator Fee Parameters](#modifying-operator-fee-parameters) - [Interface](#interface) + - [Operator fee parameters](#operator-fee-parameters) + - [`operatorFeeScalar`](#operatorfeescalar) + - [`operatorFeeConstant`](#operatorfeeconstant) + - [`setOperatorFeeScalars`](#setoperatorfeescalars) - [Fee Vault Config](#fee-vault-config) - [`setBaseFeeVaultConfig`](#setbasefeevaultconfig) - [`setL1FeeVaultConfig`](#setl1feevaultconfig) - [`setSequencerFeeVaultConfig`](#setsequencerfeevaultconfig) + - [`setOperatorFeeVaultConfig`](#setoperatorfeevaultconfig) - [`OptimismPortal`](#optimismportal) - [Interface](#interface-1) - [`setConfig`](#setconfig) @@ -25,7 +31,7 @@ ## Overview The `SystemConfig` and `OptimismPortal` are updated with a new flow for chain -configurability. +configurability. A new service role `OperatorFeeManager` is added to manage the operator fee collection. ## Constants @@ -39,10 +45,11 @@ The `ConfigType` enum represents configuration that can be modified. | `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 | +| `OPERATOR_FEE_VAULT_CONFIG` | `uint8(4)` | Sets the Fee Vault Config for the `OperatorFeeVault` | +| `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` | `uint8(5)` | Sets the `L1CrossDomainMessenger` address | +| `L1_ERC_721_BRIDGE_ADDRESS` | `uint8(6)` | Sets the `L1ERC721Bridge` address | +| `L1_STANDARD_BRIDGE_ADDRESS` | `uint8(7)` | Sets the `L1StandardBridge` address | +| `REMOTE_CHAIN_ID` | `uint8(8)` | Sets the chain id of the base chain | ## `SystemConfig` @@ -57,6 +64,7 @@ The following `ConfigUpdate` event is defined where the `CONFIG_VERSION` is `uin | `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 | +| `OPERATOR_FEE_PARAMS` | `uint8(5)` | `uint256(_operatorFeeScalar) << 64 \| _operatorFeeConstant` | Modifies the operator fee parameters | ### Initialization @@ -67,10 +75,12 @@ 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.OPERATOR_FEE_PARAMS` - `setConfig(SET_GAS_PAYING_TOKEN)` - `setConfig(SET_BASE_FEE_VAULT_CONFIG)` - `setConfig(SET_L1_FEE_VAULT_CONFIG)` - `setConfig(SET_SEQUENCER_FEE_VAULT_CONFIG)` +- `setConfig(SET_OPERATOR_FEE_VAULT_CONFIG)` - `setConfig(SET_L1_CROSS_DOMAIN_MESSENGER_ADDRESS)` - `setConfig(SET_L1_ERC_721_BRIDGE_ADDRESS)` - `setConfig(SET_L1_STANDARD_BRIDGE_ADDRESS)` @@ -78,8 +88,46 @@ The following actions should happen during the initialization of the `SystemConf These actions MAY only be triggered if there is a diff to the value. +Since the `OperatorFeeVault` is new in Isthmus, the `setConfig(SET_OPERATOR_FEE_VAULT_CONFIG)` MUST be emitted. + +`ConfigUpdate.OPERATOR_FEE_PARAMS` MAY be emitted. If it is not emitted, the `operatorFeeScalar` and +`operatorFeeConstant` are set to 0 by default. + +### Modifying Operator Fee Parameters + +A new `SystemConfig` `UpdateType` is introduced that enables the modification of +the `operatorFeeScalar` and `operatorFeeConstant` by the [`OperatorFeeManager`](../configurability.md#operator-fee-manager). + ### Interface +#### Operator fee parameters + +##### `operatorFeeScalar` + +This function returns the currently configured operator fee scalar. + +```solidity +function operatorFeeScalar()(uint32) +``` + +##### `operatorFeeConstant` + +This function returns the currently configured operator fee constant. + +```solidity +function operatorFeeConstant()(uint64) +``` + +##### `setOperatorFeeScalars` + +This function sets the `operatorFeeScalar` and `operatorFeeConstant`. + +This function MUST only be callable by the [`OperatorFeeManager`](../configurability.md#operator-fee-manager). + +```solidity +function setOperatorFeeScalar(uint32 _operatorFeeScalar, uint64 _operatorFeeConstant) +``` + #### Fee Vault Config For each `FeeVault`, there is a setter for its config. The arguments to the setter include @@ -106,6 +154,12 @@ function setL1FeeVaultConfig(address,uint256,WithdrawalNetwork) function setSequencerFeeVaultConfig(address,uint256,WithdrawalNetwork) ``` +##### `setOperatorFeeVaultConfig` + +```solidity +function setOperatorFeeVaultConfig(address,uint256,WithdrawalNetwork) +``` + ## `OptimismPortal` The `OptimismPortal` is updated to emit a special system `TransactionDeposited` event. diff --git a/specs/protocol/isthmus/derivation.md b/specs/protocol/isthmus/derivation.md new file mode 100644 index 000000000..d5e8c0c01 --- /dev/null +++ b/specs/protocol/isthmus/derivation.md @@ -0,0 +1,25 @@ +# Isthmus L2 Chain Derivation Changes + + + +**Table of Contents** + +- [Network upgrade automation transactions](#network-upgrade-automation-transactions) + + + +# Network upgrade automation transactions + +The Isthmus hardfork activation block contains the following transactions, in this order: + +- L1 Attributes Transaction +- User deposits from L1 +- Network Upgrade Transactions + - L1Block deployment + - Update L1Block Proxy ERC-1967 Implementation + - L1Block Enable Isthmus + - GasPriceOracle deployment + - Update GasPriceOracle Proxy ERC-1967 Implementation + - GasPriceOracle Enable Isthmus + - OptimismMintableERC20Factory deployment + - Update OptimismMintableERC20Factory Proxy ERC-1967 Implementation diff --git a/specs/protocol/isthmus/exec-engine.md b/specs/protocol/isthmus/exec-engine.md index e1eaf905a..0e99a74fc 100644 --- a/specs/protocol/isthmus/exec-engine.md +++ b/specs/protocol/isthmus/exec-engine.md @@ -21,6 +21,10 @@ - [Engine API Updates](#engine-api-updates) - [Update to `ExecutableData`](#update-to-executabledata) - [`engine_newPayloadV3` API](#engine_newpayloadv3-api) +- [Fees](#fees) + - [Operator Fee](#operator-fee) + - [Configuring Parameters](#configuring-parameters) + - [Fee Vaults](#fee-vaults) @@ -141,3 +145,42 @@ Withdrawals list in the block body is encoded as an empty RLP list. Post Isthmus, `engine_newPayloadV3` will be used with the additional `ExecutionPayload` attribute. This attribute is omitted prior to Isthmus. + +## Fees + +New OP stack variants have different resource consumption patterns, and thus require a more flexible +pricing model. To enable more customizable fee structures, Isthmus adds a new component to the fee +calculation: the `operatorFee`, which is parameterized by two scalars: the `operatorFeeScalar` +and the `operatorFeeConstant`. + +### Operator Fee + +The operator fee, is set as follows: + +`operatorFee = (gasUsed * operatorFeeScalar / 1e6) + operatorFeeConstant` + +Where: + +- `gasUsed` is amount of gas used by the transaction. +- `operatorFeeScalar` is a `uint32` scalar set by the chain operator, scaled by `1e6`. +- `operatorFeeConstant` is a `uint64` scalar set by the chain operator. + +#### Configuring Parameters + +`operatorFeeScalar` and `operatorFeeConstant` are loaded in a similar way to the `baseFeeScalar` and +`blobBaseFeeScalar` used in the [`L1Fee`](../../protocol/exec-engine.md#ecotone-l1-cost-fee-changes-eip-4844-da). +calculation. In more detail, these parameters can be accessed in two interchangable ways. + +- read from the deposited L1 attributes (`operatorFeeScalar` and `operatorFeeConstant`) of the current L2 block +- read from the L1 Block Info contract (`0x4200000000000000000000000000000000000015`) + - using the respective solidity getter functions (`operatorFeeScalar`, `operatorFeeConstant`) + - using direct storage-reads: + - Operator fee scalar as big-endian `uint32` in slot `8` at offset `0`. + - Operator fee constant as big-endian `uint64` in slot `8` at offset `4`. + +### Fee Vaults + +These collected fees are sent to a new vault for the `operatorFee`: the [`OperatorFeeVault`](predeploys.md#operatorfeevault). + +Like the existing vaults, this is a hardcoded address, pointing at a pre-deployed proxy contract. +The proxy is backed by a vault contract deployment, based on `FeeVault`, to route vault funds to L1 securely. diff --git a/specs/protocol/isthmus/l1-attributes.md b/specs/protocol/isthmus/l1-attributes.md new file mode 100644 index 000000000..1f1e0e559 --- /dev/null +++ b/specs/protocol/isthmus/l1-attributes.md @@ -0,0 +1,40 @@ +# L1 Block Attributes + + + +**Table of Contents** + +- [Overview](#overview) + + + +## Overview + +The L1 block attributes transaction is updated to include the operator fee parameters. + +| Input arg | Type | Calldata bytes | Segment | +| ----------------- | ------- | -------------- | ------- | +| {0x098999be} | | 0-3 | n/a | +| baseFeeScalar | uint32 | 4-7 | 1 | +| blobBaseFeeScalar | uint32 | 8-11 | | +| sequenceNumber | uint64 | 12-19 | | +| l1BlockTimestamp | uint64 | 20-27 | | +| l1BlockNumber | uint64 | 28-35 | | +| basefee | uint256 | 36-67 | 2 | +| blobBaseFee | uint256 | 68-99 | 3 | +| l1BlockHash | bytes32 | 100-131 | 4 | +| batcherHash | bytes32 | 132-163 | 5 | +| operatorFeeScalar | uint32 | 164-167 | 6 | +| operatorFeeConstant | uint64 | 168-175 | | + +Note that the first input argument, in the same pattern as previous versions of the L1 attributes transaction, +is the function selector: the first four bytes of `keccak256("setL1BlockValuesIsthmus()")`. + +In the first L2 block after the Isthmus activation block, the Isthmus L1 attributes are first used. + +The pre-Isthmus values are migrated over 1:1. +Blocks after the Isthmus activation block contain all pre-Isthmus values 1:1, +and also set the following new attributes: + +- The `operatorFeeScalar` is set to `0`. +- The `operatorFeeConstant` is set to `0`. diff --git a/specs/protocol/isthmus/overview.md b/specs/protocol/isthmus/overview.md index 90c286eb2..776747ab1 100644 --- a/specs/protocol/isthmus/overview.md +++ b/specs/protocol/isthmus/overview.md @@ -15,9 +15,16 @@ This document is not finalized and should be considered experimental. ## Execution Layer - [L2ToL1MessagePasser Storage Root in Header](./exec-engine.md##l2tol1messagepasser-storage-root-in-header) +- [Operator Fee](./exec-engine.md#operator-fee) ## Consensus Layer +- [Isthmus Derivation](./derivation.md#isthmus-derivation) +- [Configurability](./configurability.md) + ## Smart Contracts - [SuperchainConfig](./superchain-config.md) +- [Predeploys](./predeploys.md) +- [L1 Block Attributes](./l1-attributes.md) +- [System Config](./system-config.md) diff --git a/specs/protocol/isthmus/predeploys.md b/specs/protocol/isthmus/predeploys.md index 51cd6ed58..9df6bdec2 100644 --- a/specs/protocol/isthmus/predeploys.md +++ b/specs/protocol/isthmus/predeploys.md @@ -17,12 +17,17 @@ - [FeeVault](#feevault) - [Interface](#interface-1) - [`config`](#config) + - [OperatorFeeVault](#operatorfeevault) - [L2CrossDomainMessenger](#l2crossdomainmessenger) - [Interface](#interface-2) - [L2ERC721Bridge](#l2erc721bridge) - [Interface](#interface-3) - [L2StandardBridge](#l2standardbridge) - [Interface](#interface-4) + - [GasPriceOracle](#gaspriceoracle) + - [Interface](#interface-5) + - [`setIsthmus`](#setisthmus-1) + - [`getOperatorFee`](#getoperatorfee) - [OptimismMintableERC721Factory](#optimismmintableerc721factory) - [Security Considerations](#security-considerations) - [GovernanceToken](#governancetoken) @@ -46,6 +51,7 @@ of the `SystemConfig`. | `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` | +| `OPERATOR_FEE_VAULT_CONFIG` | `bytes32(uint256(keccak256("opstack.operatorfeevaultconfig")) - 1)` | The Fee Vault Config for the `OperatorFeeVault` | | `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))` | @@ -67,6 +73,7 @@ graph LR BaseFeeVault -- "getConfig(ConfigType.GAS_PAYING_TOKEN)(address,uint256,uint8)" --> L1Block SequencerFeeVault -- "getConfig(ConfigType.SEQUENCER_FEE_VAULT_CONFIG)(address,uint256,uint8)" --> L1Block L1FeeVault -- "getConfig(ConfigType.L1_FEE_VAULT_CONFIG)(address,uint256,uint8)" --> L1Block + OperatorFeeVault -- "getConfig(ConfigType.OPERATOR_FEE_VAULT_CONFIG)(address,uint256,uint8)" --> L1Block L2CrossDomainMessenger -- "getConfig(ConfigType.L1_CROSS_DOMAIN_MESSENGER_ADDRESS)(address)" --> L1Block L2StandardBridge -- "getConfig(ConfigType.L1_STANDARD_BRIDGE_ADDRESS)(address)" --> L1Block L2ERC721Bridge -- "getConfig(ConfigType.L1_ERC721_BRIDGE_ADDRESS)(address)" --> L1Block @@ -98,6 +105,7 @@ The following storage slots are defined: - `BASE_FEE_VAULT_CONFIG` - `L1_FEE_VAULT_CONFIG` - `SEQUENCER_FEE_VAULT_CONFIG` +- `OPERATOR_FEE_VAULT_CONFIG` - `L1_CROSS_DOMAIN_MESSENGER_ADDRESS` - `L1_ERC_721_BRIDGE_ADDRESS` - `L1_STANDARD_BRIDGE_ADDRESS` @@ -140,7 +148,8 @@ The caller needs to ABI decode the data into the desired type. ### FeeVault -The following changes apply to each of the `BaseFeeVault`, the `L1FeeVault` and the `SequencerFeeVault`. +The following changes apply to each of the `BaseFeeVault`, the `L1FeeVault` the `SequencerFeeVault`, and the new +`OperatorFeeVault`. #### Interface @@ -156,6 +165,7 @@ The following functions are updated to read from the `L1Block` contract: | `BaseFeeVault` | `L1Block.getConfig(ConfigType.BASE_FEE_VAULT_CONFIG)` | | `SequencerFeeVault` | `L1Block.getConfig(ConfigType.SEQUENCER_FEE_VAULT_CONFIG)` | | `L1FeeVault` | `L1Block.getConfig(ConfigType.L1_FEE_VAULT_CONFIG)` | +| `OperatorFeeVault` | `L1Block.getConfig(ConfigType.OPERATOR_FEE_VAULT_CONFIG)` | ##### `config` @@ -165,6 +175,13 @@ A new function is added to fetch the full Fee Vault Config. function config()(address,uint256,WithdrawalNetwork) ``` +### OperatorFeeVault + +This vault implements `FeeVault`, like `BaseFeeVault`, `SequencerFeeVault`, and `L1FeeVault`. No special logic is +needed in order to insert or withdraw funds. + +Its address will be `0x420000000000000000000000000000000000001b`. + ### L2CrossDomainMessenger #### Interface @@ -192,6 +209,40 @@ The following functions are updated to read from the `L1Block` contract by calli - `otherBridge()(address)` - `OTHER_BRIDGE()(address)` +### GasPriceOracle + +In order to maintain accurate offchain fee estimation, the `GasPriceOracle` must be updated to allow users +to estimate the operator fee. We also add a new boolean `isIsthmus` to help with evaluating the operator fee. + +#### Interface + +##### `setIsthmus` + +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. + +```solidity +function setIsthmus() external; +``` + +##### `getOperatorFee` + +This function calculates the operator fee based on the expected amount of gas used for a certain transaction. + +It uses the following values: + +- `operatorFeeScalar` +- `operatorFeeConstant` +- `isIsthmus` + +`operatorFeeScalar` and `operatorFeeConstant` are read from the `L1Block` contract, and `isIsthmus` +is read directly from storage. If `isIsthmus` is false, then this function MUST return `0`. + +```function +function getOperatorFee(uint256 gasUsed)(uint256) +``` + ### OptimismMintableERC721Factory The chain id is no longer read from storage but instead is read from the `L1Block` contract by calling diff --git a/specs/protocol/isthmus/system-config.md b/specs/protocol/isthmus/system-config.md new file mode 100644 index 000000000..000e85b23 --- /dev/null +++ b/specs/protocol/isthmus/system-config.md @@ -0,0 +1,29 @@ +# System Config + + + +**Table of Contents** + +- [Overview](#overview) + - [`Roles`](#roles) + + + +## Overview + +The `SystemConfig` is updated to set a new role: the `OPERATOR_FEE_MANAGER`. This role is set once upon +initialization and can only be set again with another call to `initialize`. + +### `Roles` + +The `Roles` struct is updated to include the new `OPERATOR_FEE_MANAGER` role. + +```solidity +struct Roles { + address owner; + address feeAdmin; + address operatorFeeManager; // new role +} +``` + +The operator may only change the `operatorFeeManager` role with a call to `initialize`.