-
Notifications
You must be signed in to change notification settings - Fork 128
feat: L2 call via portal template #1231
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
blmalone
merged 30 commits into
ethereum-optimism:main
from
defi-wonderland:sc-feat/l2-call-via-portal-template
Oct 1, 2025
Merged
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
ca5166b
chore: add remapping for optimism lib
oxlumi d525891
feat: L1PortalExecuteL2Call task implementation
oxlumi f1ed0dd
feat: rehearsals specs for L1PortalExecuteL2Call task
oxlumi 9339f01
test: add regression test for new template L1PortalExecuteL2Call
0xiamflux abb4eff
feat: create new rehearsal ceremony to make an upgrade to L2 Governor
0xiamflux a05ce62
chore: update comment on Optimism Portal address
0xiamflux f5567eb
refactor: remove nested try-catch
0xiamflux 2906167
chore: update fmt
0xiamflux 9f790bc
chore: complies with ci
0xiamflux 0487cc5
chore: remove unecessary remapping
0xiamflux d1f8316
chore: remove rehearsal docs for no-op upgrade
0xiamflux f6c81c7
chore: fix compiler warnings
0xiamflux cecebc3
chore: fix just simulate sep command failure
0xiamflux 8c0a793
chore: remove newline in sc rehearsals README
0xiamflux e43921e
refactor: add OptimismPortal interface in template to avoid coupling
0xiamflux 2bbef1b
chore: update comment natspec on template
0xiamflux cdc54f6
feat: revert before reaching portal with a descriptive message when i…
0xiamflux 9dd87c9
refactor: prefe readBytes over manually parsing
0xiamflux 059e459
fix: use encodeCall over encodeWithSelector
0xiamflux f972e55
chore: undo diff for canceled task
0xiamflux d43bb9f
chore: fix README newlines
0xiamflux 101b92a
chore: add fmt fix to template
0xiamflux c5fae27
fix: lib import path in template
0xiamflux 2b47db1
refactor: make value passed to portal 0
0xiamflux 876a669
fix: library path
0xiamflux f85ac3b
refactor: use L2TaskBase instead of SimpleTaskBase
0xiamflux ffa8ace
chore: add version tag of OptimismPortal
0xiamflux 8a225bb
refactor: renaming loop variables
0xiamflux 400311c
fix: portal from value change allowlist, remove value natspec ref
0xiamflux bb98b11
Merge pull request #20 from defi-wonderland/fix/value-removal
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
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,118 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.15; | ||
|
|
||
| import {VmSafe} from "forge-std/Vm.sol"; | ||
| import {stdToml} from "forge-std/StdToml.sol"; | ||
|
|
||
| import {MultisigTaskPrinter} from "src/libraries/MultisigTaskPrinter.sol"; | ||
| import {Action} from "src/libraries/MultisigTypes.sol"; | ||
| import {L2TaskBase} from "src/tasks/types/L2TaskBase.sol"; | ||
| import {SuperchainAddressRegistry} from "src/SuperchainAddressRegistry.sol"; | ||
|
|
||
| /// @notice Interface for the OptimismPortal2 contract on L1. | ||
| interface IOptimismPortal2 { | ||
| function depositTransaction(address _to, uint256 _value, uint64 _gasLimit, bool _isCreation, bytes memory _data) | ||
| external | ||
| payable; | ||
| } | ||
|
|
||
| /// @notice Template to execute an L2 call via the L1 Optimism Portal from a nested L1 Safe. | ||
| /// Sends an L2 transaction using OptimismPortal.depositTransaction with config-driven params. | ||
|
blmalone marked this conversation as resolved.
|
||
| /// Supports: op-contracts/v4.6.0 | ||
| contract L1PortalExecuteL2Call is L2TaskBase { | ||
| using stdToml for string; | ||
|
|
||
| // -------- Config inputs -------- | ||
| /// @notice The address of the L2 target contract. | ||
| address public l2Target; | ||
| /// @notice The calldata to be executed on l2Target. | ||
| bytes public l2Data; | ||
| /// @notice The L2 gas limit. | ||
| uint64 public gasLimit; | ||
| /// @notice Whether to create a contract on L2. | ||
| bool public isCreation; | ||
|
|
||
| /// @notice Default Safe name. Can be overridden via `safeAddressString` in config.toml. | ||
| function safeAddressString() public pure override returns (string memory) { | ||
| return "ProxyAdminOwner"; | ||
| } | ||
|
|
||
| /// @notice The contracts expected to have storage writes during execution. | ||
| /// Allowlist the OptimismPortal since it will mutate state (queue/event) on deposit. | ||
| function _taskStorageWrites() internal pure override returns (string[] memory) { | ||
| string[] memory _storageWrites = new string[](1); | ||
| _storageWrites[0] = "OptimismPortalProxy"; | ||
| return _storageWrites; | ||
| } | ||
|
|
||
| /// @notice The contracts expected to have balance changes during execution. | ||
| function _taskBalanceChanges() internal pure override returns (string[] memory) {} | ||
|
blmalone marked this conversation as resolved.
|
||
|
|
||
| /// @notice Parse config and initialize template variables. | ||
| /// Expected TOML keys: | ||
| /// - l2Target: address (L2 target address) | ||
| /// - l2Data: hex string (e.g. 0x1234...) | ||
| /// - gasLimit: uint (will be cast to uint64) | ||
| /// - isCreation: bool (optional, default false) | ||
| function _templateSetup(string memory _taskConfigFilePath, address) internal override { | ||
| string memory _toml = vm.readFile(_taskConfigFilePath); | ||
|
|
||
| l2Target = _toml.readAddress(".l2Target"); | ||
| require(l2Target != address(0), "l2Target must be set"); | ||
|
|
||
| // Read hex string and parse to bytes. | ||
| l2Data = _toml.readBytes(".l2Data"); | ||
| require(l2Data.length > 0, "l2Data must be set"); | ||
|
blmalone marked this conversation as resolved.
|
||
|
|
||
| uint256 _gasLimitTmp = _toml.readUint(".gasLimit"); | ||
| require(_gasLimitTmp > 0 && _gasLimitTmp <= type(uint64).max, "invalid gasLimit"); | ||
| gasLimit = uint64(_gasLimitTmp); | ||
|
blmalone marked this conversation as resolved.
|
||
|
|
||
| // Optional fields | ||
| isCreation = false; | ||
| try vm.parseTomlBool(_toml, ".isCreation") returns (bool _b) { | ||
| isCreation = _b; | ||
| } catch {} | ||
|
|
||
| // early revert in case of attempted contract creation with a non-zero target | ||
| require(isCreation && l2Target == address(0) || !isCreation, "contract creation requires zero target address"); | ||
|
blmalone marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /// @notice Build the portal deposit action. WARNING: State changes here are reverted after capture. | ||
| function _build(address) internal override { | ||
| SuperchainAddressRegistry.ChainInfo[] memory chains = superchainAddrRegistry.getChains(); | ||
| for (uint256 _i = 0; _i < chains.length; _i++) { | ||
| IOptimismPortal2(superchainAddrRegistry.getAddress("OptimismPortalProxy", chains[_i].chainId)) | ||
| .depositTransaction(l2Target, 0, gasLimit, isCreation, l2Data); | ||
|
blmalone marked this conversation as resolved.
|
||
| } | ||
| } | ||
|
|
||
| /// @notice Validate that exactly one action to the portal with the expected calldata was captured. | ||
| function _validate(VmSafe.AccountAccess[] memory, Action[] memory _actions, address) internal view override { | ||
| bytes memory _expected = | ||
|
blmalone marked this conversation as resolved.
|
||
| abi.encodeCall(IOptimismPortal2.depositTransaction, (l2Target, 0, gasLimit, isCreation, l2Data)); | ||
|
|
||
| bool _found; | ||
| uint256 _matches; | ||
| SuperchainAddressRegistry.ChainInfo[] memory chains = superchainAddrRegistry.getChains(); | ||
| for (uint256 _i = 0; _i < chains.length; _i++) { | ||
| for (uint256 _j = 0; _j < _actions.length; _j++) { | ||
| if ( | ||
| _actions[_j].target == superchainAddrRegistry.getAddress("OptimismPortalProxy", chains[_i].chainId) | ||
| && _actions[_j].value == 0 | ||
| ) { | ||
| if (keccak256(_actions[_j].arguments) == keccak256(_expected)) { | ||
| _found = true; | ||
| _matches++; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| require(_found && _matches == chains.length, "expected one portal deposit action for each chain"); | ||
| MultisigTaskPrinter.printTitle("Validated portal deposit action"); | ||
| } | ||
|
|
||
| /// @notice No code exceptions required for this template. | ||
| function _getCodeExceptions() internal view override returns (address[] memory) {} | ||
| } | ||
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
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,3 @@ | ||
| TENDERLY_GAS=10000000 | ||
| NESTED_SAFE_NAME_DEPTH_1=council | ||
| FORK_BLOCK_NUMBER=23197819 |
9 changes: 9 additions & 0 deletions
9
test/tasks/example/eth/014-noop-call-optimismportal/config.toml
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,9 @@ | ||
| templateName = "L1PortalExecuteL2Call" | ||
|
|
||
| l2chains = [{name = "OP Mainnet", chainId = 10}] | ||
|
|
||
| # L2 call params | ||
| l2Target = "0xcDF27F107725988f2261Ce2256bDfCdE8B382B10" # OptimismGovernor Proxy | ||
| l2Data = "0x3659cfe6000000000000000000000000ecbf4ed9f47302f00f0f039a691e7db83bdd2624" # upgradeTo(currentImpl) -> 0xecbf4ed9f47302f00f0f039a691e7db83bdd2624 | ||
| gasLimit = 500000 | ||
| isCreation = false |
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.