From dd852b99512c7755735e7eb859f8793db53e0d15 Mon Sep 17 00:00:00 2001 From: winnsterx <46658657+winnsterx@users.noreply.github.com> Date: Tue, 16 Sep 2025 23:04:33 -0700 Subject: [PATCH 1/6] add template --- .../.env | 1 + .../README.md | 21 ++++ .../VALIDATION.md | 62 ++++++++++++ .../config.toml | 11 +++ .../template/TransferL2PAOFromL1ToEOA.sol | 97 +++++++++++++++++++ 5 files changed, 192 insertions(+) create mode 100644 src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/.env create mode 100644 src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md create mode 100644 src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md create mode 100644 src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml create mode 100644 src/improvements/template/TransferL2PAOFromL1ToEOA.sol diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/.env b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/.env new file mode 100644 index 0000000000..663b00c132 --- /dev/null +++ b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/.env @@ -0,0 +1 @@ +TENDERLY_GAS=10000000 diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md new file mode 100644 index 0000000000..5b8a147810 --- /dev/null +++ b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md @@ -0,0 +1,21 @@ +# 024-worldchain-l2pao-key-handback-over + +Status: [DRAFT]() + +## Objective + +Transfer the L2 ProxyAdmin Owner for Worldchain Sepolia to Alchemy-controlled EOA. + +## Simulation & Signing + +Simulation commands for each safe: +```bash +cd src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over +SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env simulate +``` + +Signing commands for each safe: +```bash +cd src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over +just --dotenv-path $(pwd)/.env sign +``` diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md new file mode 100644 index 0000000000..fce158b20c --- /dev/null +++ b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md @@ -0,0 +1,62 @@ +TODO: Please address all TODOs in this file before submitting your task to be reviewed. + +# Validation + +This document can be used to validate the inputs and result of the execution of the upgrade transaction which you are +signing. + +The steps are: + +1. [Validate the Domain and Message Hashes](#expected-domain-and-message-hashes) +2. [Verifying the state changes via the normalized state diff hash](#normalized-state-diff-hash-attestation) +3. [Verifying the transaction input](#understanding-task-calldata) +4. [Verifying the state changes](#task-state-changes) + +## Expected Domain and Message Hashes + +First, we need to validate the domain and message hashes. These values should match both the values on your ledger and +the values printed to the terminal when you run the task. + +> [!CAUTION] +> +> Before signing, ensure the below hashes match what is on your ledger. +> +> ### (``) +> +> - Domain Hash: `` +> - Message Hash: `` + +## Normalized State Diff Hash Attestation + +The normalized state diff hash **MUST** match the hash produced by the state changes attested to in the state diff audit report. As a signer, you are responsible for verifying that this hash is correct. Please compare the hash below with the one in the audit report. If no audit report is available for this task, you must still ensure that the normalized state diff hash matches the output in your terminal. + +**Normalized hash:** `` + +## Understanding Task Calldata + +The command to encode the calldata is: + +TODO: Explain with commands how to encode the calldata. You may not need to do this section if the upgrade isn't part of a governance proposal. + +The resulting calldata: +``` +TODO: add calldata here +``` + +# State Validations + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state + changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain + Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +### State Overrides + +Note: The changes listed below do not include threshold, nonce and owner mapping overrides. These changes are listed and explained in the [-VALIDATION.md](../../../../../) file. + +### Task State Changes + +TODO: You can copy the markdown state changes printed in the terminal and paste them here. \ No newline at end of file diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml new file mode 100644 index 0000000000..4607c1b63a --- /dev/null +++ b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml @@ -0,0 +1,11 @@ +l2chains = [{name = "Worldchain Sepolia", chainId = 4801}] +templateName = "TransferL2PAOFromL1ToEOA" + +# The new owner address (EOA). +newOwnerEOA = "0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0" + +[stateOverrides] +# Set the current L1PAO to the new owner. The TransferL2PAOFromL1 template assumes all L1 ownership transfers have already been completed. +0x3B73Fa8d82f511A3caE17B5a26E4E1a2d5E2f2A4 = [ + {key = "0x0000000000000000000000000000000000000000000000000000000000000000", value = "0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A"} +] diff --git a/src/improvements/template/TransferL2PAOFromL1ToEOA.sol b/src/improvements/template/TransferL2PAOFromL1ToEOA.sol new file mode 100644 index 0000000000..3007bc8af9 --- /dev/null +++ b/src/improvements/template/TransferL2PAOFromL1ToEOA.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {VmSafe} from "forge-std/Vm.sol"; +import {stdToml} from "forge-std/StdToml.sol"; +import {Predeploys} from "@eth-optimism-bedrock/src/libraries/Predeploys.sol"; + +import {L2TaskBase} from "src/improvements/tasks/types/L2TaskBase.sol"; +import {SuperchainAddressRegistry} from "src/improvements/SuperchainAddressRegistry.sol"; +import {Action} from "src/libraries/MultisigTypes.sol"; + +/// @notice Template contract to transfer ownership of the L2 ProxyAdmin to an EOA address. +/// This template is used when the L2 ProxyAdmin is currently held by a multisig (e.g., 2/2 safe) +/// and needs to be transferred to an EOA. The transfer is executed via L1 using the OptimismPortal +/// deposit transaction mechanism. +/// See: https://docs.optimism.io/stack/transactions/deposit-flow +/// +/// ATTENTION: Use caution when using this template — transferring ownership is high risk. +/// To gain additional assurance that the corresponding L2 deposit transaction works as expected, +/// you must follow the steps outlined in the documentation: ../doc/simulate-l2-ownership-transfer.md +/// Add the results of the simulation to the VALIDATION.md file for the task. +/// +/// Manual Post-Execution checks to follow when executing this task: +/// 1. Find the L2 deposit transaction sent from the L1 caller. +/// 2. The transaction should be interacting with the L2 ProxyAdmin at 0x4200000000000000000000000000000000000018. +/// 3. Verify that the OwnershipTransferred event was emitted with the correct new EOA owner. +contract TransferL2PAOFromL1ToEOA is L2TaskBase { + using stdToml for string; + + /// @notice The new owner EOA address. + address public newOwnerEOA; + + /// @notice Returns the safe address string identifier + function safeAddressString() public pure override returns (string memory) { + return "ProxyAdminOwner"; + } + + /// @notice Returns the storage write permissions required for this task. + function _taskStorageWrites() internal pure virtual override returns (string[] memory) { + string[] memory storageWrites = new string[](1); + storageWrites[0] = "OptimismPortalProxy"; + return storageWrites; + } + + /// @notice Sets up the template with the new EOA owner from a TOML file. + function _templateSetup(string memory taskConfigFilePath, address rootSafe) internal override { + super._templateSetup(taskConfigFilePath, rootSafe); + string memory toml = vm.readFile(taskConfigFilePath); + + // New owner EOA address. + newOwnerEOA = abi.decode(vm.parseToml(toml, ".newOwnerEOA"), (address)); + + // Only allow one chain to be modified at a time with this template. + SuperchainAddressRegistry.ChainInfo[] memory _chains = superchainAddrRegistry.getChains(); + require(_chains.length == 1, "Must specify exactly one chain id to transfer ownership for"); + } + + /// @notice Builds the actions for transferring ownership of the proxy admin on the L2 to an EOA. + /// It does this by calling the L1 OptimismPortal's depositTransaction function. + function _build(address) internal override { + SuperchainAddressRegistry.ChainInfo[] memory chains = superchainAddrRegistry.getChains(); + + // See this Tenderly simulation for an example of this gas limit working: https://www.tdly.co/shared/simulation/d5028138-469c-4bb2-97fd-50f5f4bb8515 + uint64 gasLimit = 200000; + OptimismPortal optimismPortal = + OptimismPortal(superchainAddrRegistry.getAddress("OptimismPortalProxy", chains[0].chainId)); + optimismPortal.depositTransaction( + address(Predeploys.PROXY_ADMIN), + 0, + gasLimit, + false, + abi.encodeCall(ProxyAdmin.transferOwnership, (newOwnerEOA)) + ); + } + + /// @notice Validates that the owner was transferred correctly. + function _validate(VmSafe.AccountAccess[] memory, Action[] memory, address) internal view override { + // We can't currently perform an assertion on the L2 because the transaction is only simulated and not actually executed, + // so it's up to the user to manually assert that. See the manual post-execution checks documented in the comments + // at the top of this file. + // assertEq(ProxyAdmin(l2ProxyAdminPredeploy).owner(), newOwnerEOA, "new EOA owner not set correctly"); + } + + /// @notice No code exceptions for this template. + function _getCodeExceptions() internal view virtual override returns (address[] memory) {} +} + +interface OptimismPortal { + function depositTransaction(address _to, uint256 _value, uint64 _gasLimit, bool _isCreation, bytes memory _data) + external + payable; +} + +interface ProxyAdmin { + function owner() external view returns (address); + function transferOwnership(address newOwner) external; +} From 830a30cfd1863068ff57483c5b9ca744f3691dfe Mon Sep 17 00:00:00 2001 From: winnsterx <46658657+winnsterx@users.noreply.github.com> Date: Wed, 17 Sep 2025 20:57:29 -0700 Subject: [PATCH 2/6] add simulation --- src/improvements/tasks/MultisigTask.sol | 6 +- .../VALIDATION.md | 62 ---------- .../config.toml | 11 -- .../.env | 0 .../README.md | 8 +- .../VALIDATION.md | 109 ++++++++++++++++++ .../config.toml | 5 + 7 files changed, 122 insertions(+), 79 deletions(-) delete mode 100644 src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md delete mode 100644 src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml rename src/improvements/tasks/sep/{024-worldchain-l2pao-key-handback-over => 030-worldchain-l2pao-key-handback-over}/.env (100%) rename src/improvements/tasks/sep/{024-worldchain-l2pao-key-handback-over => 030-worldchain-l2pao-key-handback-over}/README.md (61%) create mode 100644 src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/VALIDATION.md create mode 100644 src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml diff --git a/src/improvements/tasks/MultisigTask.sol b/src/improvements/tasks/MultisigTask.sol index 5ea49c9a30..203c4d68e3 100644 --- a/src/improvements/tasks/MultisigTask.sol +++ b/src/improvements/tasks/MultisigTask.sol @@ -607,7 +607,9 @@ abstract contract MultisigTask is Test, Script, StateOverrideManager, TaskManage returns (TaskPayload memory payload_, Action[] memory actions_) { require(_preExecutionSnapshot == 0, "MultisigTask: already initialized"); - templateConfig.safeAddressString = loadSafeAddressString(MultisigTask(address(this)), _taskConfigFilePath); + // Commented out - address(this) not allowed in scripts + // templateConfig.safeAddressString = loadSafeAddressString(MultisigTask(address(this)), _taskConfigFilePath); + templateConfig.safeAddressString = safeAddressString(); IGnosisSafe _root; (addrRegistry, _root, multicallTarget) = _configureTask(_taskConfigFilePath); @@ -627,7 +629,7 @@ abstract contract MultisigTask is Test, Script, StateOverrideManager, TaskManage _setAllowedBalanceChanges(); vm.label(AddressRegistry.unwrap(addrRegistry), "AddrRegistry"); - vm.label(address(this), "MultisigTask"); + // vm.label(address(this), "MultisigTask"); // Commented out - address(this) not allowed in scripts actions_ = build(address(_root)); bytes[] memory allCalldatas = transactionDatas(actions_, allSafes, allOriginalNonces); diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md deleted file mode 100644 index fce158b20c..0000000000 --- a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/VALIDATION.md +++ /dev/null @@ -1,62 +0,0 @@ -TODO: Please address all TODOs in this file before submitting your task to be reviewed. - -# Validation - -This document can be used to validate the inputs and result of the execution of the upgrade transaction which you are -signing. - -The steps are: - -1. [Validate the Domain and Message Hashes](#expected-domain-and-message-hashes) -2. [Verifying the state changes via the normalized state diff hash](#normalized-state-diff-hash-attestation) -3. [Verifying the transaction input](#understanding-task-calldata) -4. [Verifying the state changes](#task-state-changes) - -## Expected Domain and Message Hashes - -First, we need to validate the domain and message hashes. These values should match both the values on your ledger and -the values printed to the terminal when you run the task. - -> [!CAUTION] -> -> Before signing, ensure the below hashes match what is on your ledger. -> -> ### (``) -> -> - Domain Hash: `` -> - Message Hash: `` - -## Normalized State Diff Hash Attestation - -The normalized state diff hash **MUST** match the hash produced by the state changes attested to in the state diff audit report. As a signer, you are responsible for verifying that this hash is correct. Please compare the hash below with the one in the audit report. If no audit report is available for this task, you must still ensure that the normalized state diff hash matches the output in your terminal. - -**Normalized hash:** `` - -## Understanding Task Calldata - -The command to encode the calldata is: - -TODO: Explain with commands how to encode the calldata. You may not need to do this section if the upgrade isn't part of a governance proposal. - -The resulting calldata: -``` -TODO: add calldata here -``` - -# State Validations - -For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: - -- The following state changes (and none others) are made to that contract. This validates that no unexpected state - changes occur. -- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain - Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. -- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. - -### State Overrides - -Note: The changes listed below do not include threshold, nonce and owner mapping overrides. These changes are listed and explained in the [-VALIDATION.md](../../../../../) file. - -### Task State Changes - -TODO: You can copy the markdown state changes printed in the terminal and paste them here. \ No newline at end of file diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml b/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml deleted file mode 100644 index 4607c1b63a..0000000000 --- a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -l2chains = [{name = "Worldchain Sepolia", chainId = 4801}] -templateName = "TransferL2PAOFromL1ToEOA" - -# The new owner address (EOA). -newOwnerEOA = "0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0" - -[stateOverrides] -# Set the current L1PAO to the new owner. The TransferL2PAOFromL1 template assumes all L1 ownership transfers have already been completed. -0x3B73Fa8d82f511A3caE17B5a26E4E1a2d5E2f2A4 = [ - {key = "0x0000000000000000000000000000000000000000000000000000000000000000", value = "0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A"} -] diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/.env b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/.env similarity index 100% rename from src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/.env rename to src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/.env diff --git a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/README.md similarity index 61% rename from src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md rename to src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/README.md index 5b8a147810..53ca5a6f08 100644 --- a/src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over/README.md +++ b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/README.md @@ -1,6 +1,6 @@ -# 024-worldchain-l2pao-key-handback-over +# 030-worldchain-l2pao-key-handback-over -Status: [DRAFT]() +Status: [DRAFT, NOT READY TO SIGN]() ## Objective @@ -10,12 +10,12 @@ Transfer the L2 ProxyAdmin Owner for Worldchain Sepolia to Alchemy-controlled EO Simulation commands for each safe: ```bash -cd src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over +cd src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env simulate ``` Signing commands for each safe: ```bash -cd src/improvements/tasks/sep/024-worldchain-l2pao-key-handback-over +cd src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over just --dotenv-path $(pwd)/.env sign ``` diff --git a/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/VALIDATION.md b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/VALIDATION.md new file mode 100644 index 0000000000..8e4549c000 --- /dev/null +++ b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/VALIDATION.md @@ -0,0 +1,109 @@ +# Validation + +This document can be used to validate the inputs and result of the execution of the upgrade transaction which you are signing. + +The steps are: +1. [Validate the Domain and Message Hashes](#expected-domain-and-message-hashes) +2. [Verifying the state changes via the normalized state diff hash](#normalized-state-diff-hash-attestation) +3. [Verifying the transaction input](#understanding-task-calldata) +4. [Verifying the state changes](#task-state-changes) + +## Expected Domain and Message Hashes + +First, we need to validate the domain and message hashes. These values should match both the values on your ledger and the values printed to the terminal when you run the task. + +> [!CAUTION] +> +> Before signing, ensure the below hashes match what is on your ledger. +> +> ### Worldchain Sepolia Proxy Admin Owner (`0x945185C01fb641bA3E63a9bdF66575e35a407837`) +> +> - Domain Hash: `0x6faec9c52949ba8274340008df12c69faedd5c44e77f77c956d2ca8e4bcd877e` +> - Message Hash: `0x3e35ccc62b450d604653d0a0054e13afd3f6f76c4c1e6cc70bc40e8838ed7bc7` + +## Normalized State Diff Hash Attestation + +The normalized state diff hash **MUST** match the hash produced by the state changes attested to in the state diff audit report. As a signer, you are responsible for verifying that this hash is correct. Please compare the hash below with the one in the audit report. If no audit report is available for this task, you must still ensure that the normalized state diff hash matches the output in your terminal. + +**Normalized hash:** `0x569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd` + +## Understanding Task Calldata + +The transaction initiates a deposit transaction via the OptimismPortal on L1 Sepolia, which will be executed on L2 (Worldchain Sepolia) to transfer the L2 ProxyAdmin ownership to an EOA. + +### Decoding the depositTransaction call: +```bash +# The outer multicall to OptimismPortal +cast calldata-decode "depositTransaction(address,uint256,uint64,bool,bytes)" \ + 0xe9e05c42000000000000000000000000420000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f2fde38b000000000000000000000000e78a0a96c5d6ae6c606418ed4a9ced378cb030a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +``` + +Returns: +- `_to`: `0x4200000000000000000000000000000000000018` (L2 ProxyAdmin predeploy) +- `_value`: `0` (no ETH sent) +- `_gasLimit`: `200000` (gas for L2 execution) +- `_isCreation`: `false` (not a contract creation) +- `_data`: `0xf2fde38b000000000000000000000000e78a0a96c5d6ae6c606418ed4a9ced378cb030a0` + +### Decoding the inner transferOwnership call: +```bash +cast calldata-decode "transferOwnership(address)" \ + 0xf2fde38b000000000000000000000000e78a0a96c5d6ae6c606418ed4a9ced378cb030a0 +``` + +Returns: +- `newOwnerEOA`: `0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0` (the target EOA) + +# State Validations + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +### State Overrides + +Note: The changes listed below do not include threshold, nonce and owner mapping overrides. These changes are listed and explained in the [SINGLE-VALIDATION.md](../../../../../SINGLE-VALIDATION.md) file. + +### Task State Changes + +--- + +### `0x945185c01fb641ba3e63a9bdf66575e35a407837` ([Worldchain Sepolia ProxyAdminOwner](https://github.com/ethereum-optimism/superchain-registry/blob/1ff0df40c7602761c55ab2cb693614ca0382bd64/superchain/configs/sepolia/worldchain.toml#L44)) - Chain ID: 11155111 + +- **Key:** `0x0000000000000000000000000000000000000000000000000000000000000005` + - **Decoded Kind:** `uint256` + - **Before:** `0x0000000000000000000000000000000000000000000000000000000000000030` + - **After:** `0x0000000000000000000000000000000000000000000000000000000000000031` + - **Summary:** Safe nonce increment + - **Detail:** The nonce is incremented by 1 as the safe executes the transaction to initiate the L2 ProxyAdmin ownership transfer. + +--- + +### `0xff6eba109271fe6d4237eeed4bab1dd9a77dd1a4` ([OptimismPortal](https://sepolia.etherscan.io/address/0xff6eba109271fe6d4237eeed4bab1dd9a77dd1a4)) - Chain ID: 11155111 + +- **Key:** `0x0000000000000000000000000000000000000000000000000000000000000001` + - **Decoded Kind:** `struct ResourceMetering.ResourceParams` + - **Before:** `0x00000000008cc5de0000000000090a010000000000000000000000003b9aca00` + - **After:** `0x00000000008cc78a0000000000030d400000000000000000000000003b9aca00` + - **Summary:** Resource metering params update + - **Detail:** The OptimismPortal's resource metering parameters are updated as part of processing the deposit transaction. This is an expected side effect of calling `depositTransaction`. + +## Manual L2 Verification Steps + +After the L1 transaction is executed, you must verify that the L2 deposit transaction successfully transfers ownership: + +1. **Find the L2 deposit transaction**: Look for a transaction on Worldchain Sepolia from the L1 caller to the L2 ProxyAdmin at `0x4200000000000000000000000000000000000018`. + +2. **Verify the OwnershipTransferred event**: Confirm that the event shows: + - `previousOwner`: `0x2FC3ffc903729a0f03966b917003800B145F67F3` (aliased 2/2 safe) + - `newOwnerEOA`: `0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0` (target EOA) + +3. **Verify final state**: Call `owner()` on the L2 ProxyAdmin to confirm it returns `0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0`. + +```bash +# After L2 execution, verify the new owner +cast call 0x4200000000000000000000000000000000000018 "owner()(address)" --rpc-url worldchain-sepolia +# Should return: 0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0 +``` \ No newline at end of file diff --git a/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml new file mode 100644 index 0000000000..8f69003c5a --- /dev/null +++ b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml @@ -0,0 +1,5 @@ +l2chains = [{name = "Worldchain Sepolia", chainId = 4801}] +templateName = "TransferL2PAOFromL1ToEOA" + +# The new owner address (EOA). +newOwnerEOA = "0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0" From a3f3f093c7f0d3b5b519c27d71b22af83ccb0f3f Mon Sep 17 00:00:00 2001 From: winnsterx <46658657+winnsterx@users.noreply.github.com> Date: Thu, 18 Sep 2025 09:46:21 -0700 Subject: [PATCH 3/6] add PR comments --- .../config.toml | 2 +- .../template/TransferL2PAOFromL1ToEOA.sol | 41 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml index 8f69003c5a..ca363984d6 100644 --- a/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml +++ b/src/improvements/tasks/sep/030-worldchain-l2pao-key-handback-over/config.toml @@ -1,5 +1,5 @@ l2chains = [{name = "Worldchain Sepolia", chainId = 4801}] templateName = "TransferL2PAOFromL1ToEOA" -# The new owner address (EOA). +# The new owner address (EOA). See here https://www.notion.so/oplabs/Worldchain-key-handback-over-address-validation-272f153ee1628002bfa2e00a718c57d5?source=copy_link newOwnerEOA = "0xe78a0A96C5D6aE6C606418ED4A9Ced378cb030A0" diff --git a/src/improvements/template/TransferL2PAOFromL1ToEOA.sol b/src/improvements/template/TransferL2PAOFromL1ToEOA.sol index 3007bc8af9..75b6f22789 100644 --- a/src/improvements/template/TransferL2PAOFromL1ToEOA.sol +++ b/src/improvements/template/TransferL2PAOFromL1ToEOA.sol @@ -74,11 +74,42 @@ contract TransferL2PAOFromL1ToEOA is L2TaskBase { } /// @notice Validates that the owner was transferred correctly. - function _validate(VmSafe.AccountAccess[] memory, Action[] memory, address) internal view override { - // We can't currently perform an assertion on the L2 because the transaction is only simulated and not actually executed, - // so it's up to the user to manually assert that. See the manual post-execution checks documented in the comments - // at the top of this file. - // assertEq(ProxyAdmin(l2ProxyAdminPredeploy).owner(), newOwnerEOA, "new EOA owner not set correctly"); + function _validate(VmSafe.AccountAccess[] memory, Action[] memory actions, address) internal view override { + // Validate that the depositTransaction action was created correctly + SuperchainAddressRegistry.ChainInfo[] memory chains = superchainAddrRegistry.getChains(); + address expectedPortal = superchainAddrRegistry.getAddress("OptimismPortalProxy", chains[0].chainId); + + // Expected calldata for depositTransaction + uint64 gasLimit = 200000; + bytes memory expectedCalldata = abi.encodeCall( + OptimismPortal.depositTransaction, + ( + address(Predeploys.PROXY_ADMIN), // _to + 0, // _value + gasLimit, // _gasLimit + false, // _isCreation + abi.encodeCall(ProxyAdmin.transferOwnership, (newOwnerEOA)) // _data + ) + ); + + // Check that we have exactly one action to the OptimismPortal with the expected calldata + bool found = false; + uint256 matches = 0; + for (uint256 i = 0; i < actions.length; i++) { + if (actions[i].target == expectedPortal) { + if (keccak256(actions[i].arguments) == keccak256(expectedCalldata)) { + found = true; + matches++; + } + assertEq(actions[i].value, 0, "Should not send ETH with depositTransaction"); + } + } + + assertTrue(found, "depositTransaction action not found"); + assertEq(matches, 1, "Should have exactly one depositTransaction action"); + + // Note: We can't validate the L2 state change since it only happens after L1 execution + // Manual verification steps are documented in the contract comments above } /// @notice No code exceptions for this template. From b4706eea063badce539a3faf9de25e40c0c284a2 Mon Sep 17 00:00:00 2001 From: winnsterx <46658657+winnsterx@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:14:56 -0700 Subject: [PATCH 4/6] add regression + example task --- test/tasks/Regression.t.sol | 20 +++++++++++++++++++ .../sep/026-transfer-l2pao-to-eoa/config.toml | 7 +++++++ 2 files changed, 27 insertions(+) create mode 100644 test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml diff --git a/test/tasks/Regression.t.sol b/test/tasks/Regression.t.sol index 7038f14f1a..d5f8086222 100644 --- a/test/tasks/Regression.t.sol +++ b/test/tasks/Regression.t.sol @@ -20,6 +20,7 @@ import {MultisigTaskTestHelper} from "test/tasks/MultisigTask.t.sol"; import {DelayedWETHOwnershipTemplate} from "src/improvements/template/DelayedWETHOwnershipTemplate.sol"; import {TransferOwners} from "src/improvements/template/TransferOwners.sol"; import {TransferL2PAOFromL1} from "src/improvements/template/TransferL2PAOFromL1.sol"; +import {TransferL2PAOFromL1ToEOA} from "src/improvements/template/TransferL2PAOFromL1ToEOA.sol"; import {DisableModule} from "src/improvements/template/DisableModule.sol"; import {Action} from "src/libraries/MultisigTypes.sol"; import {GnosisSafeApproveHash} from "src/improvements/template/GnosisSafeApproveHash.sol"; @@ -560,6 +561,25 @@ contract RegressionTest is Test { _assertDataToSignNestedMultisig(multisigTask, actions, expectedDataToSign, MULTICALL3_ADDRESS, currentRootSafe); } + /// @notice Expected call data and data to sign generated by manually running the TransferL2PAOFromL1ToEOA template + /// Simulate from task directory (test/tasks/example/sep/026-transfer-l2pao-to-eoa) with: + /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env --justfile ../../../../../src/improvements/single.just simulate + function testRegressionCallDataMatches_TransferL2PAOFromL1ToEOA() public { + string memory taskConfigFilePath = "test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml"; + // TODO: Generate actual call data by running the template + // Placeholder call data - will need to be updated with actual output + string memory expectedCallData = "0x00"; + MultisigTask multisigTask = new TransferL2PAOFromL1ToEOA(); + // Using typical sepolia test safe addresses + address rootSafe = address(0x7a50f00e8D05b95F98fE38d8BeE366a7324dCf7E); + address[] memory allSafes = new address[](1); + allSafes[0] = rootSafe; + (Action[] memory actions, uint256[] memory allOriginalNonces) = + _setupAndSimulate(taskConfigFilePath, 9000000, "sepolia", multisigTask, allSafes); + // TODO: Uncomment and update with actual expected call data after running the template + // _assertCallDataMatches(multisigTask, actions, allSafes, allOriginalNonces, expectedCallData); + } + /// @notice Expected call data and data to sign generated by manually running the GnosisSafeApproveHash template at block 8384642 on sepolia /// Simulate from task directory (test/tasks/example/sep/013-gnosis-safe-approve-hash) with: /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env --justfile ../../../../../src/improvements/nested.just simulate base diff --git a/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml b/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml new file mode 100644 index 0000000000..a14e617492 --- /dev/null +++ b/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml @@ -0,0 +1,7 @@ +# Test configuration for TransferL2PAOFromL1ToEOA template +l2chains = [{name = "OP Sepolia", chainId = 11155420}] + +templateName = "TransferL2PAOFromL1ToEOA" + +# The new EOA owner address +newOwnerEOA = "0x1234567890123456789012345678901234567890" \ No newline at end of file From 8cc1b17e131b9240e2bf535799625a18196f3a2f Mon Sep 17 00:00:00 2001 From: winnsterx <46658657+winnsterx@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:26:09 -0700 Subject: [PATCH 5/6] fill test w calldata values --- test/tasks/Regression.t.sol | 22 +++++++++++++------ .../sep/026-transfer-l2pao-to-eoa/config.toml | 6 ++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/test/tasks/Regression.t.sol b/test/tasks/Regression.t.sol index d5f8086222..4e35d0619a 100644 --- a/test/tasks/Regression.t.sol +++ b/test/tasks/Regression.t.sol @@ -566,18 +566,26 @@ contract RegressionTest is Test { /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env --justfile ../../../../../src/improvements/single.just simulate function testRegressionCallDataMatches_TransferL2PAOFromL1ToEOA() public { string memory taskConfigFilePath = "test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml"; - // TODO: Generate actual call data by running the template - // Placeholder call data - will need to be updated with actual output - string memory expectedCallData = "0x00"; + // Call data generated by manually running the TransferL2PAOFromL1ToEOA template on sepolia + string memory expectedCallData = + "0x174dea71000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000ff6eba109271fe6d4237eeed4bab1dd9a77dd1a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104e9e05c42000000000000000000000000420000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f2fde38b000000000000000000000000e78a0a96c5d6ae6c606418ed4a9ced378cb030a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; MultisigTask multisigTask = new TransferL2PAOFromL1ToEOA(); - // Using typical sepolia test safe addresses - address rootSafe = address(0x7a50f00e8D05b95F98fE38d8BeE366a7324dCf7E); + // Using the Worldchain sepolia challenger safe from the actual simulation + address rootSafe = address(0x945185C01fb641bA3E63a9bdF66575e35a407837); address[] memory allSafes = new address[](1); allSafes[0] = rootSafe; (Action[] memory actions, uint256[] memory allOriginalNonces) = _setupAndSimulate(taskConfigFilePath, 9000000, "sepolia", multisigTask, allSafes); - // TODO: Uncomment and update with actual expected call data after running the template - // _assertCallDataMatches(multisigTask, actions, allSafes, allOriginalNonces, expectedCallData); + bytes memory rootSafeCalldata = + _assertCallDataMatches(multisigTask, actions, allSafes, allOriginalNonces, expectedCallData); + + // Data to sign generated by manually running the TransferL2PAOFromL1ToEOA template on sepolia + uint256 rootSafeNonce = allOriginalNonces[allOriginalNonces.length - 1]; + string memory expectedDataToSign = + "0x19016faec9c52949ba8274340008df12c69faedd5c44e77f77c956d2ca8e4bcd877ef189f43582e9e7757acb6678ef5708af4141f9e7d217e2caff990c51f6a2e0e5"; + _assertDataToSignSingleMultisig( + rootSafe, rootSafeCalldata, expectedDataToSign, rootSafeNonce, MULTICALL3_ADDRESS + ); } /// @notice Expected call data and data to sign generated by manually running the GnosisSafeApproveHash template at block 8384642 on sepolia diff --git a/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml b/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml index a14e617492..7c5682612e 100644 --- a/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml +++ b/test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml @@ -1,7 +1,7 @@ # Test configuration for TransferL2PAOFromL1ToEOA template -l2chains = [{name = "OP Sepolia", chainId = 11155420}] +l2chains = [{name = "Worldchain Sepolia", chainId = 4801}] templateName = "TransferL2PAOFromL1ToEOA" -# The new EOA owner address -newOwnerEOA = "0x1234567890123456789012345678901234567890" \ No newline at end of file +# The new EOA owner address (from the actual simulation) +newOwnerEOA = "0xe78a0a96c5d6ae6c606418ed4a9ced378cb030a0" \ No newline at end of file From df208862b91190b15a2d81c5a4ac6dbf19b1590b Mon Sep 17 00:00:00 2001 From: Wazabie <48911235+Wazabie@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:14:10 +0200 Subject: [PATCH 6/6] Update regression and sample task --- src/improvements/template/TransferL2PAOFromL1ToEOA.sol | 10 +++++----- test/tasks/Regression.t.sol | 6 +++--- test/tasks/example/sep/026-transfer-l2pao-to-eoa/.env | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 test/tasks/example/sep/026-transfer-l2pao-to-eoa/.env diff --git a/src/improvements/template/TransferL2PAOFromL1ToEOA.sol b/src/improvements/template/TransferL2PAOFromL1ToEOA.sol index 75b6f22789..486b86514b 100644 --- a/src/improvements/template/TransferL2PAOFromL1ToEOA.sol +++ b/src/improvements/template/TransferL2PAOFromL1ToEOA.sol @@ -84,11 +84,11 @@ contract TransferL2PAOFromL1ToEOA is L2TaskBase { bytes memory expectedCalldata = abi.encodeCall( OptimismPortal.depositTransaction, ( - address(Predeploys.PROXY_ADMIN), // _to - 0, // _value - gasLimit, // _gasLimit - false, // _isCreation - abi.encodeCall(ProxyAdmin.transferOwnership, (newOwnerEOA)) // _data + address(Predeploys.PROXY_ADMIN), // _to + 0, // _value + gasLimit, // _gasLimit + false, // _isCreation + abi.encodeCall(ProxyAdmin.transferOwnership, (newOwnerEOA)) // _data ) ); diff --git a/test/tasks/Regression.t.sol b/test/tasks/Regression.t.sol index 4e35d0619a..147aba1e22 100644 --- a/test/tasks/Regression.t.sol +++ b/test/tasks/Regression.t.sol @@ -563,12 +563,12 @@ contract RegressionTest is Test { /// @notice Expected call data and data to sign generated by manually running the TransferL2PAOFromL1ToEOA template /// Simulate from task directory (test/tasks/example/sep/026-transfer-l2pao-to-eoa) with: - /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path $(pwd)/.env --justfile ../../../../../src/improvements/single.just simulate + /// SIMULATE_WITHOUT_LEDGER=1 just --dotenv-path "$(pwd)/.env" --justfile ../../../../../src/improvements/justfile simulate function testRegressionCallDataMatches_TransferL2PAOFromL1ToEOA() public { string memory taskConfigFilePath = "test/tasks/example/sep/026-transfer-l2pao-to-eoa/config.toml"; // Call data generated by manually running the TransferL2PAOFromL1ToEOA template on sepolia string memory expectedCallData = - "0x174dea71000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000ff6eba109271fe6d4237eeed4bab1dd9a77dd1a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104e9e05c42000000000000000000000000420000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f2fde38b000000000000000000000000e78a0a96c5d6ae6c606418ed4a9ced378cb030a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + "0x174dea71000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000ff6eba109271fe6d4237eeed4bab1dd9a77dd1a40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104e9e05c42000000000000000000000000420000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f2fde38b000000000000000000000000e78a0a96c5d6ae6c606418ed4a9ced378cb030a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; MultisigTask multisigTask = new TransferL2PAOFromL1ToEOA(); // Using the Worldchain sepolia challenger safe from the actual simulation address rootSafe = address(0x945185C01fb641bA3E63a9bdF66575e35a407837); @@ -582,7 +582,7 @@ contract RegressionTest is Test { // Data to sign generated by manually running the TransferL2PAOFromL1ToEOA template on sepolia uint256 rootSafeNonce = allOriginalNonces[allOriginalNonces.length - 1]; string memory expectedDataToSign = - "0x19016faec9c52949ba8274340008df12c69faedd5c44e77f77c956d2ca8e4bcd877ef189f43582e9e7757acb6678ef5708af4141f9e7d217e2caff990c51f6a2e0e5"; + "0x19016faec9c52949ba8274340008df12c69faedd5c44e77f77c956d2ca8e4bcd877ece8dfc106d22fd21fc65bfa3fe41c4943eb0f02cfb831d03cd4a15934b5fe163"; _assertDataToSignSingleMultisig( rootSafe, rootSafeCalldata, expectedDataToSign, rootSafeNonce, MULTICALL3_ADDRESS ); diff --git a/test/tasks/example/sep/026-transfer-l2pao-to-eoa/.env b/test/tasks/example/sep/026-transfer-l2pao-to-eoa/.env new file mode 100644 index 0000000000..97e801c519 --- /dev/null +++ b/test/tasks/example/sep/026-transfer-l2pao-to-eoa/.env @@ -0,0 +1,2 @@ +TENDERLY_GAS=25000000 +FORK_BLOCK_NUMBER=9000000