From ca94ce222fd9b70e042bbdc94b7bab3a75593764 Mon Sep 17 00:00:00 2001 From: Hex <165055168+hexshire@users.noreply.github.com> Date: Thu, 24 Jul 2025 17:01:23 -0300 Subject: [PATCH 01/17] chore(cgt): remove current spec --- specs/SUMMARY.md | 1 - specs/experimental/custom-gas-token.md | 506 ------------------------- 2 files changed, 507 deletions(-) delete mode 100644 specs/experimental/custom-gas-token.md diff --git a/specs/SUMMARY.md b/specs/SUMMARY.md index f32891d13..50dfcf14e 100644 --- a/specs/SUMMARY.md +++ b/specs/SUMMARY.md @@ -68,7 +68,6 @@ - [Governance]() - [Governance Token](./governance/gov-token.md) - [Experimental]() - - [Custom Gas Token](./experimental/custom-gas-token.md) - [Standard L2 Genesis](./experimental/standard-l2-genesis.md) - [Alt-DA](./experimental/alt-da.md) - [Interoperability](./interop/overview.md) diff --git a/specs/experimental/custom-gas-token.md b/specs/experimental/custom-gas-token.md deleted file mode 100644 index 38694ddb2..000000000 --- a/specs/experimental/custom-gas-token.md +++ /dev/null @@ -1,506 +0,0 @@ -# Custom Gas Token - - - -**Table of Contents** - -- [Overview](#overview) -- [Constants](#constants) -- [Properties of a Gas Paying Token](#properties-of-a-gas-paying-token) - - [Automated Validation](#automated-validation) -- [Configuring the Gas Paying Token](#configuring-the-gas-paying-token) -- [Contract Modifications](#contract-modifications) - - [IGasToken Interface](#igastoken-interface) - - [Gas Token Aware](#gas-token-aware) - - [OptimismPortal](#optimismportal) - - [`depositERC20Transaction`](#depositerc20transaction) - - [Function Arguments](#function-arguments) - - [`depositTransaction`](#deposittransaction) - - [`setGasPayingToken`](#setgaspayingtoken) - - [StandardBridge](#standardbridge) - - [CrossDomainMessenger](#crossdomainmessenger) - - [SystemConfig](#systemconfig) - - [initialize](#initialize) - - [L1Block](#l1block) - - [WETH9](#weth9) -- [User Flow](#user-flow) - - [When ETH is the Native Asset](#when-eth-is-the-native-asset) - - [When an ERC20 Token is the Native Asset](#when-an-erc20-token-is-the-native-asset) - - [Differences](#differences) -- [Upgrade](#upgrade) - - [L1Block Deployment](#l1block-deployment) - - [L2CrossDomainMessenger Deployment](#l2crossdomainmessenger-deployment) - - [L2StandardBridge Deployment](#l2standardbridge-deployment) - - [L1Block Proxy Update](#l1block-proxy-update) - - [L2CrossDomainMessenger Proxy Update](#l2crossdomainmessenger-proxy-update) - - [L2StandardBridge Proxy Update](#l2standardbridge-proxy-update) -- [Selection of `ETHER_TOKEN_ADDRESS`](#selection-of-ether_token_address) -- [Standard Config](#standard-config) -- [Fees](#fees) -- [Security Considerations](#security-considerations) - - [OptimismPortal Token Allowance](#optimismportal-token-allowance) - - [Interoperability Support](#interoperability-support) - - [Wrapped Ether](#wrapped-ether) - - - -## Overview - -Custom gas token is also known as "custom L2 native asset". It allows for an L1-native ERC20 token to collateralize -and act as the gas token on L2. The native asset is used to buy gas which is used to pay for resources on the network -and is represented by EVM opcodes such as `CALLVALUE` (`msg.value`). - -Both the L1 and L2 smart contract systems MUST be able to introspect on the address of the gas paying token to -ensure the security of the system. The source of truth for the configuration of the address of the token is -in the L1 `SystemConfig` smart contract. - -The terms "custom gas token", "gas paying token", "native asset" and "gas paying asset" can all be used interchangeably. -Note, Ether is not a **custom** gas token, but may be used to pay gas. More on this in -[Configuring the Gas Paying Token](#configuring-the-gas-paying-token). - -## Constants - -| Constant | Value | Description | -| ----------------- | ------------------------------- | ----------- | -| `ETHER_TOKEN_ADDRESS` | `address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)` | Represents ether for gas paying asset | -| `DEPOSITOR_ACCOUNT` | `address(0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001)` | Account with auth permissions on `L1Block` contract | -| `GAS_PAYING_TOKEN_SLOT` | `bytes32(uint256(keccak256("opstack.gaspayingtoken")) - 1)` | Storage slot that contains the address and decimals of the gas paying token | -| `GAS_PAYING_TOKEN_NAME_SLOT` | `bytes32(uint256(keccak256("opstack.gaspayingtokenname")) - 1)` | Storage slot that contains the ERC20 `name()` of the gas paying token | -| `GAS_PAYING_TOKEN_SYMBOL_SLOT` | `bytes32(uint256(keccak256("opstack.gaspayingtokensymbol")) - 1)` | Storage slot that contains the ERC20 `symbol()` of the gas paying token | - -## Properties of a Gas Paying Token - -The gas paying token MUST satisfy the standard [ERC20](https://eips.ethereum.org/EIPS/eip-20) interface -and implementation. It MUST be an L1 contract. - -The gas paying token MUST NOT: - -- Have a fee on transfer -- Have rebasing logic -- Have call hooks on transfer -- Have other than 18 decimals -- Have out of band methods for modifying balance or allowance -- Have a `name()` that is more than 32 bytes long -- Have a `symbol()` this is more than 32 bytes long -- Be a [double entrypoint token](https://stermi.xyz/blog/ethernaut-challenge-24-solution-double-entry-point) - -It MUST be enforced on chain that the token has exactly 18 decimals to guarantee no losses of precision when -depositing a token that has a different number of decimals. It MUST be enforced on chain that the -token's `name` and `symbol` are less than 32 bytes to guarantee they can each fit into a single storage -slot. - -It MAY be possible to support ERC20 tokens with varying amounts of decimals in the future. - -### Automated Validation - -Ideally it is possible to have automated validation of custom gas paying tokens to know that the token satisfies the -above properties. Due to the nature of the EVM, it may not always be possible to do so. [ERC-165][erc165] -isn't always used by ERC20 tokens and isn't guaranteed to tell the truth. [USDT][usdt] -does not correctly implement the ERC20 spec. It may be possible to use execution traces to observe the -properties of the ERC20 tokens but some degree of social consensus will be required for determining -the validity of an ERC20 token. - -[erc165]: https://eips.ethereum.org/EIPS/eip-165 -[usdt]: https://forum.openzeppelin.com/t/usdt-locked-in-a-contract-that-has-a-withdraw-token-function/32154/2 - -## Configuring the Gas Paying Token - -The gas paying token is set within the L1 `SystemConfig` smart contract. This allows for easy access to the -information required to know if an OP Stack chain is configured to use a custom gas paying token. The gas paying -token is set during initialization and cannot be modified by the `SystemConfig` bytecode. Since the `SystemConfig` -is proxied, it is always possible to modify the storage slot that holds the gas paying token address directly -during an upgrade. It is assumed that the chain operator will not modify the gas paying token address unless they -specifically decide to do so and appropriately handle the consequences of the action. - -The gas paying token address is network specific configuration, therefore it MUST be set in storage and not -as an immutable. This ensures that the same contract bytecode can be used by multiple OP Stack chains. - -If the address in the `GAS_PAYING_TOKEN_SLOT` slot for `SystemConfig` is `address(0)`, the system is configured to -use `ether` as the gas paying token, and the getter for the token returns `ETHER_TOKEN_ADDRESS`. If the address in -the `GAS_PAYING_TOKEN_SLOT` slot for `SystemConfig` is not `address(0)`, the system is configured to use a custom -gas paying token, and the getter returns the address in the slot. - -```mermaid -flowchart LR - subgraph Layer One - OP[OptimismPortal] - subgraph SystemConfig - TA1[Token Address] - end - CO[Chain Operator] -->|"initialize()"| SystemConfig - SystemConfig -->|"setGasPayingToken()"| OP - end - - subgraph Layer Two - subgraph L1Block - TA2[Token Address] - end - end - OP -->|"setGasPayingToken()"| L1Block -``` - -## Contract Modifications - -### IGasToken Interface - -This interface encapsulates the shared interface. - -```solidity -interface IGasToken { - /// @notice Getter for the ERC20 token address that is used to pay for gas and its decimals. - function gasPayingToken() external view returns (address, uint8); - /// @notice Returns the gas token name. - function gasPayingTokenName() external view returns (string memory); - /// @notice Returns the gas token symbol. - function gasPayingTokenSymbol() external view returns (string memory); - /// @notice Returns true if the network uses a custom gas token. - function isCustomGasToken() external view returns (bool); -} -``` - -If a custom gas token is not used, then `gasPayingToken()` should return `(ETHER_TOKEN_ADDRESS, 18)`, -`gasPayingTokenName` should return `Ether` and `gasPayingTokenSymbol` should return `ETH`. - -This interface applies to the following contracts: - -- `L1Block` -- `SystemConfig` - -### Gas Token Aware - -The following contracts are aware internally if the chain is using a custom gas token -but do not expose anything as part of their ABI that indicates awareness. - -- `L1StandardBridge` -- `L2StandardBridge` -- `L1CrossDomainMessenger` -- `L2CrossDomainMessenger` -- `OptimismPortal` - -### OptimismPortal - -The `OptimismPortal` is updated with a new interface specifically for depositing custom tokens. - -#### `depositERC20Transaction` - -The `depositERC20Transaction` function is useful for sending custom gas tokens to L2. It is broken out into its -own interface to maintain backwards compatibility with chains that use `ether`, to help simplify the implementation -and make it explicit for callers that are trying to deposit an ERC20 native asset. - -```solidity -function depositERC20Transaction( - address _to, - uint256 _mint, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data -) public; -``` - -This function MUST revert when `ether` is the L2's native asset. It MUST not be `payable`, meaning that it will -revert when `ether` is sent with a `CALL` to it. It uses a `transferFrom` flow, so users MUST first `approve()` -the `OptimismPortal` before they can deposit tokens. - -##### Function Arguments - -The following table describes the arguments to `depositERC20Transaction` - -| Name | Type | Description | -|---------|---------|---------| -| `_to` | `address` | The target of the deposit transaction | -| `_mint` | `uint256` | The amount of token to deposit | -| `_value` | `uint256` | The value of the deposit transaction, used to transfer native asset that is already on L2 from L1 | -| `_gasLimit` | `uint64` | The gas limit of the deposit transaction | -| `_isCreation` | `bool` | Signifies the `_data` should be used with `CREATE` | -| `_data` | `bytes` | The calldata of the deposit transaction | - -#### `depositTransaction` - -This function is the hot code path for all usage of the `L1StandardBridge` and `L1CrossDomainMessenger`, so it MUST -be as backwards compatible as possible. It MUST revert for chains using custom gas token when `ether` is sent with -the `CALL`. This prevents `ether` from being stuck in the `OptimismPortal`, since it cannot be used to mint native -asset on L2. - -#### `setGasPayingToken` - -This function MUST only be callable by the `SystemConfig`. When called, it creates a special deposit transaction -from the `DEPOSITOR_ACCOUNT` that calls the `L1Block.setGasPayingToken` function. The ERC20 `name` and `symbol` -are passed as `bytes32` to prevent the usage of dynamically sized `string` arguments. - -```solidity -function setGasPayingToken(address _token, uint8 _decimals, bytes32 _name, bytes32 _symbol) external; -``` - -### StandardBridge - -The `StandardBridge` contracts on both L1 and L2 MUST be aware of the custom gas token. The `ether` specific ABI on the -`StandardBridge` is disabled when custom gas token is enabled. - -The following methods MUST revert when custom gas token is being used: - -- `bridgeETH(uint32,bytes)` -- `bridgeETHTo(address,uint32,bytes)` -- `receive()` -- `finalizeBridgeETH(address, address, uint256, bytes)` - -The following legacy methods in `L1StandardBridge` MUST revert when custom gas token is being used: - -- `depositETH(uint32,bytes)` -- `depositETHTo(address,uint32,bytes)` -- `finalizeETHWithdrawal(address,address,uint256,bytes)` - -The following legacy methods in `L2StandardBridge` MUST always revert when custom gas token is being used: - -- `withdraw(address,uint256,uint32,bytes)` -- `withdrawTo(address,address,uint256,uint32,bytes)` -- `finalizeDeposit(address,address,address,address,uint256,bytes)` - -These methods are deprecated and subject to be removed in the future. - -### CrossDomainMessenger - -The `CrossDomainMessenger` contracts on both L1 and L2 MUST NOT be able to facilitate the transfer of native asset -on custom gas token chains due to their tight coupling with `ether` and the `OptimismPortal` and `L2ToL1MessagePasser` -contracts. - -It is possible to support this in the future with a redesign of the `CrossDomainMessenger` contract. -It would need to have conditional logic based on being a custom gas token chain and facilitate transfer -of the ERC20 token to the end user on the other side. It adds a layer of extra state modifying calls -so it may not be worth adding. - -The following methods MUST revert when `CALLVALUE` is non zero: - -- `sendMessage(address,bytes,uint32)` - -It should be impossible to call `relayMessage` when `CALLVALUE` is non zero, as it is enforced by `sendMessage`. - -The `CrossDomainMessenger` also has the API for _getting_ the custom gas token, namely `gasPayingToken()`, which outputs -a tuple of the address and decimals of the custom gas token. - -- The `L1CrossDomainMessenger` fetches this tuple from the `SystemConfig` contract. -- The `L2CrossDomainMessenger` fetches this tuple from the `L1Block` contract. - -### SystemConfig - -The `SystemConfig` is the source of truth for the address of the custom gas token. It does on chain validation, -stores information about the token and well as passes the information to L2. - -#### initialize - -The `SystemConfig` is modified to allow the address of the custom gas paying token to be set during the call -to `initialize`. Using a custom gas token is indicated by passing an address other than `ETHER_TOKEN_ADDRESS` -or `address(0)`. If `address(0)` is used for initialization, it MUST be mapped into `ETHER_TOKEN_ADDRESS` -before being forwarded to the rest of the system. When a custom gas token is set, the number of decimals -on the token MUST be exactly 18, the name of the token MUST be less than or equal to 32 bytes and the -symbol MUST be less than or equal to 32 bytes. If the token passes all of these checks, -`OptimismPortal.setGasPayingToken` is called. The implementation of `initialize` MUST not allow the chain -operator to change the address of the custom gas token if it is already set. - -### L1Block - -The `L1Block` contract is upgraded with a permissioned function `setGasPayingToken` that is used to set -information about the gas paying token. The `DEPOSITOR_ACCOUNT` MUST be the only account that can call the -setter function. This setter is differentiated from the `setL1BlockValues` functions because the gas paying -token is not meant to be dynamic config whereas the arguments to `setL1BlockValues` are generally dynamic in nature. - -Any L2 contract that wants to learn the address of the gas paying token can call the getter on the -`L1Block` contract. - -```solidity -function setGasPayingToken(address _token, uint8 _decimals, byte32 _name, bytes32 _symbol) external; -``` - -### WETH9 - -The `WETH9` predeploy is updated so that it calls out to the `L1Block` contract to retrieve the `name` and `symbol`. -This allows for front ends to more easily trust the token contract. It should prepend `Wrapped` to the `name` -and `W` to the `symbol`. - -## User Flow - -The user flow for custom gas token chains is slightly different than for chains that use `ether` -to pay for gas. The following tables highlight the methods that can be used for depositing and -withdrawing the native asset. Not every interface is included. - -The `StandardBridge` and `CrossDomainMessenger` are symmetric in their APIs between L1 and L2 -meaning that "sending to the other domain" indicates it can be used for both deposits and withdrawals. - -### When ETH is the Native Asset - -| Scenario | Method | Prerequisites | -|----------|--------|---------------| -| Native Asset Send to Other Domain | `L1StandardBridge.bridgeETH(uint32,bytes) payable` | None | -| Native Asset and/or Message Send to Other Domain | `L1CrossDomainMessenger.sendMessage(address,bytes,uint32) payable` | None | -| Native Asset Deposit | `OptimismPortal.depositTransaction(address,uint256,uint64,bool,bytes) payable` | None | -| ERC20 Send to Other Domain | `L1StandardBridge.bridgeERC20(address,address,uint256,uint32,bytes)` | Approve `L1StandardBridge` for ERC20 | -| Native Asset Withdrawal | `L2ToL1MessagePasser.initiateWithdrawal(address,uint256,bytes) payable` | None | - -There are multiple APIs for users to deposit or withdraw `ether`. Depending on the usecase, different APIs should be -preferred. For a simple send of just `ether` with no calldata, the `OptimismPortal` or `L2ToL1MessagePasser` should -be used directly. If sending with calldata and replayability on failed calls is desired, the `CrossDomainMessenger` -should be used. Using the `StandardBridge` is the most expensive and has no real benefit for end users. - -### When an ERC20 Token is the Native Asset - -| Scenario | Method | Prerequisites | -|----------|--------|---------------| -| Native Asset Deposit | `OptimismPortal.depositERC20Transaction(address,uint256,uint256,uint64,bool,bytes)` | Approve `OptimismPortal` for ERC20 | -| ERC20 Send to Other Domain | `L1StandardBridge.bridgeERC20(address,address,uint256,uint32,bytes)` | Approve `L1StandardBridge` for ERC20 | -| Native Asset Withdrawal | `L2ToL1MessagePasser.initiateWithdrawal(address,uint256,bytes) payable` | None | - -Users should deposit native asset by calling `depositERC20Transaction` on the `OptimismPortal` contract. -Users must first `approve` the address of the `OptimismPortal` so that the `OptimismPortal` can use -`transferFrom` to take ownership of the ERC20 asset. - -Users should withdraw value by calling the `L2ToL1MessagePasser` directly. - -The following diagram shows the control flow for when a user attempts to send `ether` through -the `StandardBridge`, either on L1 or L2. - -```mermaid -flowchart TD - A1(User) -->|Attempts to deposit/withdraw ether| A2(StandardBridge or CrossDomainMessenger) - A2 -->|Is chain using custom gas token?| A3{SystemConfig or L1Block} - A3 --> |Yes| A2 - A2 --> |Revert| A1 - -``` - -### Differences - -The main difference is that the `StandardBridge` and `CrossDomainMessenger` cannot be used to send the native asset -to the other domain when an ERC20 token is the native asset. The `StandardBridge` can still be used for bridging -arbitrary ERC20 tokens and the `CrossDomainMessenger` can still be used for arbitrary message passing. - -## Upgrade - -The custom gas token upgrade is not yet defined to be part of a particular network upgrade, but it will be scheduled -as part of a future hardfork. On the network upgrade block, a set of deposit transaction based upgrade transactions -are deterministically generated by the derivation pipeline in the following order: - -- L1 Attributes Transaction calling `setL1BlockValuesEcotone` -- User deposits from L1 -- Network Upgrade Transactions - - L1Block deployment - - L2CrossDomainMessenger deployment - - L2StandardBridge deployment - - Update L1Block Proxy ERC-1967 Implementation Slot - - Update L2CrossDomainMessenger Proxy ERC-1967 Implementation Slot - - Update L2StandardBridge Proxy ERC-1967 Implementation Slot - -The deployment transactions MUST have a `from` value that has no code and has no known -private key. This is to guarantee it cannot be frontrun and have its nonce modified. -If this was possible, then an attacker would be able to modify the address that the -implementation is deployed to because it is based on `CREATE` and not `CREATE2`. -This would then cause the proxy implementation set transactions to set an incorrect -implementation address, resulting in a bricked contract. The calldata is not generated -dynamically to enable deterministic upgrade transactions across all networks. - -The proxy upgrade transactions are from `address(0)` because the `Proxy` implementation -considers `address(0)` to be an admin. Going straight to the `Proxy` guarantees that -the upgrade will work because there is no guarantee that the `Proxy` is owned by the -`ProxyAdmin` and going through the `ProxyAdmin` would require stealing the identity -of its owner, which may be different on every chain. That would require adding L2 -RPC access to the derivation pipeline and make the upgrade transactions non deterministic. - -### L1Block Deployment - -- `from`: `0x4210000000000000000000000000000000000002` -- `to`: `null` -- `mint`: `0` -- `value`: `0` -- `gasLimit`: TODO -- `data`: TODO -- `sourceHash`: TODO - -### L2CrossDomainMessenger Deployment - -- `from`: `0x4210000000000000000000000000000000000003` -- `to`: `null` -- `mint`: `0` -- `value`: `0` -- `gasLimit`: `375,000` -- `data`: TODO -- `sourceHash`: TODO - -### L2StandardBridge Deployment - -- `from`: `0x4210000000000000000000000000000000000004` -- `to`: `null` -- `mint`: `0` -- `value`: `0` -- `gasLimit`: `375,000` -- `data`: TODO -- `sourceHash`: TODO - -### L1Block Proxy Update - -- `from`: `0x0000000000000000000000000000000000000000` -- `to`: `0x4200000000000000000000000000000000000015` -- `mint`: `0` -- `value`: `0` -- `gasLimit`: `50,000` -- `data`: TODO -- `sourceHash`: TODO - -### L2CrossDomainMessenger Proxy Update - -- `from`: `0x0000000000000000000000000000000000000000` -- `to`: `0x4200000000000000000000000000000000000007` -- `mint`: `0` -- `value`: `0` -- `gasLimit`: `50,000` -- `data`: TODO -- `sourceHash`: TODO - -### L2StandardBridge Proxy Update - -- `from`: `0x0000000000000000000000000000000000000000` -- `to`: `0x4200000000000000000000000000000000000010` -- `mint`: `0` -- `value`: `0` -- `gasLimit`: `50,000` -- `data`: TODO -- `sourceHash`: TODO - -## Selection of `ETHER_TOKEN_ADDRESS` - -It was decided to use `address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)` to represent `ether` to push -ecosystem standardization efforts around using this address to represent `ether` in [DeFi protocols][eip-7528] -or [APIs][execution-api-pr]. - -[eip-7528]: https://eips.ethereum.org/EIPS/eip-7528 -[execution-api-pr]: https://github.com/ethereum/execution-apis/pull/484 - -## Standard Config - -There is currently no strong definition of what it means to be part of the standard config when using -the OP Stack with custom gas token enabled. This will be defined in the future. - -## Fees - -The OP Stack natively charges fees in terms of ether due to the fee formula taking into account the basefee and -blobbasefee. When a custom gas token is used, fees are paid in the custom gas token but the conversion rate to ether -is not taken into account as part of the protocol. It is assumed that the fees will be configured by the chain -operator such that the revenue earned in custom gas token can be swapped into ether to pay for posting the data to L1. - -## Security Considerations - -### OptimismPortal Token Allowance - -The `OptimismPortal` makes calls on behalf of users. It is therefore unsafe to be able to call the address of -the custom gas token itself from the `OptimismPortal` because it would be a simple way to `approve` an attacker's -balance and steal the entire ERC20 token balance of the `OptimismPortal`. - -### Interoperability Support - -Interop is supported between chains even if they use different custom gas tokens. The token address and the number of -decimals are legible on chain. In the future we may add the ability to poke a chain such that it emits an event that -includes the custom gas token address and its number of decimals to easily be able to introspect on the native asset -of another chain. - -### Wrapped Ether - -The `WETH9` predeploy at `0x4200000000000000000000000000000000000006` represents wrapped native asset and -not wrapped `ether`. Portable and fungible `ether` across different domains is left for a future project. From 6473b988d901dff9bf0901a975c75849271072aa Mon Sep 17 00:00:00 2001 From: Hex <165055168+hexshire@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:14:17 -0300 Subject: [PATCH 02/17] feat(cgt): related specs Signed-off-by: Hex <165055168+hexshire@users.noreply.github.com> Co-authored-by: AgusDuha <81362284+agusduha@users.noreply.github.com> Co-authored-by: Joxes <91908708+Joxess@users.noreply.github.com> --- specs/interop/managed-node.md | 2 +- specs/protocol/jovian/bridges.md | 19 ++ specs/protocol/jovian/messengers.md | 13 + specs/protocol/jovian/optimism-portal.md | 42 +++ specs/protocol/jovian/overview.md | 45 +++ specs/protocol/jovian/predeploys.md | 337 +++++++++++++++++++++++ specs/protocol/jovian/system-config.md | 24 ++ specs/protocol/jovian/withdrawals.md | 13 + words.txt | 80 +++--- 9 files changed, 534 insertions(+), 41 deletions(-) create mode 100644 specs/protocol/jovian/bridges.md create mode 100644 specs/protocol/jovian/messengers.md create mode 100644 specs/protocol/jovian/optimism-portal.md create mode 100644 specs/protocol/jovian/predeploys.md create mode 100644 specs/protocol/jovian/system-config.md create mode 100644 specs/protocol/jovian/withdrawals.md diff --git a/specs/interop/managed-node.md b/specs/interop/managed-node.md index 162fd1d54..900a9be59 100644 --- a/specs/interop/managed-node.md +++ b/specs/interop/managed-node.md @@ -56,7 +56,7 @@ the file containing the jwt secret should be provided to the supervisor instance ## Node `->` Supervisor Events that a supervisor should subscribe to, originating from the node, handled by the supervisor. For the used types, -refer [this](#Types) section. +refer [this](#types) section. Every event sent from the node is of type `ManagedEvent` whose fields are populated with the events that occurred. All non-null events are sent at once. The other fields are omitted. diff --git a/specs/protocol/jovian/bridges.md b/specs/protocol/jovian/bridges.md new file mode 100644 index 000000000..a98dc9639 --- /dev/null +++ b/specs/protocol/jovian/bridges.md @@ -0,0 +1,19 @@ +# Bridges + + + +**Table of Contents** + +- [Overview](#overview) + + + +## Overview + +ETH bridging functions MUST revert when Custom Gas Token mode is enabled and the function involves ETH transfers. +This revert behavior is necessary because when a chain operates in Custom Gas Token mode, ETH is no longer the native +asset used for gas fees and transactions. The chain has shifted to using a different native asset entirely. +Allowing ETH transfers could create confusion about which asset serves as the native currency, potentially leading +to user errors and lost funds. Additionally, the custom gas token's supply is managed independently through +dedicated contracts (`NativeAssetLiquidity` and `LiquidityController`), and combining ETH bridging with custom gas +token operations introduces additional complexity to supply management and accounting. diff --git a/specs/protocol/jovian/messengers.md b/specs/protocol/jovian/messengers.md new file mode 100644 index 000000000..4617fd9f0 --- /dev/null +++ b/specs/protocol/jovian/messengers.md @@ -0,0 +1,13 @@ +# Cross Domain Messengers + + + +**Table of Contents** + +- [Message Passing](#message-passing) + + + +## Message Passing + +The `sendMessage` function MUST revert when Custom Gas Token mode is enabled and `msg.value > 0`. diff --git a/specs/protocol/jovian/optimism-portal.md b/specs/protocol/jovian/optimism-portal.md new file mode 100644 index 000000000..501936a75 --- /dev/null +++ b/specs/protocol/jovian/optimism-portal.md @@ -0,0 +1,42 @@ +# Optimism Portal + + + +**Table of Contents** + +- [Definitions](#definitions) + - [Custom Gas Token Flag](#custom-gas-token-flag) +- [Rationale](#rationale) +- [Function Specification](#function-specification) + - [isCustomGasToken](#iscustomgastoken) + - [donateETH](#donateeth) + - [depositTransaction](#deposittransaction) + + + +## Definitions + +### Custom Gas Token Flag + +The **Custom Gas Token Flag** (`isCustomGasToken`) is a boolean value that indicates +whether the chain is operating in Custom Gas Token mode. + +## Rationale + +The OptimismPortal's ETH-related logic must revert when Custom Gas Token mode is enabled to prevent ETH from +acting as the native asset. Since the client side does not discern native asset supply creation, allowing +ETH deposits would incorrectly imply that it can be minted in the chain. + +## Function Specification + +### isCustomGasToken + +Returns true if the gas token is a custom gas token, false otherwise. + +### donateETH + +- MUST revert if `isCustomGasToken()` returns `true` and `msg.value > 0`. + +### depositTransaction + +- MUST revert if `isCustomGasToken()` returns `true` and `msg.value > 0`. diff --git a/specs/protocol/jovian/overview.md b/specs/protocol/jovian/overview.md index 07194e8ef..0edc7c6e3 100644 --- a/specs/protocol/jovian/overview.md +++ b/specs/protocol/jovian/overview.md @@ -6,6 +6,9 @@ - [Execution Layer](#execution-layer) - [Consensus Layer](#consensus-layer) +- [Smart Contracts](#smart-contracts) + - [Core L2 Smart Contracts](#core-l2-smart-contracts) + - [Custom Gas Token](#custom-gas-token) @@ -14,3 +17,45 @@ This document is not finalized and should be considered experimental. ## Execution Layer ## Consensus Layer + +## Smart Contracts + +- [Predeploys](./predeploys.md) +- [Bridges](./bridges.md) +- [Cross Domain Messengers](./messengers.md) +- [System Config](./system-config.md) +- [Withdrawals](./withdrawals.md) +- [Optimism Portal](./optimism-portal.md) + +### Core L2 Smart Contracts + +#### Custom Gas Token + +The Custom Gas Token (CGT) feature allows OP Stack chains to use a native asset other than ETH as the gas +currency. This implementation introduces a streamlined approach with minimal core code intrusion through a +single `isCustomGasToken()` flag. + +Key components: + +- **NativeAssetLiquidity**: A predeploy contract containing pre-minted native assets, deployed only for + CGT-enabled chains. +- **LiquidityController**: An owner-governed mint/burn router that manages supply control, deployed only for + CGT-enabled chains. +- **ETH Transfer Blocking**: When CGT is enabled, all ETH transfer flows in bridging methods are disabled via + the `isCustomGasToken()` flag. +- **ETH Bridging Disabled**: ETH bridging functions in `L2ToL1MessagePasser` and `OptimismPortal` MUST revert + when CGT mode is enabled to prevent confusion about which asset is the native currency. +- **Native Asset Bridging**: Custom Gas Token chains use dedicated CGT bridges (`L1CGTBridge` and + `L2CGTBridge`) for native asset transfers between L1 ERC20 tokens and L2 native assets. +- **WETH as ERC20**: ETH can still be bridged as WETH using the standard `OptimismMintableERC20` bridging + path through `L2StandardBridge`. + +OP Stack chains that use a native asset other than ETH (or the native asset of the settlement layer) +introduce custom requirements that go beyond the current supply management model based on deposits and +withdrawals. This architecture decouples and winds down the native bridging for the native asset, shifting +the responsibility for supply management to the application layer. The chain operator becomes responsible +for defining and assigning meaning to the native asset, which is managed through a new set of predeployed +contracts. + +This approach preserves full alignment with EVM equivalence and client-side compatibility as provided by the +standard OP Stack. No new functionalities outside the execution environment are required to make it work. diff --git a/specs/protocol/jovian/predeploys.md b/specs/protocol/jovian/predeploys.md new file mode 100644 index 000000000..f0bc36898 --- /dev/null +++ b/specs/protocol/jovian/predeploys.md @@ -0,0 +1,337 @@ +# Predeploys + + + + +**Table of Contents** + +- [Overview](#overview) +- [WETH9](#weth9) +- [L1Block](#l1block) + - [Functions](#functions) + - [`setCustomGasToken`](#setcustomgastoken) +- [L2CrossDomainMessenger](#l2crossdomainmessenger) +- [L2StandardBridge](#l2standardbridge) +- [SequencerFeeVault](#sequencerfeevault) +- [BaseFeeVault](#basefeevault) +- [L1FeeVault](#l1feevault) +- [Native Asset Liquidity](#native-asset-liquidity) + - [Functions](#functions-1) + - [`deposit`](#deposit) + - [`withdraw`](#withdraw) + - [`fund`](#fund) + - [Events](#events) + - [`LiquidityDeposited`](#liquiditydeposited) + - [`LiquidityWithdrawn`](#liquiditywithdrawn) + - [`LiquidityFunded`](#liquidityfunded) + - [Invariants](#invariants) +- [Liquidity Controller](#liquidity-controller) + - [Functions](#functions-2) + - [`authorizeMinter`](#authorizeminter) + - [`mint`](#mint) + - [`burn`](#burn) + - [`gasPayingAssetName`](#gaspayingassetname) + - [`gasPayingAssetSymbol`](#gaspayingassetsymbol) + - [Events](#events-1) + - [`MinterAuthorized`](#minterauthorized) + - [`AssetsMinted`](#assetsminted) + - [`AssetsBurned`](#assetsburned) + - [Invariants](#invariants-1) + + + +## Overview + +| Name | Address | Introduced | Deprecated | Proxied | +| -------------------- | ------------------------------------------ | ---------- | ---------- | ------- | +| NativeAssetLiquidity | 0x4200000000000000000000000000000000000029 | Jovian | No | Yes | +| LiquidityController | 0x420000000000000000000000000000000000002A | Jovian | No | Yes | + +## WETH9 + +On chains using Custom Gas Token mode, this contract serves as the Wrapped Native +Asset (WNA) instead of Wrapped Ether. The WETH predeploy implementation remains +unchanged and continues to fetch metadata from the `L1Block` predeploy. However, +the `L1Block` predeploy is updated to source the name and symbol from the +`LiquidityController` instead of using hardcoded ETH values, with the name and +symbol being prefixed with "Wrapped" and "W" respectively. + +**Important**: Currently, a chain using ETH cannot migrate to use a custom gas token, +as this migration would introduce significant risks and breaking changes to existing +infrastructure. Custom Gas Token mode is only supported for fresh deployments. +Migration only focuses on chains utilizing the old CGT implementation. + +For fresh Custom Gas Token deployments, the `L1Block` is deployed from genesis +with the metadata functions already configured to fetch from the `LiquidityController`. + +## L1Block + +### Functions + +#### `setCustomGasToken` + +Sets the Custom Gas Token flag to `true`, enabling Custom Gas Token mode for the chain. + +```solidity +function setCustomGasToken() external +``` + +- MUST only be callable by the `DEPOSITOR_ACCOUNT` +- MUST set the internal `isCustomGasToken` flag to `true` +- MUST be callable only once per chain (the flag cannot be reverted to `false`) + +Once enabled, various predeploys will check `L1Block.isCustomGasToken()` to determine if ETH +bridging operations should be blocked. + +## L2CrossDomainMessenger + +The `sendMessage` function MUST revert if `L1Block.isCustomGasToken()` returns `true` and `msg.value > 0`. +This revert occurs because `L2CrossDomainMessenger` internally calls `L2ToL1MessagePasser.initiateWithdrawal` +which enforces the CGT restriction. + +## L2StandardBridge + +ETH bridging functions MUST revert if `L1Block.isCustomGasToken()` returns `true` and the function involves ETH transfers. +This revert occurs because `L2StandardBridge` internally calls `L2CrossDomainMessenger.sendMessage`, +which in turn calls `L2ToL1MessagePasser.initiateWithdrawal` that enforces the CGT restriction. + +## SequencerFeeVault + +The contract constructor takes a `WithdrawalNetwork` parameter that determines whether +funds are sent to L1 or L2: + +- `WithdrawalNetwork.L1`: Funds are withdrawn to an L1 address (default behavior) +- `WithdrawalNetwork.L2`: Funds are withdrawn to an L2 address + +For existing deployments, to change the withdrawal network or recipient address, +a new implementation must be deployed and the proxy must be upgraded. + +For fresh deployments with Custom Gas Token mode enabled, the withdrawal network +MUST be set to `WithdrawalNetwork.L2`. + +## BaseFeeVault + +The contract constructor takes a `WithdrawalNetwork` parameter that determines whether +funds are sent to L1 or L2: + +- `WithdrawalNetwork.L1`: Funds are withdrawn to an L1 address (default behavior) +- `WithdrawalNetwork.L2`: Funds are withdrawn to an L2 address + +For existing deployments, to change the withdrawal network or recipient address, +a new implementation must be deployed and the proxy must be upgraded. + +For fresh deployments with Custom Gas Token mode enabled, the withdrawal network +MUST be set to `WithdrawalNetwork.L2`. + +## L1FeeVault + +The contract constructor takes a `WithdrawalNetwork` parameter that determines whether +funds are sent to L1 or L2: + +- `WithdrawalNetwork.L1`: Funds are withdrawn to an L1 address (default behavior) +- `WithdrawalNetwork.L2`: Funds are withdrawn to an L2 address + +For existing deployments, to change the withdrawal network or recipient address, +a new implementation must be deployed and the proxy must be upgraded. + +For fresh deployments with Custom Gas Token mode enabled, the withdrawal network +MUST be set to `WithdrawalNetwork.L2`. + +## Native Asset Liquidity + +Address: `0x4200000000000000000000000000000000000029` + +The `NativeAssetLiquidity` predeploy stores a large amount of pre-minted native asset +that serves as the central liquidity source for Custom Gas Token chains. This contract +is only deployed on chains using Custom Gas Token mode and acts as the vault for all +native asset supply management operations. + +### Functions + +#### `deposit` + +Accepts native asset deposits and locks them in the contract, reducing circulating supply. + +```solidity +function deposit() external payable +``` + +- MUST only be callable by the `LiquidityController` predeploy +- MUST accept any amount of native asset via `msg.value` +- MUST emit `LiquidityDeposited` event + +#### `withdraw` + +Sends native assets from the contract to the `LiquidityController`, increasing circulating supply. + +```solidity +function withdraw(uint256 _amount) external +``` + +- MUST only be callable by the `LiquidityController` predeploy +- MUST send exactly `_amount` of native asset to the caller +- MUST revert if the contract balance is insufficient +- MUST emit `LiquidityWithdrawn` event + +#### `fund` + +Allows funding the contract with native assets. This function is used to initialize the contract with a large liquidity +pool, similar to how ETHLiquidity is initialized in interop chains. + +```solidity +function fund() external payable +``` + +- MUST accept any amount of native asset via `msg.value` +- MUST revert if `msg.value` is zero +- MUST emit `LiquidityFunded` event +- MUST be callable by any address + +### Events + +#### `LiquidityDeposited` + +Emitted when native assets are deposited into the contract. + +```solidity +event LiquidityDeposited(address indexed caller, uint256 value) +``` + +#### `LiquidityWithdrawn` + +Emitted when native assets are withdrawn from the contract. + +```solidity +event LiquidityWithdrawn(address indexed caller, uint256 value) +``` + +#### `LiquidityFunded` + +Emitted when the contract receives funding, typically during initial deployment. + +```solidity +event LiquidityFunded(address indexed funder, uint256 amount) +``` + +### Invariants + +- Only the `LiquidityController` predeploy can call `deposit()` and `withdraw()` +- All native asset supply changes must go through this contract when CGT mode is active +- No direct user interaction is permitted with liquidity management functions + +## Liquidity Controller + +Address: `0x420000000000000000000000000000000000002A` + +The `LiquidityController` predeploy manages access to the `NativeAssetLiquidity` contract +and provides the governance interface for Custom Gas Token chains. This contract is only +deployed on chains using Custom Gas Token mode and serves as the central authority for +native asset supply management and metadata provisioning. + +### Functions + +#### `authorizeMinter` + +Authorizes an address to mint native assets from the liquidity pool. + +```solidity +function authorizeMinter(address _minter) external +``` + +- MUST only be callable by the L1 ProxyAdmin owner +- MUST authorize `_minter` to call the `mint()` function +- MUST emit `MinterAuthorized` event + +#### `mint` + +Unlocks native assets from the `NativeAssetLiquidity` contract and sends them to a specified address. + +```solidity +function mint(address _to, uint256 _amount) external +``` + +- MUST only be callable by authorized minters +- MUST call `NativeAssetLiquidity.withdraw(_amount)` to unlock assets +- MUST send exactly `_amount` of native asset to `_to` address +- MUST revert if `NativeAssetLiquidity` has insufficient balance +- MUST emit `AssetsMinted` event + +#### `burn` + +Deposits native assets back into the `NativeAssetLiquidity` contract, reducing circulating supply. + +```solidity +function burn() external payable +``` + +- MUST accept any amount of native asset via `msg.value` +- MUST call `NativeAssetLiquidity.deposit{value: msg.value}()` to lock assets +- MUST only be callable by authorized minters +- MUST revert if `msg.value` is zero +- MUST emit `AssetsBurned` event + +#### `gasPayingAssetName` + +Returns the human-readable name of the gas-paying asset for metadata purposes. + +```solidity +function gasPayingAssetName() external view returns (string memory) +``` + +- MUST return the name of the native asset (e.g., "MyToken") +- MUST be used by L1Block predeploy for `name()` function +- Returns value used to construct "Wrapped {AssetName}" for WNA + +#### `gasPayingAssetSymbol` + +Returns the symbol of the gas-paying asset for metadata purposes. + +```solidity +function gasPayingAssetSymbol() external view returns (string memory) +``` + +- MUST return the symbol of the native asset (e.g., "MTK") +- MUST be used by L1Block predeploy for `symbol()` function +- Returns value used to construct "W{AssetSymbol}" for WNA + +### Events + +#### `MinterAuthorized` + +Emitted when a new minter is authorized by the contract owner. + +```solidity +event MinterAuthorized(address indexed minter, address indexed authorizer) +``` + +Where `minter` is the address being authorized and `authorizer` is the L1 ProxyAdmin owner who authorized them. + +#### `AssetsMinted` + +Emitted when native assets are unlocked from the liquidity pool and sent to a recipient. + +```solidity +event AssetsMinted(address indexed minter, address indexed to, uint256 amount) +``` + +Where `minter` is the authorized address calling the function, `to` is the recipient address, +and `amount` is the amount of native assets minted. + +#### `AssetsBurned` + +Emitted when native assets are locked back into the liquidity pool. + +```solidity +event AssetsBurned(address indexed burner, uint256 amount) +``` + +Where `burner` is the `msg.sender` who burned the assets and `amount` is the amount of native assets burned. + +### Invariants + +- Only authorized minters can call `mint()` to unlock native assets +- Only the L1 ProxyAdmin owner can authorize new minters via `authorizeMinter()` +- All native asset supply changes must flow through this contract's governance +- The contract acts as the sole interface between governance and `NativeAssetLiquidity` +- `burn()` operations always increase locked supply by calling `NativeAssetLiquidity.deposit()` +- `mint()` operations always decrease locked supply by calling `NativeAssetLiquidity.withdraw()` diff --git a/specs/protocol/jovian/system-config.md b/specs/protocol/jovian/system-config.md new file mode 100644 index 000000000..3319b2485 --- /dev/null +++ b/specs/protocol/jovian/system-config.md @@ -0,0 +1,24 @@ +# System Config + + + +**Table of Contents** + +- [Definitions](#definitions) + - [Custom Gas Token Flag](#custom-gas-token-flag) +- [Function Specification](#function-specification) + - [isCustomGasToken](#iscustomgastoken) + + + +## Definitions + +### Custom Gas Token Flag + +## Function Specification + +### isCustomGasToken + +Returns true if the gas token is a custom gas token, false otherwise. + +- MUST return the result of a call to `optimismPortal.isCustomGasToken()`. diff --git a/specs/protocol/jovian/withdrawals.md b/specs/protocol/jovian/withdrawals.md new file mode 100644 index 000000000..480d521e4 --- /dev/null +++ b/specs/protocol/jovian/withdrawals.md @@ -0,0 +1,13 @@ +# Withdrawals + + + +**Table of Contents** + +- [The L2ToL1MessagePasser Contract](#the-l2tol1messagepasser-contract) + + + +## The L2ToL1MessagePasser Contract + +The `initiateWithdrawal` function MUST revert if `L1Block.isCustomGasToken()` returns `true` and `msg.value > 0`. diff --git a/words.txt b/words.txt index 25585355a..d506eecde 100644 --- a/words.txt +++ b/words.txt @@ -1,26 +1,25 @@ - addi airgap altda -Basefee basefee +Basefee basefeevault basefeevaultconfig bijective -Bitfield bitfield +Bitfield bitlist bitlists -BLOBBASEFEE blobbasefee +BLOBBASEFEE blobdata -Blockhash blockhash +Blockhash blockheaders blockquotes -Blockspace blockspace +Blockspace blocksv blocktime Bootnode @@ -32,19 +31,19 @@ calldatas CALLVALUE celestia cellborder -CHAINID chainid +CHAINID checkpointed clabby codehash Coef configurables -CONFIGURATOR -Configurator configurator +Configurator +CONFIGURATOR counterfactually -Crosschain crosschain +Crosschain crossdomainmessenger crossdomainmessengeraddress cwia @@ -59,12 +58,12 @@ Dencun Depositer devnet dialable -Discv discv +Discv disincentivized disputegamefactory -DOCTOC doctoc +DOCTOC doubleword EBADF ecpairing @@ -74,21 +73,22 @@ elif ENDIANNESS errno fanout -Fastlz fastlz +Fastlz fcntl feevault feevaultconfig Filecoin FINDNODES flippin -Forkchoice forkchoice +Forkchoice FPAC FPVM -FUTEX -Futex +funder futex +Futex +FUTEX Futexes gaspayingtoken gaspayingtokenname @@ -98,8 +98,8 @@ GETFD GETFL gindex griefed -Hardfork hardfork +Hardfork hardforks hardfork’s Holesky @@ -119,17 +119,17 @@ IWANT justfile katex keccack -Keccak keccak -Libp +Keccak libp +Libp linkcheck mcache MCOPY mdbook Mdiamond -Merkle merkle +Merkle merkleized meshsub messagepasser @@ -141,8 +141,8 @@ mplex Multicall multiclient multisignature -Multistream multistream +Multistream nanosleep newrank nodehash @@ -153,32 +153,32 @@ OPCM opstack outputroot parseable -Pausability pausability +Pausability Pectra peerstore -PERMISSIONED -Permissioned permissioned +Permissioned +PERMISSIONED permissionless permissionlessly Precommitments preconfirmation preconfirmations -Predeploy predeploy -Predeployed +Predeploy predeployed -Predeploys +Predeployed predeploys -Preimage +Predeploys preimage +Preimage preimage's preimages -Preinstalls preinstalls -PRESTATE +Preinstalls prestate +PRESTATE PREVRANDAO protobuf protocolversion @@ -195,11 +195,11 @@ replayability replayable reposted returndata -Rollups rollups +Rollups runbooks -Sched sched +Sched secp SELFDESTRUCT sequencerfeevault @@ -212,38 +212,38 @@ standardbridgeaddress statelessly struct structs -Subdelegations subdelegations -Subgame +Subdelegations subgame -Subgames +Subgame subgames +Subgames sublinear SUPC -Superchain superchain +Superchain superfactory syscall -Syscalls syscalls +Syscalls systemconfig timelock tlsv triggerable tstore txdata -Typehash typehash +Typehash unaliased uncountered unfinalized uniprocessor unmetered unsafeblocksigner -Unsubmitted unsubmitted -UPGRADER +Unsubmitted upgrader +UPGRADER UPNP usecase userspace From ba023c5581b7eaaf07cffe101e4b41b2cac314cc Mon Sep 17 00:00:00 2001 From: hexshire Date: Tue, 19 Aug 2025 18:19:46 -0300 Subject: [PATCH 03/17] fix: run linter --- specs/protocol/jovian/predeploys.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/protocol/jovian/predeploys.md b/specs/protocol/jovian/predeploys.md index f0bc36898..e238794b6 100644 --- a/specs/protocol/jovian/predeploys.md +++ b/specs/protocol/jovian/predeploys.md @@ -2,7 +2,6 @@ - **Table of Contents** - [Overview](#overview) From 26df4a18ca14b329868bad7ae96446d01c78ed80 Mon Sep 17 00:00:00 2001 From: Hex <165055168+hexshire@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:31:30 -0300 Subject: [PATCH 04/17] chore: add deauthorize function --- specs/protocol/jovian/predeploys.md | 13 +++++++++++++ words.txt | 3 +++ 2 files changed, 16 insertions(+) diff --git a/specs/protocol/jovian/predeploys.md b/specs/protocol/jovian/predeploys.md index e238794b6..9ecf4a5fa 100644 --- a/specs/protocol/jovian/predeploys.md +++ b/specs/protocol/jovian/predeploys.md @@ -27,6 +27,7 @@ - [Liquidity Controller](#liquidity-controller) - [Functions](#functions-2) - [`authorizeMinter`](#authorizeminter) + - [`deauthorizeMinter`](#deauthorizeminter) - [`mint`](#mint) - [`burn`](#burn) - [`gasPayingAssetName`](#gaspayingassetname) @@ -241,6 +242,18 @@ function authorizeMinter(address _minter) external - MUST authorize `_minter` to call the `mint()` function - MUST emit `MinterAuthorized` event +#### `deauthorizeMinter` + +Deauthorizes an address from minting native assets from the liquidity pool. + +```solidity +function deauthorizeMinter(address _minter) external +``` + +- MUST only be callable by the L1 ProxyAdmin owner +- MUST deauthorize `_minter` from calling the `mint()` function +- MUST emit `MinterDeauthorized` event + #### `mint` Unlocks native assets from the `NativeAssetLiquidity` contract and sends them to a specified address. diff --git a/words.txt b/words.txt index d506eecde..1180c7b17 100644 --- a/words.txt +++ b/words.txt @@ -48,6 +48,9 @@ crossdomainmessenger crossdomainmessengeraddress cwia datas +deauthorize +Deauthorized +Deauthorizes Decentralizable delayedweth DELEGATECALL From a46d18a5f66db5caceb62fd987e2afc8f91389cd Mon Sep 17 00:00:00 2001 From: Hex <165055168+hexshire@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:44:50 -0300 Subject: [PATCH 05/17] chore: address review comments --- specs/protocol/jovian/optimism-portal.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/specs/protocol/jovian/optimism-portal.md b/specs/protocol/jovian/optimism-portal.md index 501936a75..2e333cc0b 100644 --- a/specs/protocol/jovian/optimism-portal.md +++ b/specs/protocol/jovian/optimism-portal.md @@ -11,6 +11,7 @@ - [isCustomGasToken](#iscustomgastoken) - [donateETH](#donateeth) - [depositTransaction](#deposittransaction) +- [Security Considerations](#security-considerations) @@ -19,7 +20,9 @@ ### Custom Gas Token Flag The **Custom Gas Token Flag** (`isCustomGasToken`) is a boolean value that indicates -whether the chain is operating in Custom Gas Token mode. +whether the chain is operating in Custom Gas Token mode. This flag is set during the +`initialize` function call by directly passing the `_isCustomGasToken` boolean parameter, +which is then assigned to the contract's `isCustomGasToken` state variable. ## Rationale @@ -40,3 +43,14 @@ Returns true if the gas token is a custom gas token, false otherwise. ### depositTransaction - MUST revert if `isCustomGasToken()` returns `true` and `msg.value > 0`. + +## Security Considerations + +### Custom Gas Token Flag Immutability + +Once the `isCustomGasToken` flag is set to `true` during initialization, it should not be +changed back to `false` in subsequent reinitializations. Changing from custom gas token mode +back to ETH mode could create inconsistencies in the chain's gas token handling and +potentially lead to security vulnerabilities. The flag represents a fundamental configuration +of the chain's gas token mechanism and should remain consistent throughout the chain's +lifetime. From 101df4dd199aa9ff7344c801abd75a8c50a56153 Mon Sep 17 00:00:00 2001 From: Hex <165055168+hexshire@users.noreply.github.com> Date: Wed, 3 Sep 2025 09:44:32 -0300 Subject: [PATCH 06/17] chore: cgt review comments 2 Signed-off-by: Hex <165055168+hexshire@users.noreply.github.com> Co-authored-by: Joxes <91908708+Joxess@users.noreply.github.com> --- .../{jovian => custom-gas-token}/bridges.md | 0 .../messengers.md | 0 .../optimism-portal.md | 1 + specs/protocol/custom-gas-token/overview.md | 59 +++++++++++++++++++ .../predeploys.md | 0 .../system-config.md | 0 .../withdrawals.md | 0 specs/protocol/jovian/overview.md | 45 -------------- 8 files changed, 60 insertions(+), 45 deletions(-) rename specs/protocol/{jovian => custom-gas-token}/bridges.md (100%) rename specs/protocol/{jovian => custom-gas-token}/messengers.md (100%) rename specs/protocol/{jovian => custom-gas-token}/optimism-portal.md (96%) create mode 100644 specs/protocol/custom-gas-token/overview.md rename specs/protocol/{jovian => custom-gas-token}/predeploys.md (100%) rename specs/protocol/{jovian => custom-gas-token}/system-config.md (100%) rename specs/protocol/{jovian => custom-gas-token}/withdrawals.md (100%) diff --git a/specs/protocol/jovian/bridges.md b/specs/protocol/custom-gas-token/bridges.md similarity index 100% rename from specs/protocol/jovian/bridges.md rename to specs/protocol/custom-gas-token/bridges.md diff --git a/specs/protocol/jovian/messengers.md b/specs/protocol/custom-gas-token/messengers.md similarity index 100% rename from specs/protocol/jovian/messengers.md rename to specs/protocol/custom-gas-token/messengers.md diff --git a/specs/protocol/jovian/optimism-portal.md b/specs/protocol/custom-gas-token/optimism-portal.md similarity index 96% rename from specs/protocol/jovian/optimism-portal.md rename to specs/protocol/custom-gas-token/optimism-portal.md index 2e333cc0b..711b1b63b 100644 --- a/specs/protocol/jovian/optimism-portal.md +++ b/specs/protocol/custom-gas-token/optimism-portal.md @@ -12,6 +12,7 @@ - [donateETH](#donateeth) - [depositTransaction](#deposittransaction) - [Security Considerations](#security-considerations) + - [Custom Gas Token Flag Immutability](#custom-gas-token-flag-immutability) diff --git a/specs/protocol/custom-gas-token/overview.md b/specs/protocol/custom-gas-token/overview.md new file mode 100644 index 000000000..a6462b8c4 --- /dev/null +++ b/specs/protocol/custom-gas-token/overview.md @@ -0,0 +1,59 @@ +# Custom Gas Token Mode + + + + +**Table of Contents** + +- [Execution Layer](#execution-layer) +- [Consensus Layer](#consensus-layer) +- [Smart Contracts](#smart-contracts) + - [Core L2 Smart Contracts](#core-l2-smart-contracts) + - [Custom Gas Token](#custom-gas-token) + + + +This document is not finalized and should be considered experimental. + +## Execution Layer + +## Consensus Layer + +## Smart Contracts + +- [Predeploys](./predeploys.md) +- [Bridges](./bridges.md) +- [Cross Domain Messengers](./messengers.md) +- [System Config](./system-config.md) +- [Withdrawals](./withdrawals.md) +- [Optimism Portal](./optimism-portal.md) + +### Core L2 Smart Contracts + +#### Custom Gas Token + +The Custom Gas Token (CGT) feature allows OP Stack chains to use a native asset other than ETH as the gas +currency. This implementation introduces a streamlined approach with minimal core code intrusion through a +single `isCustomGasToken()` flag. + +Key components: + +- **NativeAssetLiquidity**: A predeploy contract containing pre-minted native assets, deployed only for + CGT-enabled chains. +- **LiquidityController**: An owner-governed mint/burn router that manages supply control, deployed only for + CGT-enabled chains. +- **ETH Transfer Blocking**: When CGT is enabled, all ETH transfer flows in bridging methods are disabled via + the `isCustomGasToken()` flag. +- **ETH Bridging Disabled**: ETH bridging functions in `L2ToL1MessagePasser` and `OptimismPortal` MUST revert + when CGT mode is enabled to prevent confusion about which asset is the native currency. +- **ETH as an ERC20 representation**: ETH can be bridged by wrapping it as an ERC20 token (e.g., WETH) and using the `StandardBridge` to mint an `OptimismMintableERC20` representation. + +OP Stack chains that use a native asset other than ETH (or the native asset of the settlement layer) +introduce custom requirements that go beyond the current supply management model based on deposits and +withdrawals. This architecture decouples and winds down the native bridging for the native asset, shifting +the responsibility for supply management to the application layer. The chain operator becomes responsible +for defining and assigning meaning to the native asset, which is managed through a new set of predeployed +contracts. + +This approach preserves full alignment with EVM equivalence and client-side compatibility as provided by the +standard OP Stack. No new functionalities outside the execution environment are required to make it work. diff --git a/specs/protocol/jovian/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md similarity index 100% rename from specs/protocol/jovian/predeploys.md rename to specs/protocol/custom-gas-token/predeploys.md diff --git a/specs/protocol/jovian/system-config.md b/specs/protocol/custom-gas-token/system-config.md similarity index 100% rename from specs/protocol/jovian/system-config.md rename to specs/protocol/custom-gas-token/system-config.md diff --git a/specs/protocol/jovian/withdrawals.md b/specs/protocol/custom-gas-token/withdrawals.md similarity index 100% rename from specs/protocol/jovian/withdrawals.md rename to specs/protocol/custom-gas-token/withdrawals.md diff --git a/specs/protocol/jovian/overview.md b/specs/protocol/jovian/overview.md index 0edc7c6e3..07194e8ef 100644 --- a/specs/protocol/jovian/overview.md +++ b/specs/protocol/jovian/overview.md @@ -6,9 +6,6 @@ - [Execution Layer](#execution-layer) - [Consensus Layer](#consensus-layer) -- [Smart Contracts](#smart-contracts) - - [Core L2 Smart Contracts](#core-l2-smart-contracts) - - [Custom Gas Token](#custom-gas-token) @@ -17,45 +14,3 @@ This document is not finalized and should be considered experimental. ## Execution Layer ## Consensus Layer - -## Smart Contracts - -- [Predeploys](./predeploys.md) -- [Bridges](./bridges.md) -- [Cross Domain Messengers](./messengers.md) -- [System Config](./system-config.md) -- [Withdrawals](./withdrawals.md) -- [Optimism Portal](./optimism-portal.md) - -### Core L2 Smart Contracts - -#### Custom Gas Token - -The Custom Gas Token (CGT) feature allows OP Stack chains to use a native asset other than ETH as the gas -currency. This implementation introduces a streamlined approach with minimal core code intrusion through a -single `isCustomGasToken()` flag. - -Key components: - -- **NativeAssetLiquidity**: A predeploy contract containing pre-minted native assets, deployed only for - CGT-enabled chains. -- **LiquidityController**: An owner-governed mint/burn router that manages supply control, deployed only for - CGT-enabled chains. -- **ETH Transfer Blocking**: When CGT is enabled, all ETH transfer flows in bridging methods are disabled via - the `isCustomGasToken()` flag. -- **ETH Bridging Disabled**: ETH bridging functions in `L2ToL1MessagePasser` and `OptimismPortal` MUST revert - when CGT mode is enabled to prevent confusion about which asset is the native currency. -- **Native Asset Bridging**: Custom Gas Token chains use dedicated CGT bridges (`L1CGTBridge` and - `L2CGTBridge`) for native asset transfers between L1 ERC20 tokens and L2 native assets. -- **WETH as ERC20**: ETH can still be bridged as WETH using the standard `OptimismMintableERC20` bridging - path through `L2StandardBridge`. - -OP Stack chains that use a native asset other than ETH (or the native asset of the settlement layer) -introduce custom requirements that go beyond the current supply management model based on deposits and -withdrawals. This architecture decouples and winds down the native bridging for the native asset, shifting -the responsibility for supply management to the application layer. The chain operator becomes responsible -for defining and assigning meaning to the native asset, which is managed through a new set of predeployed -contracts. - -This approach preserves full alignment with EVM equivalence and client-side compatibility as provided by the -standard OP Stack. No new functionalities outside the execution environment are required to make it work. From 6761a31a6955cafb70f2c1c5b677f482e770486d Mon Sep 17 00:00:00 2001 From: hexshire Date: Wed, 3 Sep 2025 09:58:31 -0300 Subject: [PATCH 07/17] chore(cgt): run checks and linters --- specs/protocol/custom-gas-token/overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/protocol/custom-gas-token/overview.md b/specs/protocol/custom-gas-token/overview.md index a6462b8c4..af9117039 100644 --- a/specs/protocol/custom-gas-token/overview.md +++ b/specs/protocol/custom-gas-token/overview.md @@ -2,7 +2,6 @@ - **Table of Contents** - [Execution Layer](#execution-layer) @@ -46,7 +45,8 @@ Key components: the `isCustomGasToken()` flag. - **ETH Bridging Disabled**: ETH bridging functions in `L2ToL1MessagePasser` and `OptimismPortal` MUST revert when CGT mode is enabled to prevent confusion about which asset is the native currency. -- **ETH as an ERC20 representation**: ETH can be bridged by wrapping it as an ERC20 token (e.g., WETH) and using the `StandardBridge` to mint an `OptimismMintableERC20` representation. +- **ETH as an ERC20 representation**: ETH can be bridged by wrapping it as an ERC20 token (e.g., WETH) and using + the `StandardBridge` to mint an `OptimismMintableERC20` representation. OP Stack chains that use a native asset other than ETH (or the native asset of the settlement layer) introduce custom requirements that go beyond the current supply management model based on deposits and From 01a542c128a3de372ceba17b6ab96d2898a097ac Mon Sep 17 00:00:00 2001 From: hexshire Date: Wed, 3 Sep 2025 10:05:54 -0300 Subject: [PATCH 08/17] chore: add deauthorize details --- specs/protocol/custom-gas-token/predeploys.md | 11 +++++++++++ words.txt | 2 ++ 2 files changed, 13 insertions(+) diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index 9ecf4a5fa..1d1144f24 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -34,6 +34,7 @@ - [`gasPayingAssetSymbol`](#gaspayingassetsymbol) - [Events](#events-1) - [`MinterAuthorized`](#minterauthorized) + - [`MinterDeauthorized`](#minterdeauthorized) - [`AssetsMinted`](#assetsminted) - [`AssetsBurned`](#assetsburned) - [Invariants](#invariants-1) @@ -318,6 +319,16 @@ event MinterAuthorized(address indexed minter, address indexed authorizer) Where `minter` is the address being authorized and `authorizer` is the L1 ProxyAdmin owner who authorized them. +#### `MinterDeauthorized` + +Emitted when a minter is deauthorized by the contract owner. + +```solidity +event MinterDeauthorized(address indexed minter, address indexed deauthorizer) +``` + +Where `minter` is the address being deauthorized and `deauthorizer` is the L1 ProxyAdmin owner who deauthorized them. + #### `AssetsMinted` Emitted when native assets are unlocked from the liquidity pool and sent to a recipient. diff --git a/words.txt b/words.txt index 31d2d4d4c..c0cd13acd 100644 --- a/words.txt +++ b/words.txt @@ -47,7 +47,9 @@ cwia CWIA datas deauthorize +deauthorized Deauthorized +deauthorizer Deauthorizes Decentralizable delayedweth From 2322526a656ed2036890914857e00d63ef92f0d6 Mon Sep 17 00:00:00 2001 From: hexshire Date: Wed, 3 Sep 2025 13:18:52 -0300 Subject: [PATCH 09/17] chore: add missing words --- words.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/words.txt b/words.txt index c0cd13acd..c27cd24eb 100644 --- a/words.txt +++ b/words.txt @@ -21,6 +21,7 @@ Blockspace blocksv blocktime Bootnode +Brotli bridgeaddress bytestring calldatas @@ -47,6 +48,8 @@ cwia CWIA datas deauthorize +doctoc +DOCTOC deauthorized Deauthorized deauthorizer @@ -62,6 +65,7 @@ Dencun Depositer devnet dialable +disincentivized discv Discv disputegamefactory @@ -71,6 +75,7 @@ ecpairing Edgington eigenda elif +ENDIANNESS errno failovers fanout @@ -187,8 +192,10 @@ prestate Prestate PRESTATE PREVRANDAO +protobuf protocolversion protolambda +pubkey randao Randao RANDAO @@ -220,6 +227,8 @@ Solana standardbridge standardbridgeaddress statelessly +struct +structs subgame Subgame subgames From 39efa5ed78750c0b1bedef3a846df28ed8891958 Mon Sep 17 00:00:00 2001 From: Ashitaka Date: Mon, 22 Sep 2025 15:05:26 -0300 Subject: [PATCH 10/17] fix: update with the latest features --- specs/protocol/custom-gas-token/overview.md | 57 ++++++++++++++++++- specs/protocol/custom-gas-token/predeploys.md | 11 +++- .../custom-gas-token/system-config.md | 27 +++++++-- .../protocol/custom-gas-token/withdrawals.md | 3 +- 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/specs/protocol/custom-gas-token/overview.md b/specs/protocol/custom-gas-token/overview.md index a6462b8c4..9f9ee4657 100644 --- a/specs/protocol/custom-gas-token/overview.md +++ b/specs/protocol/custom-gas-token/overview.md @@ -10,6 +10,10 @@ - [Smart Contracts](#smart-contracts) - [Core L2 Smart Contracts](#core-l2-smart-contracts) - [Custom Gas Token](#custom-gas-token) +- [Feature Flag System](#feature-flag-system) +- [Development and Configuration](#development-and-configuration) + - [Fresh Deployments](#fresh-deployments) + - [Migration from Existing Implementation](#migration-from-existing-implementation) @@ -17,8 +21,16 @@ This document is not finalized and should be considered experimental. ## Execution Layer +Custom Gas Token mode operates at the execution layer by modifying the behavior of predeploy contracts +and bridging functions. The implementation uses a bitmap-based feature flag approach that enables CGT functionality +through the `systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)` check across various system contracts. + ## Consensus Layer +CGT mode does not require consensus layer changes. The native asset used for gas fees is managed +entirely at the execution layer through smart contract logic, maintaining full compatibility with +the existing consensus mechanisms. + ## Smart Contracts - [Predeploys](./predeploys.md) @@ -39,11 +51,11 @@ single `isCustomGasToken()` flag. Key components: - **NativeAssetLiquidity**: A predeploy contract containing pre-minted native assets, deployed only for - CGT-enabled chains. + CGT-enabled chains. The amount of the pre-minted liquidity is configurable with uint248 as max. - **LiquidityController**: An owner-governed mint/burn router that manages supply control, deployed only for CGT-enabled chains. - **ETH Transfer Blocking**: When CGT is enabled, all ETH transfer flows in bridging methods are disabled via - the `isCustomGasToken()` flag. + the `systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)` check. - **ETH Bridging Disabled**: ETH bridging functions in `L2ToL1MessagePasser` and `OptimismPortal` MUST revert when CGT mode is enabled to prevent confusion about which asset is the native currency. - **ETH as an ERC20 representation**: ETH can be bridged by wrapping it as an ERC20 token (e.g., WETH) and using the `StandardBridge` to mint an `OptimismMintableERC20` representation. @@ -57,3 +69,44 @@ contracts. This approach preserves full alignment with EVM equivalence and client-side compatibility as provided by the standard OP Stack. No new functionalities outside the execution environment are required to make it work. + +## Feature Flag System + +### Bitmap-based Feature Detection + +The CGT implementation uses a bitmap-based feature flag system that provides efficient storage and allows +multiple features to be tracked in a single storage slot. + +**Implementation:** + +- Uses `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` bitmap approach +- Enables efficient checking of multiple features +- Provides gas-optimized feature detection across contracts + +### Feature Checking + +Contracts check for CGT mode by querying the `SystemConfig` contract: + +```solidity +systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN) +``` + +The `SystemConfig` contains the `isFeatureEnabled` mapping that provides a unified interface for feature detection across all system contracts. + +## Development and Configuration + +### Fresh Deployments + +For fresh CGT deployments, the following configuration process is used: + +1. **L2 Genesis Configuration**: The initial liquidity amount is configured during genesis via `l2genesis.s.sol` +2. **Contract Initialization**: `NativeAssetLiquidity` and `LiquidityController` contracts are deployed with proxy support +3. **Feature Flag Setting**: The `Features.CUSTOM_GAS_TOKEN` bitmap is enabled to activate CGT mode across all relevant predeploys + +### Migration from Existing Implementation + +For chains migrating from older CGT implementations: + +1. **Contract Funding**: Use the `fund()` function on `NativeAssetLiquidity` to provide initial liquidity +2. **Liquidity Limits**: Ensure funded amounts do not exceed the configured native asset liquidity amount +3. **Compatibility**: Migration is only supported from previous CGT implementations, not from ETH-based chains diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index 9ecf4a5fa..48ebd3709 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -2,6 +2,7 @@ + **Table of Contents** - [Overview](#overview) @@ -80,18 +81,18 @@ function setCustomGasToken() external - MUST set the internal `isCustomGasToken` flag to `true` - MUST be callable only once per chain (the flag cannot be reverted to `false`) -Once enabled, various predeploys will check `L1Block.isCustomGasToken()` to determine if ETH +Once enabled, various predeploys will check `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` to determine if ETH bridging operations should be blocked. ## L2CrossDomainMessenger -The `sendMessage` function MUST revert if `L1Block.isCustomGasToken()` returns `true` and `msg.value > 0`. +The `sendMessage` function MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` is `true` and `msg.value > 0`. This revert occurs because `L2CrossDomainMessenger` internally calls `L2ToL1MessagePasser.initiateWithdrawal` which enforces the CGT restriction. ## L2StandardBridge -ETH bridging functions MUST revert if `L1Block.isCustomGasToken()` returns `true` and the function involves ETH transfers. +ETH bridging functions MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` is `true` and the function involves ETH transfers. This revert occurs because `L2StandardBridge` internally calls `L2CrossDomainMessenger.sendMessage`, which in turn calls `L2ToL1MessagePasser.initiateWithdrawal` that enforces the CGT restriction. @@ -187,6 +188,9 @@ function fund() external payable - MUST emit `LiquidityFunded` event - MUST be callable by any address +**Important for CGT Migration**: When migrating to Custom Gas Token mode, the total amount funded through this function +should not exceed the `nativeAssetLiquidity` amount configured during chain setup. Exceeding this amount may cause liquidity problems. + ### Events #### `LiquidityDeposited` @@ -218,6 +222,7 @@ event LiquidityFunded(address indexed funder, uint256 amount) - Only the `LiquidityController` predeploy can call `deposit()` and `withdraw()` - All native asset supply changes must go through this contract when CGT mode is active - No direct user interaction is permitted with liquidity management functions +- For CGT migrations: `fund()` amounts should not exceed the configured `nativeAssetLiquidity` amount to maintain consistency ## Liquidity Controller diff --git a/specs/protocol/custom-gas-token/system-config.md b/specs/protocol/custom-gas-token/system-config.md index 3319b2485..74280ee53 100644 --- a/specs/protocol/custom-gas-token/system-config.md +++ b/specs/protocol/custom-gas-token/system-config.md @@ -2,23 +2,38 @@ + **Table of Contents** - [Definitions](#definitions) - - [Custom Gas Token Flag](#custom-gas-token-flag) + - [Feature Flag Bitmap](#feature-flag-bitmap) - [Function Specification](#function-specification) - - [isCustomGasToken](#iscustomgastoken) + - [isFeatureEnabled](#isfeatureenabled) ## Definitions -### Custom Gas Token Flag +### Feature Flag Bitmap + +The `SystemConfig` contract contains a mapping `isFeatureEnabled` that uses bitmaps to efficiently track multiple features including Custom Gas Token mode. + +```solidity +mapping(uint256 => bool) public isFeatureEnabled; +``` ## Function Specification -### isCustomGasToken +### isFeatureEnabled + +Returns true if the specified feature is enabled, false otherwise. + +```solidity +function isFeatureEnabled(uint256 _feature) external view returns (bool) +``` + +For Custom Gas Token mode, contracts check: -Returns true if the gas token is a custom gas token, false otherwise. +- `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` -- MUST return the result of a call to `optimismPortal.isCustomGasToken()`. +This provides an efficient way to query feature states across the system. diff --git a/specs/protocol/custom-gas-token/withdrawals.md b/specs/protocol/custom-gas-token/withdrawals.md index 480d521e4..8e946c6f3 100644 --- a/specs/protocol/custom-gas-token/withdrawals.md +++ b/specs/protocol/custom-gas-token/withdrawals.md @@ -2,6 +2,7 @@ + **Table of Contents** - [The L2ToL1MessagePasser Contract](#the-l2tol1messagepasser-contract) @@ -10,4 +11,4 @@ ## The L2ToL1MessagePasser Contract -The `initiateWithdrawal` function MUST revert if `L1Block.isCustomGasToken()` returns `true` and `msg.value > 0`. +The `initiateWithdrawal` function MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` is `true` and `msg.value > 0`. From 025eb38b01fc5726ad050d2d034830b555125ad7 Mon Sep 17 00:00:00 2001 From: Ashitaka Date: Tue, 23 Sep 2025 14:28:18 -0300 Subject: [PATCH 11/17] fix: comments --- specs/protocol/custom-gas-token/overview.md | 26 +------------------ specs/protocol/custom-gas-token/predeploys.md | 6 ++--- .../custom-gas-token/system-config.md | 24 +++++++++++++++++ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/specs/protocol/custom-gas-token/overview.md b/specs/protocol/custom-gas-token/overview.md index 9f9ee4657..f285f90b1 100644 --- a/specs/protocol/custom-gas-token/overview.md +++ b/specs/protocol/custom-gas-token/overview.md @@ -10,7 +10,6 @@ - [Smart Contracts](#smart-contracts) - [Core L2 Smart Contracts](#core-l2-smart-contracts) - [Custom Gas Token](#custom-gas-token) -- [Feature Flag System](#feature-flag-system) - [Development and Configuration](#development-and-configuration) - [Fresh Deployments](#fresh-deployments) - [Migration from Existing Implementation](#migration-from-existing-implementation) @@ -70,29 +69,6 @@ contracts. This approach preserves full alignment with EVM equivalence and client-side compatibility as provided by the standard OP Stack. No new functionalities outside the execution environment are required to make it work. -## Feature Flag System - -### Bitmap-based Feature Detection - -The CGT implementation uses a bitmap-based feature flag system that provides efficient storage and allows -multiple features to be tracked in a single storage slot. - -**Implementation:** - -- Uses `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` bitmap approach -- Enables efficient checking of multiple features -- Provides gas-optimized feature detection across contracts - -### Feature Checking - -Contracts check for CGT mode by querying the `SystemConfig` contract: - -```solidity -systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN) -``` - -The `SystemConfig` contains the `isFeatureEnabled` mapping that provides a unified interface for feature detection across all system contracts. - ## Development and Configuration ### Fresh Deployments @@ -101,7 +77,7 @@ For fresh CGT deployments, the following configuration process is used: 1. **L2 Genesis Configuration**: The initial liquidity amount is configured during genesis via `l2genesis.s.sol` 2. **Contract Initialization**: `NativeAssetLiquidity` and `LiquidityController` contracts are deployed with proxy support -3. **Feature Flag Setting**: The `Features.CUSTOM_GAS_TOKEN` bitmap is enabled to activate CGT mode across all relevant predeploys +3. **Feature Flag Setting**: The `Features.CUSTOM_GAS_TOKEN` bitmap is enabled to activate CGT mode across all relevant predeploys (see [System Config](./system-config.md) for details on feature flags) ### Migration from Existing Implementation diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index 48ebd3709..489492149 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -81,18 +81,18 @@ function setCustomGasToken() external - MUST set the internal `isCustomGasToken` flag to `true` - MUST be callable only once per chain (the flag cannot be reverted to `false`) -Once enabled, various predeploys will check `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` to determine if ETH +Once enabled, various predeploys will check `L1Block.isCustomGasToken()` to determine if ETH bridging operations should be blocked. ## L2CrossDomainMessenger -The `sendMessage` function MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` is `true` and `msg.value > 0`. +The `sendMessage` function MUST revert if `L1Block.isCustomGasToken()` returns `true` and `msg.value > 0`. This revert occurs because `L2CrossDomainMessenger` internally calls `L2ToL1MessagePasser.initiateWithdrawal` which enforces the CGT restriction. ## L2StandardBridge -ETH bridging functions MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` is `true` and the function involves ETH transfers. +ETH bridging functions MUST revert if `L1Block.isCustomGasToken()` returns `true` and the function involves ETH transfers. This revert occurs because `L2StandardBridge` internally calls `L2CrossDomainMessenger.sendMessage`, which in turn calls `L2ToL1MessagePasser.initiateWithdrawal` that enforces the CGT restriction. diff --git a/specs/protocol/custom-gas-token/system-config.md b/specs/protocol/custom-gas-token/system-config.md index 74280ee53..b1b1a5d91 100644 --- a/specs/protocol/custom-gas-token/system-config.md +++ b/specs/protocol/custom-gas-token/system-config.md @@ -5,6 +5,9 @@ **Table of Contents** +- [Feature Flag System](#feature-flag-system) + - [Bitmap-based Feature Detection](#bitmap-based-feature-detection) + - [Feature Checking](#feature-checking) - [Definitions](#definitions) - [Feature Flag Bitmap](#feature-flag-bitmap) - [Function Specification](#function-specification) @@ -12,6 +15,27 @@ +## Feature Flag System + +### Bitmap-based Feature Detection + +The CGT implementation uses a bitmap-based feature flag system that provides efficient storage and allows +multiple features to be tracked in a single storage slot. + +**Implementation:** + +- Uses `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` bitmap approach +- Enables efficient checking of multiple features +- Provides gas-optimized feature detection across contracts + +### Feature Checking + +Contracts check for CGT mode by querying the `SystemConfig` contract: + +```solidity +systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN) +``` + ## Definitions ### Feature Flag Bitmap From 28e338856fb2d26f4c1028ad40b4b830141290c8 Mon Sep 17 00:00:00 2001 From: Ashitaka Date: Wed, 24 Sep 2025 13:55:00 -0300 Subject: [PATCH 12/17] fix: comments --- specs/protocol/custom-gas-token/overview.md | 8 +++++--- specs/protocol/custom-gas-token/predeploys.md | 3 ++- specs/protocol/custom-gas-token/system-config.md | 3 ++- specs/protocol/custom-gas-token/withdrawals.md | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/specs/protocol/custom-gas-token/overview.md b/specs/protocol/custom-gas-token/overview.md index f285f90b1..71c212ab0 100644 --- a/specs/protocol/custom-gas-token/overview.md +++ b/specs/protocol/custom-gas-token/overview.md @@ -50,14 +50,15 @@ single `isCustomGasToken()` flag. Key components: - **NativeAssetLiquidity**: A predeploy contract containing pre-minted native assets, deployed only for - CGT-enabled chains. The amount of the pre-minted liquidity is configurable with uint248 as max. + CGT-enabled chains. The amount of the pre-minted liquidity is configurable up to `type(uint248).max`. - **LiquidityController**: An owner-governed mint/burn router that manages supply control, deployed only for CGT-enabled chains. - **ETH Transfer Blocking**: When CGT is enabled, all ETH transfer flows in bridging methods are disabled via the `systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)` check. - **ETH Bridging Disabled**: ETH bridging functions in `L2ToL1MessagePasser` and `OptimismPortal` MUST revert when CGT mode is enabled to prevent confusion about which asset is the native currency. -- **ETH as an ERC20 representation**: ETH can be bridged by wrapping it as an ERC20 token (e.g., WETH) and using the `StandardBridge` to mint an `OptimismMintableERC20` representation. +- **ETH as an ERC20 representation**: ETH can be bridged by wrapping it as an ERC20 token (e.g., WETH) + and using the `StandardBridge` to mint an `OptimismMintableERC20` representation. OP Stack chains that use a native asset other than ETH (or the native asset of the settlement layer) introduce custom requirements that go beyond the current supply management model based on deposits and @@ -77,7 +78,8 @@ For fresh CGT deployments, the following configuration process is used: 1. **L2 Genesis Configuration**: The initial liquidity amount is configured during genesis via `l2genesis.s.sol` 2. **Contract Initialization**: `NativeAssetLiquidity` and `LiquidityController` contracts are deployed with proxy support -3. **Feature Flag Setting**: The `Features.CUSTOM_GAS_TOKEN` bitmap is enabled to activate CGT mode across all relevant predeploys (see [System Config](./system-config.md) for details on feature flags) +3. **Feature Flag Setting**: The `Features.CUSTOM_GAS_TOKEN` bitmap is enabled + to activate CGT mode across all relevant predeploys (see [System Config](./system-config.md) for details on feature flags) ### Migration from Existing Implementation diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index 489492149..724770e11 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -189,7 +189,8 @@ function fund() external payable - MUST be callable by any address **Important for CGT Migration**: When migrating to Custom Gas Token mode, the total amount funded through this function -should not exceed the `nativeAssetLiquidity` amount configured during chain setup. Exceeding this amount may cause liquidity problems. +should not exceed the `nativeAssetLiquidity` amount configured during chain setup. +Exceeding this amount may cause liquidity problems. ### Events diff --git a/specs/protocol/custom-gas-token/system-config.md b/specs/protocol/custom-gas-token/system-config.md index b1b1a5d91..fc22b8899 100644 --- a/specs/protocol/custom-gas-token/system-config.md +++ b/specs/protocol/custom-gas-token/system-config.md @@ -40,7 +40,8 @@ systemConfig.isFeatureEnabled(Features.CUSTOM_GAS_TOKEN) ### Feature Flag Bitmap -The `SystemConfig` contract contains a mapping `isFeatureEnabled` that uses bitmaps to efficiently track multiple features including Custom Gas Token mode. +The `SystemConfig` contract contains a mapping `isFeatureEnabled` that uses bitmaps to efficiently +track multiple features including Custom Gas Token mode. ```solidity mapping(uint256 => bool) public isFeatureEnabled; diff --git a/specs/protocol/custom-gas-token/withdrawals.md b/specs/protocol/custom-gas-token/withdrawals.md index 8e946c6f3..523a37c30 100644 --- a/specs/protocol/custom-gas-token/withdrawals.md +++ b/specs/protocol/custom-gas-token/withdrawals.md @@ -11,4 +11,5 @@ ## The L2ToL1MessagePasser Contract -The `initiateWithdrawal` function MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` is `true` and `msg.value > 0`. +The `initiateWithdrawal` function MUST revert if `isFeatureEnabled[Features.CUSTOM_GAS_TOKEN]` +is `true` and `msg.value > 0`. From a658f26c765ee6ec3195e6827413588ebcc6c314 Mon Sep 17 00:00:00 2001 From: hexshire Date: Mon, 29 Sep 2025 13:26:23 -0300 Subject: [PATCH 13/17] chore: run linters --- specs/protocol/custom-gas-token/predeploys.md | 1 - specs/protocol/custom-gas-token/system-config.md | 1 - specs/protocol/custom-gas-token/withdrawals.md | 1 - 3 files changed, 3 deletions(-) diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index 1c8587497..bc35e2d13 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -2,7 +2,6 @@ - **Table of Contents** - [Overview](#overview) diff --git a/specs/protocol/custom-gas-token/system-config.md b/specs/protocol/custom-gas-token/system-config.md index fc22b8899..b152f51cb 100644 --- a/specs/protocol/custom-gas-token/system-config.md +++ b/specs/protocol/custom-gas-token/system-config.md @@ -2,7 +2,6 @@ - **Table of Contents** - [Feature Flag System](#feature-flag-system) diff --git a/specs/protocol/custom-gas-token/withdrawals.md b/specs/protocol/custom-gas-token/withdrawals.md index 523a37c30..a624ea616 100644 --- a/specs/protocol/custom-gas-token/withdrawals.md +++ b/specs/protocol/custom-gas-token/withdrawals.md @@ -2,7 +2,6 @@ - **Table of Contents** - [The L2ToL1MessagePasser Contract](#the-l2tol1messagepasser-contract) From cdc2aa0928037cfd08f524b8ae80017c84a1bb3f Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:26:01 -0300 Subject: [PATCH 14/17] fix: cgt flag (#74) --- .../custom-gas-token/optimism-portal.md | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/specs/protocol/custom-gas-token/optimism-portal.md b/specs/protocol/custom-gas-token/optimism-portal.md index 711b1b63b..758fbe86c 100644 --- a/specs/protocol/custom-gas-token/optimism-portal.md +++ b/specs/protocol/custom-gas-token/optimism-portal.md @@ -2,13 +2,14 @@ + **Table of Contents** - [Definitions](#definitions) - [Custom Gas Token Flag](#custom-gas-token-flag) - [Rationale](#rationale) - [Function Specification](#function-specification) - - [isCustomGasToken](#iscustomgastoken) + - [Custom Gas Token Detection](#custom-gas-token-detection) - [donateETH](#donateeth) - [depositTransaction](#deposittransaction) - [Security Considerations](#security-considerations) @@ -20,10 +21,10 @@ ### Custom Gas Token Flag -The **Custom Gas Token Flag** (`isCustomGasToken`) is a boolean value that indicates -whether the chain is operating in Custom Gas Token mode. This flag is set during the -`initialize` function call by directly passing the `_isCustomGasToken` boolean parameter, -which is then assigned to the contract's `isCustomGasToken` state variable. +The **Custom Gas Token Flag** is determined by querying the SystemConfig contract's feature flags. +The OptimismPortal determines if Custom Gas Token mode is active by checking if the +`CUSTOM_GAS_TOKEN` feature is enabled in the SystemConfig contract, rather than maintaining +its own local storage for this flag. ## Rationale @@ -33,25 +34,28 @@ ETH deposits would incorrectly imply that it can be minted in the chain. ## Function Specification -### isCustomGasToken +### Custom Gas Token Detection -Returns true if the gas token is a custom gas token, false otherwise. +The OptimismPortal does not expose a public `isCustomGasToken()` function. Instead, it uses an internal +`_isUsingCustomGasToken()` method that queries the SystemConfig contract to determine if the +`CUSTOM_GAS_TOKEN` feature flag is enabled. External consumers should query the SystemConfig directly +to determine the Custom Gas Token mode status. ### donateETH -- MUST revert if `isCustomGasToken()` returns `true` and `msg.value > 0`. +- MUST revert if Custom Gas Token mode is active and `msg.value > 0`. ### depositTransaction -- MUST revert if `isCustomGasToken()` returns `true` and `msg.value > 0`. +- MUST revert if Custom Gas Token mode is active and `msg.value > 0`. ## Security Considerations ### Custom Gas Token Flag Immutability -Once the `isCustomGasToken` flag is set to `true` during initialization, it should not be -changed back to `false` in subsequent reinitializations. Changing from custom gas token mode -back to ETH mode could create inconsistencies in the chain's gas token handling and -potentially lead to security vulnerabilities. The flag represents a fundamental configuration -of the chain's gas token mechanism and should remain consistent throughout the chain's -lifetime. +The Custom Gas Token mode is controlled through the SystemConfig contract's feature flags. +Once the `CUSTOM_GAS_TOKEN` feature is enabled in the SystemConfig, it should not be +disabled in subsequent updates. Changing from custom gas token mode back to ETH mode could +create inconsistencies in the chain's gas token handling and potentially lead to security +vulnerabilities. The feature flag represents a fundamental configuration of the chain's gas +token mechanism and should remain consistent throughout the chain's lifetime. From 3f85e8888c70eefd52efd4ba727c3a4f8318ec75 Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:26:44 -0300 Subject: [PATCH 15/17] feat: remove fund func (#75) --- specs/protocol/custom-gas-token/predeploys.md | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index bc35e2d13..f95ef5081 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -2,6 +2,7 @@ + **Table of Contents** - [Overview](#overview) @@ -18,11 +19,9 @@ - [Functions](#functions-1) - [`deposit`](#deposit) - [`withdraw`](#withdraw) - - [`fund`](#fund) - [Events](#events) - [`LiquidityDeposited`](#liquiditydeposited) - [`LiquidityWithdrawn`](#liquiditywithdrawn) - - [`LiquidityFunded`](#liquidityfunded) - [Invariants](#invariants) - [Liquidity Controller](#liquidity-controller) - [Functions](#functions-2) @@ -145,7 +144,8 @@ Address: `0x4200000000000000000000000000000000000029` The `NativeAssetLiquidity` predeploy stores a large amount of pre-minted native asset that serves as the central liquidity source for Custom Gas Token chains. This contract is only deployed on chains using Custom Gas Token mode and acts as the vault for all -native asset supply management operations. +native asset supply management operations. The contract provides controlled access to +liquidity through the `LiquidityController` predeploy only. ### Functions @@ -174,24 +174,6 @@ function withdraw(uint256 _amount) external - MUST revert if the contract balance is insufficient - MUST emit `LiquidityWithdrawn` event -#### `fund` - -Allows funding the contract with native assets. This function is used to initialize the contract with a large liquidity -pool, similar to how ETHLiquidity is initialized in interop chains. - -```solidity -function fund() external payable -``` - -- MUST accept any amount of native asset via `msg.value` -- MUST revert if `msg.value` is zero -- MUST emit `LiquidityFunded` event -- MUST be callable by any address - -**Important for CGT Migration**: When migrating to Custom Gas Token mode, the total amount funded through this function -should not exceed the `nativeAssetLiquidity` amount configured during chain setup. -Exceeding this amount may cause liquidity problems. - ### Events #### `LiquidityDeposited` @@ -210,20 +192,11 @@ Emitted when native assets are withdrawn from the contract. event LiquidityWithdrawn(address indexed caller, uint256 value) ``` -#### `LiquidityFunded` - -Emitted when the contract receives funding, typically during initial deployment. - -```solidity -event LiquidityFunded(address indexed funder, uint256 amount) -``` - ### Invariants - Only the `LiquidityController` predeploy can call `deposit()` and `withdraw()` - All native asset supply changes must go through this contract when CGT mode is active - No direct user interaction is permitted with liquidity management functions -- For CGT migrations: `fund()` amounts should not exceed the configured `nativeAssetLiquidity` amount to maintain consistency ## Liquidity Controller From 573fdb3eb14314a72643cd11da970434a325652c Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:29:02 -0300 Subject: [PATCH 16/17] feat: liquidity event (#76) * feat: remove fund func * fix: liquidity events --- specs/protocol/custom-gas-token/predeploys.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/specs/protocol/custom-gas-token/predeploys.md b/specs/protocol/custom-gas-token/predeploys.md index f95ef5081..7149a2a6b 100644 --- a/specs/protocol/custom-gas-token/predeploys.md +++ b/specs/protocol/custom-gas-token/predeploys.md @@ -34,8 +34,8 @@ - [Events](#events-1) - [`MinterAuthorized`](#minterauthorized) - [`MinterDeauthorized`](#minterdeauthorized) - - [`AssetsMinted`](#assetsminted) - - [`AssetsBurned`](#assetsburned) + - [`LiquidityMinted`](#liquidityminted) + - [`LiquidityBurned`](#liquidityburned) - [Invariants](#invariants-1) @@ -245,7 +245,7 @@ function mint(address _to, uint256 _amount) external - MUST call `NativeAssetLiquidity.withdraw(_amount)` to unlock assets - MUST send exactly `_amount` of native asset to `_to` address - MUST revert if `NativeAssetLiquidity` has insufficient balance -- MUST emit `AssetsMinted` event +- MUST emit `LiquidityMinted` event #### `burn` @@ -259,7 +259,7 @@ function burn() external payable - MUST call `NativeAssetLiquidity.deposit{value: msg.value}()` to lock assets - MUST only be callable by authorized minters - MUST revert if `msg.value` is zero -- MUST emit `AssetsBurned` event +- MUST emit `LiquidityBurned` event #### `gasPayingAssetName` @@ -292,41 +292,41 @@ function gasPayingAssetSymbol() external view returns (string memory) Emitted when a new minter is authorized by the contract owner. ```solidity -event MinterAuthorized(address indexed minter, address indexed authorizer) +event MinterAuthorized(address indexed minter) ``` -Where `minter` is the address being authorized and `authorizer` is the L1 ProxyAdmin owner who authorized them. +Where `minter` is the address being authorized. #### `MinterDeauthorized` Emitted when a minter is deauthorized by the contract owner. ```solidity -event MinterDeauthorized(address indexed minter, address indexed deauthorizer) +event MinterDeauthorized(address indexed minter) ``` -Where `minter` is the address being deauthorized and `deauthorizer` is the L1 ProxyAdmin owner who deauthorized them. +Where `minter` is the address being deauthorized. -#### `AssetsMinted` +#### `LiquidityMinted` Emitted when native assets are unlocked from the liquidity pool and sent to a recipient. ```solidity -event AssetsMinted(address indexed minter, address indexed to, uint256 amount) +event LiquidityMinted(address indexed minter, address indexed to, uint256 amount) ``` Where `minter` is the authorized address calling the function, `to` is the recipient address, and `amount` is the amount of native assets minted. -#### `AssetsBurned` +#### `LiquidityBurned` Emitted when native assets are locked back into the liquidity pool. ```solidity -event AssetsBurned(address indexed burner, uint256 amount) +event LiquidityBurned(address indexed minter, uint256 amount) ``` -Where `burner` is the `msg.sender` who burned the assets and `amount` is the amount of native assets burned. +Where `minter` is the `msg.sender` who burned the assets and `amount` is the amount of native assets burned. ### Invariants From abad2677a2b141770f59cb724b76e9429e540619 Mon Sep 17 00:00:00 2001 From: Ashitaka <96790496+ashitakah@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:51:06 -0300 Subject: [PATCH 17/17] fix: donate eth (#73) * fix: donate eth * fix: comments * fix: donate eth --- specs/protocol/custom-gas-token/optimism-portal.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/specs/protocol/custom-gas-token/optimism-portal.md b/specs/protocol/custom-gas-token/optimism-portal.md index 758fbe86c..fe24e7c44 100644 --- a/specs/protocol/custom-gas-token/optimism-portal.md +++ b/specs/protocol/custom-gas-token/optimism-portal.md @@ -28,9 +28,13 @@ its own local storage for this flag. ## Rationale -The OptimismPortal's ETH-related logic must revert when Custom Gas Token mode is enabled to prevent ETH from -acting as the native asset. Since the client side does not discern native asset supply creation, allowing -ETH deposits would incorrectly imply that it can be minted in the chain. +The OptimismPortal's deposit logic must revert only for deposits with ETH value when Custom Gas Token mode +is enabled to prevent ETH from acting as the native asset. Deposits without value (msg.value == 0) are +permitted. Since the client side does not discern native asset supply creation, allowing ETH deposits with +value would incorrectly imply that ETH can be minted in the chain. + +The `donateETH` function is permitted to accept ETH even in Custom Gas Token mode, but such donations +cannot be withdrawn through normal withdrawal mechanisms due to the restrictions on value-bearing withdrawals. ## Function Specification @@ -43,7 +47,7 @@ to determine the Custom Gas Token mode status. ### donateETH -- MUST revert if Custom Gas Token mode is active and `msg.value > 0`. +- Accepts ETH value without triggering a deposit to L2. ### depositTransaction