feat: shared dispute game migration for interop#19840
feat: shared dispute game migration for interop#19840stevennevins wants to merge 33 commits intodevelopfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #19840 +/- ##
===========================================
+ Coverage 0 80.7% +80.7%
===========================================
Files 0 140 +140
Lines 0 7347 +7347
===========================================
+ Hits 0 5936 +5936
- Misses 0 1411 +1411
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
| event ETHMigrated(address indexed lockbox, uint256 balance); | ||
| event PortalMigrated( | ||
| IETHLockbox indexed oldLockbox, | ||
| IETHLockbox indexed _newLockbox, |
There was a problem hiding this comment.
Event parameter naming convention violation: The parameter '_newLockbox' in the PortalMigrated event should not be prefixed with an underscore. According to the Optimism Solidity Style Guide naming conventions, event parameters should NOT be prefixed with an underscore, while function parameters should be prefixed with an underscore. Please rename '_newLockbox' to 'newLockbox' to comply with the style guide.
| IETHLockbox indexed _newLockbox, | |
| IETHLockbox indexed newLockbox, |
Spotted by Graphite (based on custom rule: Solidity Style Guide)
Is this helpful? React 👍 or 👎 to let us know.
| if (!_cts.systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { | ||
| _cts.systemConfig.setFeature(Features.ETH_LOCKBOX, true); | ||
| } | ||
| if (!_cts.systemConfig.isFeatureEnabled(Features.INTEROP)) { |
There was a problem hiding this comment.
Critical Bug: Interface mismatch
This line calls migrateLiquidity() on IOptimismPortalInterop, but based on the diff, migrateLiquidity() was added to OptimismPortal2 (line 472-481 in OptimismPortal2.sol) and is only exposed in the IOptimismPortal2 interface (line 86-87 in IOptimismPortal2.sol). The IOptimismPortalInterop interface does not include this function.
Fix:
IOptimismPortal(payable(_cts.optimismPortal)).migrateLiquidity();This matches the pattern used in OPContractsManagerMigrator.sol (line 242) where the cast was correctly changed from IOptimismPortalInterop to IOptimismPortal.
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
| @@ -208,9 +231,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase | |||
| error OptimismPortal_InvalidLockboxState(); | |||
|
|
|||
| /// @notice Semantic version. | |||
| /// @custom:semver 5.3.0 | |||
| /// @custom:semver 5.4.0 | |||
| function version() public pure virtual returns (string memory) { | |||
| return "5.3.0"; | |||
| return "5.4.0"; | |||
| } | |||
|
|
|||
| /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. | |||
| @@ -224,7 +247,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase | |||
| /// @param _anchorStateRegistry Address of the AnchorStateRegistry. | |||
| function initialize( | |||
| ISystemConfig _systemConfig, | |||
| IAnchorStateRegistry _anchorStateRegistry | |||
| IAnchorStateRegistry _anchorStateRegistry, | |||
| IETHLockbox _ethLockbox | |||
| ) | |||
| external | |||
| reinitializer(initVersion()) | |||
| @@ -235,6 +259,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase | |||
| // Now perform initialization logic. | |||
| systemConfig = _systemConfig; | |||
| anchorStateRegistry = _anchorStateRegistry; | |||
| if (address(_ethLockbox) != address(0)){ | |||
| ethLockbox = _ethLockbox; | |||
|
|
|||
| } | |||
|
|
|||
There was a problem hiding this comment.
Non-Idempotent Initializer — Acknowledgment Required
This initialize() function contains operations that are not idempotent (not safe to call multiple times with the same arguments). Since proxied contracts can be re-initialized during upgrades, this is disallowed unless explicitly acknowledged.
Please either:
- Make the operation idempotent, or
- Add a
@noticecomment on the function explaining why the non-idempotent behavior is safe given how callers use it
See docs/ai/contract-dev.md for detailed guidance.
| error OptimismPortal_ProofNotOldEnough(); | |
| error OptimismPortal_InvalidLockboxState(); | |
| /// @notice Semantic version. | |
| /// @custom:semver 5.4.0 | |
| function version() public pure virtual returns (string memory) { | |
| return "5.4.0"; | |
| } | |
| /// @notice Initializes the OptimismPortal2 contract. This function is non-idempotent and will | |
| /// overwrite existing state variables when called. This is acceptable because the function | |
| /// is only called during controlled upgrade scenarios where the new values are intended | |
| /// to replace the existing configuration. | |
| /// @param _systemConfig The SystemConfig contract. | |
| /// @param _anchorStateRegistry Address of the AnchorStateRegistry. | |
| /// @param _ethLockbox Address of the ETHLockbox contract. | |
| function initialize( | |
| ISystemConfig _systemConfig, | |
| IAnchorStateRegistry _anchorStateRegistry, | |
| IETHLockbox _ethLockbox | |
| ) | |
| external | |
| reinitializer(initVersion()) | |
| { | |
| // Now perform initialization logic. | |
| systemConfig = _systemConfig; | |
| anchorStateRegistry = _anchorStateRegistry; | |
| if (address(_ethLockbox) != address(0)) { | |
| ethLockbox = _ethLockbox; | |
| } | |
| } | |
Spotted by Graphite (based on custom rule: Monorepo Graphite Rules)
Is this helpful? React 👍 or 👎 to let us know.
…OptimismPortal2 via the feature flag
…ve interface from OPCM_V2
…/ethereum-optimism/optimism into feat/interop-shared-dispute-game
| function migrateLiquidity() public { | ||
| if (!_isUsingInterop()) revert OptimismPortal_NotUsingInterop(); | ||
| // Liquidity migration can only be triggered by the ProxyAdmin owner. | ||
| _assertOnlyProxyAdminOwner(); | ||
|
|
||
| // Migrate the liquidity. | ||
| uint256 ethBalance = address(this).balance; | ||
| ethLockbox.lockETH{ value: ethBalance }(); | ||
| emit ETHMigrated(address(ethLockbox), ethBalance); | ||
| } |
There was a problem hiding this comment.
The migrateLiquidity() function checks _isUsingInterop() but doesn't verify that ethLockbox is set before calling ethLockbox.lockETH(). Since initialize() conditionally sets ethLockbox only if _ethLockbox != address(0) (line 262-264), there's a scenario where INTEROP feature is enabled but ethLockbox remains uninitialized (address(0)). This will cause a revert when migrateLiquidity() is called.
function migrateLiquidity() public {
if (!_isUsingInterop()) revert OptimismPortal_NotUsingInterop();
// Add lockbox validation
if (address(ethLockbox) == address(0)) revert OptimismPortal_InvalidLockboxState();
_assertOnlyProxyAdminOwner();
uint256 ethBalance = address(this).balance;
ethLockbox.lockETH{ value: ethBalance }();
emit ETHMigrated(address(ethLockbox), ethBalance);
}Alternatively, update _isUsingInterop() to also check that ethLockbox != address(0), similar to how _isUsingLockbox() works at line 728.
| function migrateLiquidity() public { | |
| if (!_isUsingInterop()) revert OptimismPortal_NotUsingInterop(); | |
| // Liquidity migration can only be triggered by the ProxyAdmin owner. | |
| _assertOnlyProxyAdminOwner(); | |
| // Migrate the liquidity. | |
| uint256 ethBalance = address(this).balance; | |
| ethLockbox.lockETH{ value: ethBalance }(); | |
| emit ETHMigrated(address(ethLockbox), ethBalance); | |
| } | |
| function migrateLiquidity() public { | |
| if (!_isUsingInterop()) revert OptimismPortal_NotUsingInterop(); | |
| if (address(ethLockbox) == address(0)) revert OptimismPortal_InvalidLockboxState(); | |
| // Liquidity migration can only be triggered by the ProxyAdmin owner. | |
| _assertOnlyProxyAdminOwner(); | |
| // Migrate the liquidity. | |
| uint256 ethBalance = address(this).balance; | |
| ethLockbox.lockETH{ value: ethBalance }(); | |
| emit ETHMigrated(address(ethLockbox), ethBalance); | |
| } | |
Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
…rade ordering Bump OptimismPortal2.version() from 5.4.0 to 5.5.0 so OPCM_V2 can upgrade from OptimismPortalInterop (5.4.1+interop) without triggering the DowngradeNotAllowed check. In OPContractsManagerV2._apply(), set the ETH_LOCKBOX feature on SystemConfig before upgrading the portal. Unlike OptimismPortalInterop which blindly sets the ethLockbox, OptimismPortal2.initialize() calls _assertValidLockboxState() which requires the ETH_LOCKBOX feature flag and ethLockbox address to be consistent. Update superRootsActive tests to support both OPCM_V2 (gameType mock) and legacy (slot 63 manipulation) paths via _enableSuperRootBehavior helper.
…lInterop when OPCM_V2 and Interop feature flags enabled
…e portal migration functions
| error OptimismPortal_InvalidRootClaim(); | ||
| error OptimismPortal_MigratingToSameRegistry(); | ||
| error OptimismPortal_NoReentrancy(); | ||
| error OptimismPortal_NotUsingInterop(); |
There was a problem hiding this comment.
The custom error OptimismPortal_NotUsingInterop violates the Optimism Solidity Style Guide naming convention for errors. Custom errors should take the format ContractName_ErrorDescription. Since this is in the IOptimismPortal2 interface, it should be named OptimismPortal2_NotUsingInterop to match the contract name.
| error OptimismPortal_NotUsingInterop(); | |
| error OptimismPortal2_NotUsingInterop(); |
Spotted by Graphite (based on custom rule: Solidity Style Guide)
Is this helpful? React 👍 or 👎 to let us know.
| if (anchorStateRegistry == _newAnchorStateRegistry) { | ||
| revert OptimismPortal_MigratingToSameRegistry(); | ||
| } |
There was a problem hiding this comment.
The state change in the migrateToSharedDisputeGame function violates the Optimism Solidity Style Guide requirement that all state changing functions should emit a corresponding event. The function checks if anchorStateRegistry == _newAnchorStateRegistry and reverts, but this validation logic modifies the control flow based on state without emitting an event for this specific validation failure. While the function does emit a PortalMigrated event on success, there should be consistent event emission for all state-dependent operations.
Spotted by Graphite (based on custom rule: Solidity Style Guide)
Is this helpful? React 👍 or 👎 to let us know.
Description
Move migrateToSuperRoots(), migrateLiquidity(), and 3 param initialize() from OptimismPortalInterop to OptimismPortal2.
And unify OPCM_V2 to target OptimismPortal2 for the Interop migration codepath
Tests
Tests the superchain system features are properly configured
Tests that migration succeeds with the system feature is enabled
Additional context
Part of #19699
Associated design doc: https://github.com/ethereum-optimism/design-docs/blob/main/protocol/proofs/shared-dispute-game-migration.md
Metadata
Closes #19708
Closes #19710
Closes #19011