forked from ethereum-optimism/specs
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add initial version of predeploys and l2-contracts-manager #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
0xiamflux
merged 6 commits into
sc-feat/l2cm-specs
from
feat/l2cm-contracts-manager-predeploys
Nov 13, 2025
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
eaba3ec
feat: add initial version of predeploys and l2-contracts-manager
0xiamflux 85abbfb
chore: moves files to experimental
0xiamflux 64f9711
chore: remove unrelated functions
0xiamflux 2afddd3
chore: make clear all network config is gathered
0xiamflux 0556a1e
feat: clarify use of gather config
0xiamflux 46c8fde
fix: update flow diagram
0xiamflux File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Conditional Deployer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| # L2ContractsManager | ||
|
|
||
| <!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
| <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
|
|
||
| - [Summary](#summary) | ||
| - [Upgrade Flow](#upgrade-flow) | ||
| - [Definitions](#definitions) | ||
| - [NUT Bundle](#nut-bundle) | ||
| - [Hard Fork Activation Block](#hard-fork-activation-block) | ||
| - [Predeploys Config](#predeploys-config) | ||
| - [Assumptions](#assumptions) | ||
| - [A-01: Addresses for implementations are properly calculated](#a-01-addresses-for-implementations-are-properly-calculated) | ||
| - [A-02: Execution of the `upgrade` function is performed in `L2ProxyAdmin's` context](#a-02-execution-of-the-upgrade-function-is-performed-in-l2proxyadmins-context) | ||
| - [A-03: Sufficient gas is available for the upgrade process](#a-03-sufficient-gas-is-available-for-the-upgrade-process) | ||
| - [A-04: All predeploys use ERC1967 proxy pattern](#a-04-all-predeploys-use-erc1967-proxy-pattern) | ||
| - [Invariants](#invariants) | ||
| - [Functions](#functions) | ||
| - [`upgrade`](#upgrade) | ||
| - [Behavior](#behavior) | ||
| - [Security Considerations](#security-considerations) | ||
|
|
||
| <!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
|
||
| ## Summary | ||
|
|
||
| A contract responsible for orchestrating the upgrade of all the supported predeploys for a given hard fork. Contains the logic necessary to perform the upgrades and any additional work they might need for the correct setup. The base upgrade logic lives in an abstract contract that MUST be inherited. Given that each hard fork may require different upgrade logic or different configuration handling, a new `L2ContractsManager` is deployed for each hard fork. The manager is deployed as part of the NUT bundle for the target hard fork, ensuring that the upgrade logic is versioned and tied to what the specific hard fork requires. | ||
|
|
||
| The `upgrade()` function is the only function in the public interface of the manager. The manager itself is not a privileged contract and the process relies on the `L2ProxyAdmin` properly calling the `upgrade()` function via a `delegatecall`. | ||
|
|
||
| Implementation addresses for predeploys are determined by the deterministic deployment process using the `ConditionalDeployer` contract. These implementations are deployed in the NUT bundle before the `L2ContractsManager` is invoked. If a contract’s bytecode is unchanged, its implementation address will be unchanged, ensuring deterministic behavior across upgrades. | ||
|
|
||
| This contract supports dev feature flags and MUST upgrade predeploys to the correct implementation based on the enabled feature flags when executed in alphanets or testing environments. | ||
|
|
||
| ## Upgrade Flow | ||
|
|
||
| ```mermaid | ||
| graph TD | ||
| A[NUT Bundle Transactions] -->|1. Deploy implementations| B[ConditionalDeployer] | ||
| A -->|2. Deploy| C[L2ContractsManager] | ||
| A -->|3. Call| D[DEPOSITOR_ACCOUNT] | ||
| D -->|calls upgradePredeploys| E[L2ProxyAdmin] | ||
| E -->|delegatecall| F[L2ContractsManager.upgrade] | ||
| F -->|gather_config| G[Existing Predeploys] | ||
| F -->|upgradeTo/upgradeToAndCall| H[All Predeploys] | ||
| ``` | ||
|
|
||
| 1. Implementation contracts are deployed via the `ConditionalDeployer` contract using the NUT bundle, ensuring deterministic addresses. | ||
| 2. The `L2ContractsManager` is deployed using the NUT bundle. | ||
| 3. The `DEPOSITOR_ACCOUNT` calls `L2ProxyAdmin.upgradePredeploys(_l2ContractsManager)` during the hard fork activation block. | ||
| 4. `L2ProxyAdmin` performs a `delegatecall` to `L2ContractsManager.upgrade()`. | ||
| 5. `L2ContractsManager.upgrade()` gathers network-specific configuration from existing predeploys. | ||
| 6. `L2ContractsManager.upgrade()` upgrades all supported predeploys using either `upgradeTo()` or `upgradeToAndCall()` as appropriate, using the deterministically deployed implementation addresses. | ||
|
|
||
| ## Definitions | ||
|
|
||
| ### NUT Bundle | ||
|
|
||
| A collection of Network Upgrade Transactions stored in JSON format and executed in a specific order. The bundle includes transactions for deploying implementation contracts via `ConditionalDeployer`, deploying the `L2ContractsManager` for the target hard fork, and calling `L2ProxyAdmin.upgradePredeploys()`. The bundle is client-agnostic and can be consumed by any implementation: | ||
|
|
||
| ```json | ||
| { | ||
| "version": "", | ||
| "createdAt": 0, | ||
| "transactions": [ | ||
| // Example of a contract deployment transaction | ||
| { | ||
| "data": "0x608060405234801561001057600080fd5b50610b...", | ||
| "gas": 375000, | ||
| "mint": 0, | ||
| "sourceHash": "0x877a6077205782ea15a6dc8699fa5ebcec5e0f4389f09cb8eda09488231346f8", | ||
| "to": "0x0000000000000000000000000000000000000000", | ||
| "value": 0 | ||
| }, | ||
| // Example of a function execution transaction | ||
| { | ||
| "data": "0x7c36f37e000000000000000000000000bb2cfb2907d198451a12e58a6afee0339f3bbd33", | ||
| "from": "0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001", | ||
| "gas": 18446744073709551615, | ||
| "mint": 0, | ||
| "sourceHash": "0x0d638f060aea832124ac02c40cddc81f6d4800b51451ac9ba34a92d14f1426d2", | ||
| "to": "0x4200000000000000000000000000000000000018", | ||
| "value": 0, | ||
| "contractMethod": { | ||
| "inputs": [ | ||
| { | ||
| "internalType": "address", | ||
| "name": "_target", | ||
| "type": "address" | ||
| } | ||
| ], | ||
| "name": "upgradePredeploys", | ||
| "payable": true | ||
| }, | ||
| "contractInputsValues": { | ||
| "_target": "0xbb2cfb2907d198451a12e58a6afee0339f3bbd33" | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ### Hard Fork Activation Block | ||
|
|
||
| The specific L2 block at which a hard fork becomes active. At this block, the NUT bundle transactions are executed, including the deployment of new implementations and the upgrade of all predeploys. | ||
|
|
||
| ### Predeploys Config | ||
|
|
||
| Network-specific configuration values gathered from existing predeploys before **performing upgrades**. This includes values such as L1 cross-domain messenger addresses, bridge configurations, and other network-specific parameters that must be preserved during upgrades. | ||
|
|
||
| ## Assumptions | ||
|
|
||
| ### A-01: Addresses for implementations are properly calculated | ||
|
|
||
| The contract assumes that all implementation addresses point to valid, properly deployed contracts with the expected interface. | ||
|
|
||
| ### A-02: Execution of the `upgrade` function is performed in `L2ProxyAdmin's` context | ||
|
|
||
| The contract assumes it has sufficient permissions to perform upgrades on the predeploys; therefore, it assumes the `upgrade` function is being called by the `L2ProxyAdmin` via `delegatecall`. | ||
|
|
||
| ### A-03: Sufficient gas is available for the upgrade process | ||
|
|
||
| The contract assumes that sufficient gas is available for the entire upgrade process, including configuration gathering and all predeploy upgrades. The system provides additional upgrade gas beyond the normal `systemTxMaxGas` limit to ensure upgrades can complete successfully. | ||
|
|
||
| ### A-04: All predeploys use ERC1967 proxy pattern | ||
|
|
||
| The contract assumes that all predeploys being upgraded use the ERC1967 proxy pattern and support the `upgradeTo()` and `upgradeToAndCall()` methods. | ||
|
|
||
| ## Invariants | ||
|
|
||
| TBD | ||
|
|
||
| ## Functions | ||
|
|
||
| ### `upgrade` | ||
|
|
||
| This function is responsible for orchestrating the upgrades of all the supported predeploys for the target hard fork. The manager has no special privileges and relies on the `L2ProxyAdmin` calling `upgrade()` via a `delegatecall`. | ||
|
|
||
| ```solidity | ||
| function upgrade() external; | ||
| ``` | ||
|
|
||
| #### Behavior | ||
|
|
||
| - MUST always succeed when called via `delegatecall` by `L2ProxyAdmin`. | ||
| - MUST gather all network-specific configuration values from existing predeploys before performing upgrades by calling `_gatherPredeploysConfig()`. These values are read from the current predeploy implementations and used to initialize or configure the upgraded implementations. | ||
| - MUST upgrade ALL predeploys that are supported by the target hard fork, including: | ||
| - Predeploys with unchanged implementations (they will be upgraded to the same implementation address). | ||
| - Predeploys with feature-flagged implementations when executed in alphanets or testing environments. | ||
| - For each predeploy being upgraded: | ||
| - MUST call `Proxy.upgradeToAndCall()` if the contract has initializer arguments that need to be set, passing the gathered configuration values. | ||
| - MUST call `Proxy.upgradeTo()` if the contract does not have initializer arguments. | ||
| - MUST use the implementation addresses that were deterministically deployed via the `ConditionalDeployer` contract in the NUT bundle. | ||
|
|
||
|
0xOneTony marked this conversation as resolved.
|
||
| ## Security Considerations | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| # Predeploys | ||
|
|
||
| <!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
| <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
|
|
||
| - [Overview](#overview) | ||
| - [L2ProxyAdmin](#l2proxyadmin) | ||
| - [Invariants](#invariants) | ||
| - [I-01: upgradePredeploys Access Control](#i-01-upgradepredeploys-access-control) | ||
| - [Impact](#impact) | ||
| - [I-02: Depositor Account Execution Guarantee](#i-02-depositor-account-execution-guarantee) | ||
| - [Impact](#impact-1) | ||
| - [Functions](#functions) | ||
| - [`upgradePredeploys`](#upgradepredeploys) | ||
| - [`setImplementationName`](#setimplementationname) | ||
| - [`setUpgrading`](#setupgrading) | ||
| - [`isUpgrading`](#isupgrading) | ||
| - [`getProxyImplementation`](#getproxyimplementation) | ||
| - [`getProxyAdmin`](#getproxyadmin) | ||
| - [`changeProxyAdmin`](#changeproxyadmin) | ||
| - [`upgrade`](#upgrade) | ||
| - [`upgradeAndCall`](#upgradeandcall) | ||
| - [Security Considerations](#security-considerations) | ||
|
|
||
| <!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
|
|
||
| ## Overview | ||
|
|
||
| ## L2ProxyAdmin | ||
|
|
||
| The `ProxyAdmin` predeploy at `0x4200000000000000000000000000000000000018` is upgraded with a new L2-specific implementation, `L2ProxyAdmin`, that supports predeploy upgrades during hard forks. The `L2ProxyAdmin` contract adds the `upgradePredeploys()` function, which is called during a hard fork activation block by the `DEPOSITOR_ACCOUNT` to upgrade all predeploys in a single transaction. | ||
|
|
||
| The `L2ProxyAdmin` implementation also removes unused logic from the universal `ProxyAdmin` contract. Previously, the L2 ProxyAdmin used the same "universal" implementation deployed on L1, which supports multiple proxy types (ERC1967, ChugSplash, and ResolvedDelegate) and includes proxy type validation logic. Since all L2 predeploys use ERC1967 proxies exclusively, `L2ProxyAdmin` overrides functions related to legacy proxy types to remove their support while keeping the same public interface for backward compatibility. Setters for configuration values regarding proxy types are overridden to result in no-ops, and getters are overridden to either return default values or values that are relevant in the ERC1967 context only. | ||
|
|
||
| ## Invariants | ||
|
|
||
| ### I-01: upgradePredeploys Access Control | ||
|
|
||
| The `upgradePredeploys` function MUST only be callable by the contract owner or the `DEPOSITOR_ACCOUNT` (`0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001`). | ||
|
|
||
| #### Impact | ||
|
|
||
| Violation of this invariant would allow an attacker to trigger predeploy upgrades via `delegatecall` to arbitrary contracts. | ||
|
|
||
| ### I-02: Depositor Account Execution Guarantee | ||
|
|
||
| When called by the `DEPOSITOR_ACCOUNT`, the `upgradePredeploys` function MUST NOT revert, provided that the `_l2ContractsManager` parameter is a valid contract address. | ||
|
|
||
| #### Impact | ||
|
|
||
| Violation of this invariant will allow upgrades to fail, potentially leading to network stalling. | ||
|
|
||
| ## Functions | ||
|
|
||
| ### `upgradePredeploys` | ||
|
|
||
| This is a permissioned function that performs a `delegatecall` to a previously deployed `L2ContractsManager`. | ||
|
|
||
| ```solidity | ||
| function upgradePredeploys(address _l2ContractsManager) external; | ||
| ``` | ||
|
|
||
| - MUST revert if not called by either the `DEPOSITOR_ACCOUNT` or the owner of the `L2ProxyAdmin` | ||
| - MUST perform a single `delegatecall` to `_l2ContractsManager` | ||
| - MUST never revert | ||
|
|
||
| ### `setImplementationName` | ||
|
|
||
| ```solidity | ||
| function setImplementationName(address _address, string memory _name) external; | ||
| ``` | ||
|
|
||
| - MUST NOT revert | ||
| - MUST NOT modify any state | ||
|
|
||
| ### `setUpgrading` | ||
|
|
||
| ```solidity | ||
| function setUpgrading(bool _upgrading) external; | ||
| ``` | ||
|
|
||
| - MUST NOT revert | ||
| - MUST NOT modify any state | ||
|
|
||
| ### `isUpgrading` | ||
|
|
||
| ```solidity | ||
| function isUpgrading() external view returns (bool); | ||
| ``` | ||
|
|
||
| - MUST return `false` | ||
|
|
||
| ### `getProxyImplementation` | ||
|
|
||
| ```solidity | ||
| function getProxyImplementation(address _proxy) external view returns (address); | ||
| ``` | ||
|
|
||
| - MUST return the implementation address by calling `implementation()` on the ERC1967 proxy | ||
| - MUST NOT query proxy type configuration | ||
|
|
||
| ### `getProxyAdmin` | ||
|
|
||
| ```solidity | ||
| function getProxyAdmin(address payable _proxy) external view returns (address); | ||
| ``` | ||
|
|
||
| - MUST return the admin address by calling `admin()` on the ERC1967 proxy | ||
| - MUST NOT query proxy type configuration | ||
|
|
||
| ### `changeProxyAdmin` | ||
|
|
||
| ```solidity | ||
| function changeProxyAdmin(address payable _proxy, address _newAdmin) external; | ||
| ``` | ||
|
|
||
| - MUST revert if caller is not the owner | ||
| - MUST call `changeAdmin(_newAdmin)` on the ERC1967 proxy | ||
|
|
||
| ### `upgrade` | ||
|
|
||
| ```solidity | ||
| function upgrade(address payable _proxy, address _implementation) public; | ||
| ``` | ||
|
|
||
| - MUST revert if caller is not the owner | ||
| - MUST call `upgradeTo(_implementation)` on the ERC1967 proxy | ||
|
|
||
| ### `upgradeAndCall` | ||
|
|
||
| ```solidity | ||
| function upgradeAndCall( | ||
| address payable _proxy, | ||
| address _implementation, | ||
| bytes memory _data | ||
| ) external payable; | ||
| ``` | ||
|
|
||
| - MUST revert if caller is not the owner | ||
| - MUST call `upgradeToAndCall(_implementation, _data)` on the ERC1967 proxy with forwarded `msg.value` | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| - Performing a `delegatecall` from the `ProxyAdmin` which manages **ALL** the predeploys must be implemented with extreme caution. We must ensure that no accounts other than the owner or the `DEPOSITOR_ACCOUNT` can call the `upgradePredeploys` function. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Transaction Generation Script |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.