diff --git a/op-chain-ops/interopgen/configs.go b/op-chain-ops/interopgen/configs.go index a365fd6cb78ee..c3113d240e10c 100644 --- a/op-chain-ops/interopgen/configs.go +++ b/op-chain-ops/interopgen/configs.go @@ -74,16 +74,15 @@ type L2Config struct { Challenger common.Address SystemConfigOwner common.Address genesis.L2InitializationConfig - Prefund map[common.Address]*big.Int - SaltMixer string - GasLimit uint64 - DisputeGameUsesSuperRoots bool - DisputeGameType uint32 - DisputeAbsolutePrestate common.Hash - DisputeMaxGameDepth uint64 - DisputeSplitDepth uint64 - DisputeClockExtension uint64 - DisputeMaxClockDuration uint64 + Prefund map[common.Address]*big.Int + SaltMixer string + GasLimit uint64 + DisputeGameType uint32 + DisputeAbsolutePrestate common.Hash + DisputeMaxGameDepth uint64 + DisputeSplitDepth uint64 + DisputeClockExtension uint64 + DisputeMaxClockDuration uint64 } func (c *L2Config) Check(log log.Logger) error { diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index 84a06245132ae..4ef1d32b658df 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -198,25 +198,24 @@ func DeployL2ToL1(l1Host *script.Host, superCfg *SuperchainConfig, superDeployme l1Host.SetTxOrigin(cfg.Deployer) output, err := opcm.DeployOPChain(l1Host, opcm.DeployOPChainInput{ - OpChainProxyAdminOwner: cfg.ProxyAdminOwner, - SystemConfigOwner: cfg.SystemConfigOwner, - Batcher: cfg.BatchSenderAddress, - UnsafeBlockSigner: cfg.P2PSequencerAddress, - Proposer: cfg.Proposer, - Challenger: cfg.Challenger, - BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, - BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, - L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), - Opcm: superDeployment.Opcm, - SaltMixer: cfg.SaltMixer, - GasLimit: cfg.GasLimit, - DisputeGameUsesSuperRoots: cfg.DisputeGameUsesSuperRoots, - DisputeGameType: cfg.DisputeGameType, - DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, - DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, - DisputeSplitDepth: cfg.DisputeSplitDepth, - DisputeClockExtension: cfg.DisputeClockExtension, - DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, + OpChainProxyAdminOwner: cfg.ProxyAdminOwner, + SystemConfigOwner: cfg.SystemConfigOwner, + Batcher: cfg.BatchSenderAddress, + UnsafeBlockSigner: cfg.P2PSequencerAddress, + Proposer: cfg.Proposer, + Challenger: cfg.Challenger, + BasefeeScalar: cfg.GasPriceOracleBaseFeeScalar, + BlobBaseFeeScalar: cfg.GasPriceOracleBlobBaseFeeScalar, + L2ChainId: new(big.Int).SetUint64(cfg.L2ChainID), + Opcm: superDeployment.Opcm, + SaltMixer: cfg.SaltMixer, + GasLimit: cfg.GasLimit, + DisputeGameType: cfg.DisputeGameType, + DisputeAbsolutePrestate: cfg.DisputeAbsolutePrestate, + DisputeMaxGameDepth: cfg.DisputeMaxGameDepth, + DisputeSplitDepth: cfg.DisputeSplitDepth, + DisputeClockExtension: cfg.DisputeClockExtension, + DisputeMaxClockDuration: cfg.DisputeMaxClockDuration, }) if err != nil { return nil, fmt.Errorf("failed to deploy L2 OP chain: %w", err) diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index b835716ea588b..6cf3265925c4d 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -275,16 +275,15 @@ func (r *InteropDevL2Recipe) build(l1ChainID uint64, addrs devkeys.Addresses) (* UseAltDA: false, }, }, - Prefund: make(map[common.Address]*big.Int), - SaltMixer: "", - GasLimit: 60_000_000, - DisputeGameUsesSuperRoots: true, - DisputeGameType: 1, // PERMISSIONED_CANNON Game Type - DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"), - DisputeMaxGameDepth: 73, - DisputeSplitDepth: 30, - DisputeClockExtension: 10800, // 3 hours (input in seconds) - DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds) + Prefund: make(map[common.Address]*big.Int), + SaltMixer: "", + GasLimit: 60_000_000, + DisputeGameType: 1, // PERMISSIONED_CANNON Game Type + DisputeAbsolutePrestate: common.HexToHash("0x038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c"), + DisputeMaxGameDepth: 73, + DisputeSplitDepth: 30, + DisputeClockExtension: 10800, // 3 hours (input in seconds) + DisputeMaxClockDuration: 302400, // 3.5 days (input in seconds) } l2Users := devkeys.ChainUserKeys(new(big.Int).SetUint64(r.ChainID)) diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index 134d9a34c2cd7..43ab3ded3a22b 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -529,7 +529,6 @@ func TestAdditionalDisputeGames(t *testing.T) { intent.Chains[0].AdditionalDisputeGames = []state.AdditionalDisputeGame{ { ChainProofParams: state.ChainProofParams{ - DisputeGameUsesSuperRoots: false, DisputeGameType: 255, DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, DisputeMaxGameDepth: 50, diff --git a/op-deployer/pkg/deployer/opcm/opchain.go b/op-deployer/pkg/deployer/opcm/opchain.go index 813b7435170ad..9d87fb4979a4e 100644 --- a/op-deployer/pkg/deployer/opcm/opchain.go +++ b/op-deployer/pkg/deployer/opcm/opchain.go @@ -33,7 +33,6 @@ type DeployOPChainInput struct { SaltMixer string GasLimit uint64 - DisputeGameUsesSuperRoots bool DisputeGameType uint32 DisputeAbsolutePrestate common.Hash DisputeMaxGameDepth uint64 diff --git a/op-deployer/pkg/deployer/pipeline/opchain.go b/op-deployer/pkg/deployer/pipeline/opchain.go index 3007aaa1f47fe..fd6c647667887 100644 --- a/op-deployer/pkg/deployer/pipeline/opchain.go +++ b/op-deployer/pkg/deployer/pipeline/opchain.go @@ -74,13 +74,12 @@ func DeployOPChain(env *Env, intent *state.Intent, st *state.State, chainID comm func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common.Hash, st *state.State) (opcm.DeployOPChainInput, error) { proofParams, err := jsonutil.MergeJSON( state.ChainProofParams{ - DisputeGameUsesSuperRoots: standard.DisputeGameUsesSuperRoots, - DisputeGameType: standard.DisputeGameType, - DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, - DisputeMaxGameDepth: standard.DisputeMaxGameDepth, - DisputeSplitDepth: standard.DisputeSplitDepth, - DisputeClockExtension: standard.DisputeClockExtension, - DisputeMaxClockDuration: standard.DisputeMaxClockDuration, + DisputeGameType: standard.DisputeGameType, + DisputeAbsolutePrestate: standard.DisputeAbsolutePrestate, + DisputeMaxGameDepth: standard.DisputeMaxGameDepth, + DisputeSplitDepth: standard.DisputeSplitDepth, + DisputeClockExtension: standard.DisputeClockExtension, + DisputeMaxClockDuration: standard.DisputeMaxClockDuration, }, intent.GlobalDeployOverrides, thisIntent.DeployOverrides, @@ -102,7 +101,6 @@ func makeDCI(intent *state.Intent, thisIntent *state.ChainIntent, chainID common Opcm: st.ImplementationsDeployment.OpcmAddress, SaltMixer: st.Create2Salt.String(), // passing through salt generated at state initialization GasLimit: standard.GasLimit, - DisputeGameUsesSuperRoots: proofParams.DisputeGameUsesSuperRoots, DisputeGameType: proofParams.DisputeGameType, DisputeAbsolutePrestate: proofParams.DisputeAbsolutePrestate, DisputeMaxGameDepth: proofParams.DisputeMaxGameDepth, diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 72f65d5d2aef1..e9ba7d8d45540 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -26,7 +26,6 @@ const ( ProofMaturityDelaySeconds uint64 = 604800 DisputeGameFinalityDelaySeconds uint64 = 302400 MIPSVersion uint64 = 1 - DisputeGameUsesSuperRoots bool = false DisputeGameType uint32 = 1 // PERMISSIONED game type DisputeMaxGameDepth uint64 = 73 DisputeSplitDepth uint64 = 30 diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index b53a8d0a8bc1e..f374466ea41ca 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -17,7 +17,6 @@ const ( ) type ChainProofParams struct { - DisputeGameUsesSuperRoots bool `json:"disputeGameUsesSuperRoots" toml:"disputeGameUsesSuperRoots"` DisputeGameType uint32 `json:"respectedGameType" toml:"respectedGameType"` DisputeAbsolutePrestate common.Hash `json:"faultGameAbsolutePrestate" toml:"faultGameAbsolutePrestate"` DisputeMaxGameDepth uint64 `json:"faultGameMaxDepth" toml:"faultGameMaxDepth"` diff --git a/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go b/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go index b025855751b11..95585401b1182 100644 --- a/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go +++ b/op-deployer/pkg/deployer/upgrade/v2_0_0/upgrade_test.go @@ -21,7 +21,6 @@ import ( ) func TestUpgradeOPChainInput_OpChainConfigs(t *testing.T) { - t.Skip("skipping for upgrade 15") input := &UpgradeOPChainInput{ Prank: common.Address{0xaa}, Opcm: common.Address{0xbb}, @@ -55,7 +54,6 @@ func TestUpgradeOPChainInput_OpChainConfigs(t *testing.T) { } func TestUpgrader_Upgrade(t *testing.T) { - t.Skip("skipping for upgrade 15") lgr := testlog.Logger(t, slog.LevelDebug) forkedL1, stopL1, err := devnet.NewForkedSepolia(lgr) diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 74c54093a2e0d..b13a60023426d 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -436,13 +436,12 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, { ChainProofParams: state.ChainProofParams{ // Fast game - DisputeGameUsesSuperRoots: false, - DisputeGameType: 254, - DisputeAbsolutePrestate: defaultPrestate, - DisputeMaxGameDepth: 14 + 3 + 1, - DisputeSplitDepth: 14, - DisputeClockExtension: 0, - DisputeMaxClockDuration: 0, + DisputeGameType: 254, + DisputeAbsolutePrestate: defaultPrestate, + DisputeMaxGameDepth: 14 + 3 + 1, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 0, }, VMType: state.VMTypeAlphabet, UseCustomOracle: true, @@ -453,25 +452,23 @@ func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, { ChainProofParams: state.ChainProofParams{ // Alphabet game - DisputeGameUsesSuperRoots: false, - DisputeGameType: 255, - DisputeAbsolutePrestate: defaultPrestate, - DisputeMaxGameDepth: 14 + 3 + 1, - DisputeSplitDepth: 14, - DisputeClockExtension: 0, - DisputeMaxClockDuration: 1200, + DisputeGameType: 255, + DisputeAbsolutePrestate: defaultPrestate, + DisputeMaxGameDepth: 14 + 3 + 1, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 1200, }, VMType: state.VMTypeAlphabet, }, { ChainProofParams: state.ChainProofParams{ - DisputeGameUsesSuperRoots: false, - DisputeGameType: 0, - DisputeAbsolutePrestate: cannonPrestate(root, allocType), - DisputeMaxGameDepth: 50, - DisputeSplitDepth: 14, - DisputeClockExtension: 0, - DisputeMaxClockDuration: 1200, + DisputeGameType: 0, + DisputeAbsolutePrestate: cannonPrestate(root, allocType), + DisputeMaxGameDepth: 50, + DisputeSplitDepth: 14, + DisputeClockExtension: 0, + DisputeMaxClockDuration: 1200, }, VMType: cannonVMType(allocType), }, diff --git a/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol b/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol index 5d4abd72c79f5..7f9f5c2d0d137 100644 --- a/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol +++ b/packages/contracts-bedrock/interfaces/L1/IETHLockbox.sol @@ -12,20 +12,21 @@ interface IETHLockbox is IProxyAdminOwnedBase, ISemver { error ETHLockbox_InsufficientBalance(); error ETHLockbox_NoWithdrawalTransactions(); error ETHLockbox_DifferentProxyAdminOwner(); + error ETHLockbox_DifferentSuperchainConfig(); event Initialized(uint8 version); - event ETHLocked(address indexed portal, uint256 amount); - event ETHUnlocked(address indexed portal, uint256 amount); - event PortalAuthorized(address indexed portal); - event LockboxAuthorized(address indexed lockbox); - event LiquidityMigrated(address indexed lockbox, uint256 amount); - event LiquidityReceived(address indexed lockbox, uint256 amount); + event ETHLocked(IOptimismPortal2 indexed portal, uint256 amount); + event ETHUnlocked(IOptimismPortal2 indexed portal, uint256 amount); + event PortalAuthorized(IOptimismPortal2 indexed portal); + event LockboxAuthorized(IETHLockbox indexed lockbox); + event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); + event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); function initialize(ISuperchainConfig _superchainConfig, IOptimismPortal2[] calldata _portals) external; function superchainConfig() external view returns (ISuperchainConfig); function paused() external view returns (bool); - function authorizedPortals(address) external view returns (bool); - function authorizedLockboxes(address) external view returns (bool); + function authorizedPortals(IOptimismPortal2) external view returns (bool); + function authorizedLockboxes(IETHLockbox) external view returns (bool); function receiveLiquidity() external payable; function lockETH() external payable; function unlockETH(uint256 _value) external; diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol index d2f83d685d99e..09821208687ac 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOPContractsManager.sol @@ -108,7 +108,6 @@ interface IOPContractsManager { string saltMixer; uint64 gasLimit; // Configurable dispute game parameters. - bool disputeGameUsesSuperRoots; GameType disputeGameType; Claim disputeAbsolutePrestate; uint256 disputeMaxGameDepth; @@ -176,7 +175,6 @@ interface IOPContractsManager { ISystemConfig systemConfigProxy; IProxyAdmin proxyAdmin; Claim absolutePrestate; - bool disputeGameUsesSuperRoots; } struct AddGameInput { diff --git a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol b/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol deleted file mode 100644 index 057139005a96a..0000000000000 --- a/packages/contracts-bedrock/interfaces/L1/IOPContractsManagerLegacyUpgrade.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; -import { Claim } from "src/dispute/lib/Types.sol"; - -/// @title IOPContractsManagerLegacyUpgrade -/// @notice Interface for the legacy OPContractsManager upgrade function. -/// This interface is used to test Upgrade 13 and 14 paths and can be safely removed -/// after those upgrades are completed. Only difference in the new struct is the added -/// disputeGameUsesSuperRoots boolean. -interface IOPContractsManagerLegacyUpgrade { - struct OpChainConfig { - ISystemConfig systemConfigProxy; - IProxyAdmin proxyAdmin; - Claim absolutePrestate; - } - - function upgrade(OpChainConfig[] memory _opChainConfigs) external; -} diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol index f9cfc5325436c..673b3c778f85a 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol @@ -37,6 +37,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { error OptimismPortal_InvalidSuperRootProof(); error OptimismPortal_InvalidOutputRootChainId(); error OptimismPortal_WrongProofMethod(); + error OptimismPortal_MigratingToSameRegistry(); error Encoding_EmptySuperRoot(); error Encoding_InvalidSuperRootVersion(); error OutOfGas(); @@ -49,7 +50,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); event ETHMigrated(address indexed lockbox, uint256 ethBalance); - event LockboxUpdated(address oldLockbox, address newLockbox); + event PortalMigrated(IETHLockbox oldLockbox, IETHLockbox newLockbox, IAnchorStateRegistry oldAnchorStateRegistry, IAnchorStateRegistry newAnchorStateRegistry); receive() external payable; @@ -68,7 +69,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { function disputeGameFactory() external view returns (IDisputeGameFactory); function disputeGameFinalityDelaySeconds() external view returns (uint256); function donateETH() external payable; - function updateLockbox(IETHLockbox _newLockbox) external; + function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external; function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external; function finalizeWithdrawalTransactionExternalProof( Types.WithdrawalTransaction memory _tx, @@ -81,8 +82,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig, IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external; function initVersion() external view returns (uint8); @@ -123,8 +123,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { function systemConfig() external view returns (ISystemConfig); function upgrade( IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external; function version() external pure returns (string memory); diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol index 7cd674a0c1835..1b4eb759787cc 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortalInterop.sol @@ -38,6 +38,7 @@ interface IOptimismPortalInterop is IProxyAdminOwnedBase { error OptimismPortal_InvalidSuperRootProof(); error OptimismPortal_InvalidOutputRootChainId(); error OptimismPortal_WrongProofMethod(); + error OptimismPortal_MigratingToSameRegistry(); error Encoding_EmptySuperRoot(); error Encoding_InvalidSuperRootVersion(); error OutOfGas(); @@ -50,7 +51,7 @@ interface IOptimismPortalInterop is IProxyAdminOwnedBase { event WithdrawalProven(bytes32 indexed withdrawalHash, address indexed from, address indexed to); event WithdrawalProvenExtension1(bytes32 indexed withdrawalHash, address indexed proofSubmitter); event ETHMigrated(address indexed lockbox, uint256 ethBalance); - event LockboxUpdated(address oldLockbox, address newLockbox); + event PortalMigrated(IETHLockbox oldLockbox, IETHLockbox newLockbox, IAnchorStateRegistry oldAnchorStateRegistry, IAnchorStateRegistry newAnchorStateRegistry); receive() external payable; @@ -69,7 +70,7 @@ interface IOptimismPortalInterop is IProxyAdminOwnedBase { function disputeGameFactory() external view returns (IDisputeGameFactory); function disputeGameFinalityDelaySeconds() external view returns (uint256); function donateETH() external payable; - function updateLockbox(IETHLockbox _newLockbox) external; + function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external; function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external; function finalizeWithdrawalTransactionExternalProof( Types.WithdrawalTransaction memory _tx, @@ -83,8 +84,7 @@ interface IOptimismPortalInterop is IProxyAdminOwnedBase { ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig, IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external; function initVersion() external view returns (uint8); @@ -126,8 +126,7 @@ interface IOptimismPortalInterop is IProxyAdminOwnedBase { function systemConfig() external view returns (ISystemConfig); function upgrade( IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external; function version() external pure returns (string memory); diff --git a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol index a8457150ec48b..0812811993bc3 100644 --- a/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol +++ b/packages/contracts-bedrock/scripts/deploy/ChainAssertions.sol @@ -423,11 +423,14 @@ library ChainAssertions { if (_isProxy) { require(ethLockbox.superchainConfig() == superchainConfig, "CHECK-ELB-20"); - require(ethLockbox.authorizedPortals(_contracts.OptimismPortal), "CHECK-ELB-30"); + require(ethLockbox.authorizedPortals(IOptimismPortal(payable(_contracts.OptimismPortal))), "CHECK-ELB-30"); require(ethLockbox.proxyAdminOwner() == _cfg.finalSystemOwner(), "CHECK-ELB-40"); } else { require(address(ethLockbox.superchainConfig()) == address(0), "CHECK-ELB-50"); - require(ethLockbox.authorizedPortals(_contracts.OptimismPortal) == false, "CHECK-ELB-60"); + require( + ethLockbox.authorizedPortals(IOptimismPortal(payable(_contracts.OptimismPortal))) == false, + "CHECK-ELB-60" + ); } } diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index 1a353f2f063f4..efefac73aa5a1 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -969,7 +969,6 @@ contract Deploy is Deployer { ), saltMixer: saltMixer, gasLimit: uint64(cfg.l2GenesisBlockGasLimit()), - disputeGameUsesSuperRoots: false, disputeGameType: GameTypes.PERMISSIONED_CANNON, disputeAbsolutePrestate: Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())), disputeMaxGameDepth: cfg.faultGameMaxDepth(), diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index 776803cb6df1f..b1fa022d91f13 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -371,7 +371,7 @@ contract DeployImplementationsOutput is BaseDeployIO { DeployUtils.assertInitialized({ _contractAddress: address(lockbox), _isProxy: false, _slot: 0, _offset: 0 }); require(address(lockbox.superchainConfig()) == address(0), "ELB-10"); - require(lockbox.authorizedPortals(address(optimismPortalImpl())) == false, "ELB-20"); + require(lockbox.authorizedPortals(optimismPortalImpl()) == false, "ELB-20"); } function assertValidDelayedWETHImpl(DeployImplementationsInput _dii) internal view { diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol index 2978582420a2d..042f06a2a022e 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPChain.s.sol @@ -52,7 +52,6 @@ contract DeployOPChainInput is BaseDeployIO { uint64 internal _gasLimit; // Configurable dispute game inputs - bool internal _disputeGameUsesSuperRoots; GameType internal _disputeGameType; Claim internal _disputeAbsolutePrestate; uint256 internal _disputeMaxGameDepth; @@ -100,9 +99,6 @@ contract DeployOPChainInput is BaseDeployIO { _operatorFeeScalar = SafeCast.toUint32(_value); } else if (_sel == this.operatorFeeConstant.selector) { _operatorFeeConstant = SafeCast.toUint64(_value); - } else if (_sel == this.disputeGameUsesSuperRoots.selector) { - require(_value == 0 || _value == 1, "DeployOPChainInput: invalid disputeGameUsesSuperRoots"); - _disputeGameUsesSuperRoots = _value == 1; } else { revert("DeployOPChainInput: unknown selector"); } @@ -199,10 +195,6 @@ contract DeployOPChainInput is BaseDeployIO { return _gasLimit; } - function disputeGameUsesSuperRoots() public view returns (bool) { - return _disputeGameUsesSuperRoots; - } - function disputeGameType() public view returns (GameType) { return _disputeGameType; } @@ -388,7 +380,6 @@ contract DeployOPChain is Script { startingAnchorRoot: _doi.startingAnchorRoot(), saltMixer: _doi.saltMixer(), gasLimit: _doi.gasLimit(), - disputeGameUsesSuperRoots: _doi.disputeGameUsesSuperRoots(), disputeGameType: _doi.disputeGameType(), disputeAbsolutePrestate: _doi.disputeAbsolutePrestate(), disputeMaxGameDepth: _doi.disputeMaxGameDepth(), @@ -655,7 +646,7 @@ contract DeployOPChain is Script { IETHLockbox lockbox = _doo.ethLockboxProxy(); require(address(lockbox.superchainConfig()) == address(_doi.opcm().superchainConfig()), "ETHLOCKBOX-10"); - require(lockbox.authorizedPortals(address(_doo.optimismPortalProxy())), "ETHLOCKBOX-20"); + require(lockbox.authorizedPortals(_doo.optimismPortalProxy()), "ETHLOCKBOX-20"); require(lockbox.proxyAdminOwner() == _doi.opChainProxyAdminOwner(), "ETHLOCKBOX-30"); } diff --git a/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json b/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json index d0e9c8fb9a626..50b2a344f0579 100644 --- a/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json +++ b/packages/contracts-bedrock/snapshots/abi/ETHLockbox.json @@ -33,7 +33,7 @@ { "inputs": [ { - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "", "type": "address" } @@ -52,7 +52,7 @@ { "inputs": [ { - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "", "type": "address" } @@ -183,7 +183,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "portal", "type": "address" }, @@ -202,7 +202,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "portal", "type": "address" }, @@ -234,7 +234,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "lockbox", "type": "address" }, @@ -253,7 +253,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "lockbox", "type": "address" }, @@ -272,7 +272,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "lockbox", "type": "address" } @@ -285,7 +285,7 @@ "inputs": [ { "indexed": true, - "internalType": "address", + "internalType": "contract IOptimismPortal2", "name": "portal", "type": "address" } @@ -298,6 +298,11 @@ "name": "ETHLockbox_DifferentProxyAdminOwner", "type": "error" }, + { + "inputs": [], + "name": "ETHLockbox_DifferentSuperchainConfig", + "type": "error" + }, { "inputs": [], "name": "ETHLockbox_InsufficientBalance", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json index 381040d946efc..47e5d823e8c78 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManager.json @@ -293,11 +293,6 @@ "name": "gasLimit", "type": "uint64" }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" - }, { "internalType": "GameType", "name": "disputeGameType", @@ -637,11 +632,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", @@ -672,11 +662,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json index a3e0783fd0d34..b24342213aaed 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployer.json @@ -186,11 +186,6 @@ "name": "gasLimit", "type": "uint64" }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" - }, { "internalType": "GameType", "name": "disputeGameType", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json index a3e0783fd0d34..b24342213aaed 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerDeployerInterop.json @@ -186,11 +186,6 @@ "name": "gasLimit", "type": "uint64" }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" - }, { "internalType": "GameType", "name": "disputeGameType", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json index 75a4caf40467b..bc3cd92692052 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerGameTypeAdder.json @@ -316,11 +316,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json index d9ac40df7ca94..42887ce92c3d6 100644 --- a/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json +++ b/packages/contracts-bedrock/snapshots/abi/OPContractsManagerUpgrader.json @@ -213,11 +213,6 @@ "internalType": "Claim", "name": "absolutePrestate", "type": "bytes32" - }, - { - "internalType": "bool", - "name": "disputeGameUsesSuperRoots", - "type": "bool" } ], "internalType": "struct OPContractsManager.OpChainConfig[]", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index 5a60bf69cd620..9719bfe65046c 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -285,11 +285,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "bool", - "name": "_superRootsActive", - "type": "bool" } ], "name": "initialize", @@ -317,6 +312,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IETHLockbox", + "name": "_newLockbox", + "type": "address" + }, + { + "internalType": "contract IAnchorStateRegistry", + "name": "_newAnchorStateRegistry", + "type": "address" + } + ], + "name": "migrateToSuperRoots", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -738,19 +751,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "_newLockbox", - "type": "address" - } - ], - "name": "updateLockbox", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -762,11 +762,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "bool", - "name": "_superRootsActive", - "type": "bool" } ], "name": "upgrade", @@ -824,18 +819,30 @@ "inputs": [ { "indexed": false, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "oldLockbox", "type": "address" }, { "indexed": false, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "newLockbox", "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "oldAnchorStateRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "newAnchorStateRegistry", + "type": "address" } ], - "name": "LockboxUpdated", + "name": "PortalMigrated", "type": "event" }, { @@ -1037,6 +1044,11 @@ "name": "OptimismPortal_InvalidSuperRootProof", "type": "error" }, + { + "inputs": [], + "name": "OptimismPortal_MigratingToSameRegistry", + "type": "error" + }, { "inputs": [], "name": "OptimismPortal_NoReentrancy", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json index 4835f00aa2ca9..be5f6e027caa1 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortalInterop.json @@ -285,11 +285,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "bool", - "name": "_superRootsActive", - "type": "bool" } ], "name": "initialize", @@ -317,6 +312,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IETHLockbox", + "name": "_newLockbox", + "type": "address" + }, + { + "internalType": "contract IAnchorStateRegistry", + "name": "_newAnchorStateRegistry", + "type": "address" + } + ], + "name": "migrateToSuperRoots", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -756,19 +769,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "_newLockbox", - "type": "address" - } - ], - "name": "updateLockbox", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -780,11 +780,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "bool", - "name": "_superRootsActive", - "type": "bool" } ], "name": "upgrade", @@ -842,18 +837,30 @@ "inputs": [ { "indexed": false, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "oldLockbox", "type": "address" }, { "indexed": false, - "internalType": "address", + "internalType": "contract IETHLockbox", "name": "newLockbox", "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "oldAnchorStateRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IAnchorStateRegistry", + "name": "newAnchorStateRegistry", + "type": "address" } ], - "name": "LockboxUpdated", + "name": "PortalMigrated", "type": "event" }, { @@ -1055,6 +1062,11 @@ "name": "OptimismPortal_InvalidSuperRootProof", "type": "error" }, + { + "inputs": [], + "name": "OptimismPortal_MigratingToSameRegistry", + "type": "error" + }, { "inputs": [], "name": "OptimismPortal_NoReentrancy", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index ea49df2bac3c4..5272f8721ea80 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -4,8 +4,8 @@ "sourceCodeHash": "0xe772f7db8033e4a738850cb28ac4849d3a454c93732135a8a10d4f7cb498088e" }, "src/L1/ETHLockbox.sol:ETHLockbox": { - "initCodeHash": "0x599593bf96170081d2fd191bb2f61eaa2d094faafb6a583a34b3eea2dc49e5c6", - "sourceCodeHash": "0x34da4a8a0e32cabce97056f10cc1c8231f14a1cd706ae63ee0bef3c1f65f3e13" + "initCodeHash": "0x3b5eac93a55c8fc20a4a517f5683aa1b2aa4424652b3bc2e9d4d6f4b6c9219e9", + "sourceCodeHash": "0x350aefb43cd1c40b7555473e09ad901970b813b7fd16ff4f009194810075685c" }, "src/L1/L1CrossDomainMessenger.sol:L1CrossDomainMessenger": { "initCodeHash": "0x5a6272f6bd4346da460b7ff2ebc426d158d7ffc65dc0f2c0651a9e6d545bfd03", @@ -20,16 +20,16 @@ "sourceCodeHash": "0x44797707aea8c63dec049a02d69ea056662a06e5cf320028ab8b388634bf1c67" }, "src/L1/OPContractsManager.sol:OPContractsManager": { - "initCodeHash": "0x9e7496b9720bae99e5db18dc18c99b5c06cd755c6d4079e0d38c56d625fe7b4e", - "sourceCodeHash": "0x8302de0ccd551512f2430d545ea55c938b002701d2292fd7aad2b4d2b975727b" + "initCodeHash": "0xbe05d8be3a52f8d3700d9c6a99dde5be57dc85ad4e02eea17a9884db1ffbe760", + "sourceCodeHash": "0xa6e8f1a900ab1e9c50f666668126d764e10737e74aff056342baa36d63f20a48" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x774dd0c79fb11ea7ecbbffb28d1c005b72790577adbeeeb881c2a180e2feaadf", - "sourceCodeHash": "0x2d36fbd2326ceafe89ca1a3b80c223ad7f934e89e996d20b847498f015316bdf" + "initCodeHash": "0xbc7db7b1016025228d99a40db1760290b333bb90563dff5514fd253fd91019ba", + "sourceCodeHash": "0x367e0a1c609e3c82db41a8c5e056108cbbae58d61bfeb08707449655485ba8ab" }, "src/L1/OptimismPortalInterop.sol:OptimismPortalInterop": { - "initCodeHash": "0x9c35223461ee313b77eafb727c51300596975b5134ff51847d8fab424b4dcd0b", - "sourceCodeHash": "0xa1ecf8a19638dd24d80f62e2faef132afa15c6945c14133fe58709015fcb14a6" + "initCodeHash": "0x1f46f4d541697fb2c3aea36170d27dbc1a5cd5c0adb2bdf195ba6c7b53c1a70b", + "sourceCodeHash": "0x7d578d9ba963da4ef83e445912772b21e4dc011d2f038243202ccae6e15520b4" }, "src/L1/ProtocolVersions.sol:ProtocolVersions": { "initCodeHash": "0x5a76c8530cb24cf23d3baacc6eefaac226382af13f1e2a35535d2ec2b0573b29", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json b/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json index 4ba870d921ec9..0f69cd3e54cf4 100644 --- a/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json +++ b/packages/contracts-bedrock/snapshots/storageLayout/ETHLockbox.json @@ -25,13 +25,13 @@ "label": "authorizedPortals", "offset": 0, "slot": "1", - "type": "mapping(address => bool)" + "type": "mapping(contract IOptimismPortal2 => bool)" }, { "bytes": "32", "label": "authorizedLockboxes", "offset": 0, "slot": "2", - "type": "mapping(address => bool)" + "type": "mapping(contract IETHLockbox => bool)" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L1/ETHLockbox.sol b/packages/contracts-bedrock/src/L1/ETHLockbox.sol index 3931c90fb3e0e..364194a39dbd3 100644 --- a/packages/contracts-bedrock/src/L1/ETHLockbox.sol +++ b/packages/contracts-bedrock/src/L1/ETHLockbox.sol @@ -34,46 +34,49 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { /// @notice Thrown when the admin owner of the lockbox is different from the admin owner of the proxy admin. error ETHLockbox_DifferentProxyAdminOwner(); + /// @notice Thrown when any authorized portal has a different SuperchainConfig. + error ETHLockbox_DifferentSuperchainConfig(); + /// @notice Emitted when ETH is locked in the lockbox by an authorized portal. /// @param portal The address of the portal that locked the ETH. /// @param amount The amount of ETH locked. - event ETHLocked(address indexed portal, uint256 amount); + event ETHLocked(IOptimismPortal indexed portal, uint256 amount); /// @notice Emitted when ETH is unlocked from the lockbox by an authorized portal. /// @param portal The address of the portal that unlocked the ETH. /// @param amount The amount of ETH unlocked. - event ETHUnlocked(address indexed portal, uint256 amount); + event ETHUnlocked(IOptimismPortal indexed portal, uint256 amount); /// @notice Emitted when a portal is authorized to lock and unlock ETH. /// @param portal The address of the portal that was authorized. - event PortalAuthorized(address indexed portal); + event PortalAuthorized(IOptimismPortal indexed portal); /// @notice Emitted when an ETH lockbox is authorized to migrate its liquidity to the current ETH lockbox. /// @param lockbox The address of the ETH lockbox that was authorized. - event LockboxAuthorized(address indexed lockbox); + event LockboxAuthorized(IETHLockbox indexed lockbox); /// @notice Emitted when ETH liquidity is migrated from the current ETH lockbox to another. /// @param lockbox The address of the ETH lockbox that was migrated. - event LiquidityMigrated(address indexed lockbox, uint256 amount); + event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); /// @notice Emitted when ETH liquidity is received during an authorized lockbox migration. /// @param lockbox The address of the ETH lockbox that received the liquidity. /// @param amount The amount of ETH received. - event LiquidityReceived(address indexed lockbox, uint256 amount); + event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); /// @notice The address of the SuperchainConfig contract. ISuperchainConfig public superchainConfig; /// @notice Mapping of authorized portals. - mapping(address => bool) public authorizedPortals; + mapping(IOptimismPortal => bool) public authorizedPortals; /// @notice Mapping of authorized lockboxes. - mapping(address => bool) public authorizedLockboxes; + mapping(IETHLockbox => bool) public authorizedLockboxes; /// @notice Semantic version. - /// @custom:semver 0.0.1 + /// @custom:semver 1.0.0 function version() public view virtual returns (string memory) { - return "0.0.1"; + return "1.0.0"; } /// @notice Constructs the ETHLockbox contract. @@ -93,33 +96,44 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { { superchainConfig = ISuperchainConfig(_superchainConfig); for (uint256 i; i < _portals.length; i++) { - _authorizePortal(address(_portals[i])); + _authorizePortal(_portals[i]); } } + /// @notice Getter for the current paused status. + function paused() public view returns (bool) { + return superchainConfig.paused(); + } + /// @notice Authorizes a portal to lock and unlock ETH. /// @param _portal The address of the portal to authorize. function authorizePortal(IOptimismPortal _portal) external { + // Check that the sender is the proxy admin owner. if (msg.sender != proxyAdminOwner()) revert ETHLockbox_Unauthorized(); - _authorizePortal(address(_portal)); - } - /// @notice Getter for the current paused status. - function paused() public view returns (bool) { - return superchainConfig.paused(); + // Authorize the portal. + _authorizePortal(_portal); } /// @notice Receives the ETH liquidity migrated from an authorized lockbox. function receiveLiquidity() external payable { - if (!authorizedLockboxes[msg.sender]) revert ETHLockbox_Unauthorized(); - emit LiquidityReceived(msg.sender, msg.value); + // Check that the sender is authorized to trigger this function. + IETHLockbox sender = IETHLockbox(payable(msg.sender)); + if (!authorizedLockboxes[sender]) revert ETHLockbox_Unauthorized(); + + // Emit the event. + emit LiquidityReceived(sender, msg.value); } /// @notice Locks ETH in the lockbox. /// Called by an authorized portal on a deposit to lock the ETH value. function lockETH() external payable { - if (!authorizedPortals[msg.sender]) revert ETHLockbox_Unauthorized(); - emit ETHLocked(msg.sender, msg.value); + // Check that the sender is authorized to trigger this function. + IOptimismPortal sender = IOptimismPortal(payable(msg.sender)); + if (!authorizedPortals[sender]) revert ETHLockbox_Unauthorized(); + + // Emit the event. + emit ETHLocked(sender, msg.value); } /// @notice Unlocks ETH from the lockbox. @@ -127,27 +141,44 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { /// Cannot be called if the lockbox is paused. /// @param _value The amount of ETH to unlock. function unlockETH(uint256 _value) external { + // Unlocks are blocked when paused, locks are not. if (paused()) revert ETHLockbox_Paused(); - if (!authorizedPortals[msg.sender]) revert ETHLockbox_Unauthorized(); + + // Check that the sender is authorized to trigger this function. + IOptimismPortal sender = IOptimismPortal(payable(msg.sender)); + if (!authorizedPortals[sender]) revert ETHLockbox_Unauthorized(); + + // Check that we have enough balance to process the unlock. if (_value > address(this).balance) revert ETHLockbox_InsufficientBalance(); - /// NOTE: Check l2Sender is not set to avoid this function to be called as a target on a withdrawal transaction - if (IOptimismPortal(payable(msg.sender)).l2Sender() != Constants.DEFAULT_L2_SENDER) { + + // Check that the sender is not executing a withdrawal transaction. + if (sender.l2Sender() != Constants.DEFAULT_L2_SENDER) { revert ETHLockbox_NoWithdrawalTransactions(); } - // Using `donateETH` to avoid triggering a deposit - IOptimismPortal(payable(msg.sender)).donateETH{ value: _value }(); - emit ETHUnlocked(msg.sender, _value); + // Using donateETH to avoid triggering a deposit. + sender.donateETH{ value: _value }(); + + // Emit the event. + emit ETHUnlocked(sender, _value); } - /// @notice Authorizes an ETH lockbox to migrate its liquidity to the current ETH lockbox. + /// @notice Authorizes an ETH lockbox to migrate its liquidity to the current ETH lockbox. We + /// allow this function to be called more than once for the same lockbox. A lockbox + /// cannot be removed from the authorized list once added. /// @param _lockbox The address of the ETH lockbox to authorize. function authorizeLockbox(IETHLockbox _lockbox) external { + // Check that the sender is the proxy admin owner. if (msg.sender != proxyAdminOwner()) revert ETHLockbox_Unauthorized(); + + // Check that the lockbox has the same proxy admin owner. if (!_sameProxyAdminOwner(address(_lockbox))) revert ETHLockbox_DifferentProxyAdminOwner(); - authorizedLockboxes[address(_lockbox)] = true; - emit LockboxAuthorized(address(_lockbox)); + // Authorize the lockbox. + authorizedLockboxes[_lockbox] = true; + + // Emit the event. + emit LockboxAuthorized(_lockbox); } /// @notice Migrates liquidity from the current ETH lockbox to another. @@ -156,18 +187,32 @@ contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ISemver { /// from the ETHLockbox on finalized withdrawals. /// @param _lockbox The address of the ETH lockbox to migrate liquidity to. function migrateLiquidity(IETHLockbox _lockbox) external { + // Check that the sender is the proxy admin owner. if (msg.sender != proxyAdminOwner()) revert ETHLockbox_Unauthorized(); + + // Check that the lockbox has the same proxy admin owner. if (!_sameProxyAdminOwner(address(_lockbox))) revert ETHLockbox_DifferentProxyAdminOwner(); + // Receive the liquidity. IETHLockbox(_lockbox).receiveLiquidity{ value: address(this).balance }(); - emit LiquidityMigrated(address(_lockbox), address(this).balance); + + // Emit the event. + emit LiquidityMigrated(_lockbox, address(this).balance); } /// @notice Authorizes a portal to lock and unlock ETH. /// @param _portal The address of the portal to authorize. - function _authorizePortal(address _portal) internal { - if (!_sameProxyAdminOwner(_portal)) revert ETHLockbox_DifferentProxyAdminOwner(); + function _authorizePortal(IOptimismPortal _portal) internal { + // Check that the portal has the same proxy admin owner. + if (!_sameProxyAdminOwner(address(_portal))) revert ETHLockbox_DifferentProxyAdminOwner(); + + // Check that the portal has the same superchain config. + if (_portal.superchainConfig() != superchainConfig) revert ETHLockbox_DifferentSuperchainConfig(); + + // Authorize the portal. authorizedPortals[_portal] = true; + + // Emit the event. emit PortalAuthorized(_portal); } } diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 096b46c5bd477..cca2cc3bc0594 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -630,9 +630,7 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { } // Call `upgrade` on the OptimismPortal contract. - IOptimismPortal(payable(opChainAddrs.optimismPortal)).upgrade( - newAnchorStateRegistryProxy, ethLockbox, _opChainConfigs[i].disputeGameUsesSuperRoots - ); + IOptimismPortal(payable(opChainAddrs.optimismPortal)).upgrade(newAnchorStateRegistryProxy, ethLockbox); } // We also need to redeploy the dispute games because the AnchorStateRegistry is new. @@ -724,10 +722,17 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { // Modify the params with the new vm values. params.anchorStateRegistry = IAnchorStateRegistry(address(_newAnchorStateRegistryProxy)); params.vm = IBigStepper(impls.mipsImpl); - if (Claim.unwrap(_opChainConfig.absolutePrestate) == bytes32(0)) { + + // If the prestate is set in the config, use it. If not set, we'll try to use the prestate + // that already exists on the current dispute game. + if (Claim.unwrap(_opChainConfig.absolutePrestate) != bytes32(0)) { + params.absolutePrestate = _opChainConfig.absolutePrestate; + } + + // As a sanity check, if the prestate is zero here, revert. + if (params.absolutePrestate.raw() == bytes32(0)) { revert OPContractsManager.PrestateNotSet(); } - params.absolutePrestate = _opChainConfig.absolutePrestate; IDisputeGame newGame; if (GameType.unwrap(_gameType) == GameType.unwrap(GameTypes.PERMISSIONED_CANNON)) { @@ -889,7 +894,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), implementation.l1ERC721BridgeImpl, data ); - data = encodeOptimismPortalInitializer(_input, output, _superchainConfig); + data = encodeOptimismPortalInitializer(output, _superchainConfig); upgradeToAndCall( output.opChainProxyAdmin, address(output.optimismPortalProxy), implementation.optimismPortalImpl, data ); @@ -1044,7 +1049,6 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { /// @notice Helper method for encoding the OptimismPortal initializer data. function encodeOptimismPortalInitializer( - OPContractsManager.DeployInput memory _input, OPContractsManager.DeployOutput memory _output, ISuperchainConfig _superchainConfig ) @@ -1055,13 +1059,7 @@ contract OPContractsManagerDeployer is OPContractsManagerBase { { return abi.encodeCall( IOptimismPortal.initialize, - ( - _output.systemConfigProxy, - _superchainConfig, - _output.anchorStateRegistryProxy, - _output.ethLockboxProxy, - _input.disputeGameUsesSuperRoots - ) + (_output.systemConfigProxy, _superchainConfig, _output.anchorStateRegistryProxy, _output.ethLockboxProxy) ); } @@ -1251,7 +1249,6 @@ contract OPContractsManager is ISemver { string saltMixer; uint64 gasLimit; // Configurable dispute game parameters. - bool disputeGameUsesSuperRoots; GameType disputeGameType; Claim disputeAbsolutePrestate; uint256 disputeMaxGameDepth; @@ -1319,7 +1316,6 @@ contract OPContractsManager is ISemver { ISystemConfig systemConfigProxy; IProxyAdmin proxyAdmin; Claim absolutePrestate; - bool disputeGameUsesSuperRoots; } struct AddGameInput { @@ -1345,9 +1341,9 @@ contract OPContractsManager is ISemver { // -------- Constants and Variables -------- - /// @custom:semver 1.10.0 + /// @custom:semver 1.11.0 function version() public pure virtual returns (string memory) { - return "1.10.0"; + return "1.11.0"; } OPContractsManagerGameTypeAdder public immutable opcmGameTypeAdder; diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 5b8cef6cc83dd..a6478226fb00b 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -160,7 +160,14 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Emitted when the ETHLockbox contract is updated. /// @param oldLockbox The address of the old ETHLockbox contract. /// @param newLockbox The address of the new ETHLockbox contract. - event LockboxUpdated(address oldLockbox, address newLockbox); + /// @param oldAnchorStateRegistry The address of the old AnchorStateRegistry contract. + /// @param newAnchorStateRegistry The address of the new AnchorStateRegistry contract. + event PortalMigrated( + IETHLockbox oldLockbox, + IETHLockbox newLockbox, + IAnchorStateRegistry oldAnchorStateRegistry, + IAnchorStateRegistry newAnchorStateRegistry + ); /// @notice Thrown when a withdrawal has already been finalized. error OptimismPortal_AlreadyFinalized(); @@ -222,6 +229,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when an output root chain id is invalid. error OptimismPortal_InvalidOutputRootChainId(); + /// @notice Thrown when trying to migrate to the same AnchorStateRegistry. + error OptimismPortal_MigratingToSameRegistry(); + /// @notice Reverts when paused. modifier whenNotPaused() { if (paused()) revert OptimismPortal_CallPaused(); @@ -229,9 +239,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase } /// @notice Semantic version. - /// @custom:semver 4.0.0 + /// @custom:semver 4.1.0 function version() public pure virtual returns (string memory) { - return "4.0.0"; + return "4.1.0"; } /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. @@ -245,13 +255,11 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @param _superchainConfig Address of the SuperchainConfig. /// @param _anchorStateRegistry Address of the AnchorStateRegistry. /// @param _ethLockbox Contract of the ETHLockbox. - /// @param _superRootsActive Whether the OptimismPortal is using Super Roots or Output Roots. function initialize( ISystemConfig _systemConfig, ISuperchainConfig _superchainConfig, IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external reinitializer(initVersion()) @@ -260,7 +268,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase superchainConfig = _superchainConfig; anchorStateRegistry = _anchorStateRegistry; ethLockbox = _ethLockbox; - superRootsActive = _superRootsActive; // Set the l2Sender slot, only if it is currently empty. This signals the first // initialization of the contract. @@ -274,18 +281,15 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Upgrades the OptimismPortal contract to have a reference to the AnchorStateRegistry. /// @param _anchorStateRegistry AnchorStateRegistry contract. /// @param _ethLockbox ETHLockbox contract. - /// @param _superRootsActive Whether the OptimismPortal is using Super Roots or Output Roots. function upgrade( IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - bool _superRootsActive + IETHLockbox _ethLockbox ) external reinitializer(initVersion()) { anchorStateRegistry = _anchorStateRegistry; ethLockbox = _ethLockbox; - superRootsActive = _superRootsActive; // Migrate the whole ETH balance to the ETHLockbox. _migrateLiquidity(); @@ -354,18 +358,46 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Intentionally empty. } - /// @notice Updates the ETHLockbox contract. - /// @dev This function MUST be called atomically with `ETHLockbox.migrateLiquidity()` - /// in the same transaction batch, or otherwise the OptimismPortal may not be able to - /// unlock ETH from the ETHLockbox on finalized withdrawals. + /// @notice Allows the owner of the ProxyAdmin to migrate the OptimismPortal to use a new + /// lockbox, point at a new AnchorStateRegistry, and start to use the Super Roots proof + /// method. Primarily used for OptimismPortal instances to join the interop set, but + /// can also be used to swap the proof method from Output Roots to Super Roots if the + /// provided lockbox is the same as the current one. + /// @dev It is possible to change lockboxes without migrating liquidity. This can cause one + /// of the OptimismPortal instances connected to the new lockbox to not be able to + /// unlock sufficient ETH to finalize withdrawals which would trigger reverts. To avoid + /// this issue, guarantee that this function is called atomically alongside the + /// ETHLockbox.migrateLiquidity() function within the same transaction. /// @param _newLockbox The address of the new ETHLockbox contract. - function updateLockbox(IETHLockbox _newLockbox) external { + function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external { + // Make sure the caller is the owner of the ProxyAdmin. if (msg.sender != proxyAdminOwner()) revert OptimismPortal_Unauthorized(); - address oldLockbox = address(ethLockbox); + // Chains can use this method to swap the proof method from Output Roots to Super Roots + // without joining the interop set. In this case, the old and new lockboxes will be the + // same. However, whether or not a chain is joining the interop set, all chains will need a + // new AnchorStateRegistry when migrating to Super Roots. We therefore check that the new + // AnchorStateRegistry is different than the old one to prevent this function from being + // accidentally misused. + if (anchorStateRegistry == _newAnchorStateRegistry) { + revert OptimismPortal_MigratingToSameRegistry(); + } + + // Update the ETHLockbox. + IETHLockbox oldLockbox = ethLockbox; ethLockbox = _newLockbox; - emit LockboxUpdated(oldLockbox, address(_newLockbox)); + // Update the AnchorStateRegistry. + IAnchorStateRegistry oldAnchorStateRegistry = anchorStateRegistry; + anchorStateRegistry = _newAnchorStateRegistry; + + // Set the proof method to Super Roots. We expect that migration will happen more than once + // for some chains (switching to single-chain Super Roots and then later joining the + // interop set) so we don't need to check that this is false. + superRootsActive = true; + + // Emit a PortalMigrated event. + emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); } /// @notice Proves a withdrawal transaction using a Super Root proof. Only callable when the diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol index da2e9559e645b..86ebf1cf2ff26 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol @@ -20,9 +20,9 @@ contract OptimismPortalInterop is OptimismPortal2 { /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. constructor(uint256 _proofMaturityDelaySeconds) OptimismPortal2(_proofMaturityDelaySeconds) { } - /// @custom:semver +interop.4 + /// @custom:semver +interop.5 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop.4"); + return string.concat(super.version(), "+interop.5"); } /// @notice Sets static configuration options for the L2 system. diff --git a/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol b/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol index 17ade24455429..962dba0fb7501 100644 --- a/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol +++ b/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol @@ -21,12 +21,12 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; contract ETHLockboxTest is CommonTest { error InvalidInitialization(); - event ETHLocked(address indexed portal, uint256 amount); - event ETHUnlocked(address indexed portal, uint256 amount); - event PortalAuthorized(address indexed portal); - event LockboxAuthorized(address indexed lockbox); - event LiquidityMigrated(address indexed lockbox, uint256 amount); - event LiquidityReceived(address indexed lockbox, uint256 amount); + event ETHLocked(IOptimismPortal indexed portal, uint256 amount); + event ETHUnlocked(IOptimismPortal indexed portal, uint256 amount); + event PortalAuthorized(IOptimismPortal indexed portal); + event LockboxAuthorized(IETHLockbox indexed lockbox); + event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); + event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); ProxyAdmin public proxyAdmin; address public proxyAdminOwner; @@ -45,7 +45,7 @@ contract ETHLockboxTest is CommonTest { /// @notice Tests the superchain config was correctly set during initialization. function test_initialization_succeeds() public view { assertEq(address(ethLockbox.superchainConfig()), address(superchainConfig)); - assertEq(ethLockbox.authorizedPortals(address(optimismPortal2)), true); + assertEq(ethLockbox.authorizedPortals(optimismPortal2), true); } /// @notice Tests it reverts when the contract is already initialized. @@ -72,6 +72,12 @@ contract ETHLockboxTest is CommonTest { assertEq(ethLockbox.paused(), true); } + /// @notice Tests that the version function returns a valid string. We avoid testing the + /// specific value of the string as it changes frequently. + function test_version_succeeds() public view { + assert(bytes(ethLockbox.version()).length > 0); + } + /// @notice Tests the liquidity is correctly received. function testFuzz_receiveLiquidity_succeeds(address _lockbox, uint256 _value) public { // Since on the fork the `_lockbox` fuzzed address doesn't exist, we skip the test @@ -88,7 +94,7 @@ contract ETHLockboxTest is CommonTest { ); // Authorize the lockbox if needed - if (!ethLockbox.authorizedLockboxes(_lockbox)) { + if (!ethLockbox.authorizedLockboxes(IETHLockbox(_lockbox))) { vm.prank(proxyAdminOwner); ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); } @@ -98,7 +104,7 @@ contract ETHLockboxTest is CommonTest { // Expect the `LiquidityReceived` event to be emitted vm.expectEmit(address(ethLockbox)); - emit LiquidityReceived(_lockbox, _value); + emit LiquidityReceived(IETHLockbox(_lockbox), _value); // Call the `receiveLiquidity` function vm.prank(address(_lockbox)); @@ -110,7 +116,7 @@ contract ETHLockboxTest is CommonTest { /// @notice Tests it reverts when the caller is not an authorized portal. function testFuzz_lockETH_unauthorizedPortal_reverts(address _caller) public { - vm.assume(!ethLockbox.authorizedPortals(_caller)); + vm.assume(!ethLockbox.authorizedPortals(IOptimismPortal2(payable(_caller)))); // Expect the revert with `Unauthorized` selector vm.expectRevert(IETHLockbox.ETHLockbox_Unauthorized.selector); @@ -134,7 +140,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHLocked` event vm.expectEmit(address(ethLockbox)); - emit ETHLocked(address(optimismPortal2), _amount); + emit ETHLocked(optimismPortal2, _amount); // Call the `lockETH` function with the portal vm.prank(address(optimismPortal2)); @@ -157,8 +163,14 @@ contract ETHLockboxTest is CommonTest { address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); + // Mock the SuperchainConfig on the portal to be the same as the SuperchainConfig on the + // lockbox. + vm.mockCall( + address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(superchainConfig) + ); + // Set the portal as an authorized portal if needed - if (!ethLockbox.authorizedPortals(address(_portal))) { + if (!ethLockbox.authorizedPortals(_portal)) { vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(_portal); } @@ -171,7 +183,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHLocked` event vm.expectEmit(address(ethLockbox)); - emit ETHLocked(address(_portal), _amount); + emit ETHLocked(_portal, _amount); // Call the `lockETH` function with the portal vm.prank(address(_portal)); @@ -196,7 +208,7 @@ contract ETHLockboxTest is CommonTest { /// @notice Tests it reverts when the caller is not an authorized portal. function testFuzz_unlockETH_unauthorizedPortal_reverts(address _caller, uint256 _value) public { - vm.assume(!ethLockbox.authorizedPortals(_caller)); + vm.assume(!ethLockbox.authorizedPortals(IOptimismPortal2(payable(_caller)))); // Expect the revert with `Unauthorized` selector vm.expectRevert(IETHLockbox.ETHLockbox_Unauthorized.selector); @@ -249,7 +261,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHUnlocked` event vm.expectEmit(address(ethLockbox)); - emit ETHUnlocked(address(optimismPortal2), _value); + emit ETHUnlocked(optimismPortal2, _value); // Call the `unlockETH` function with the portal vm.prank(address(optimismPortal2)); @@ -269,8 +281,14 @@ contract ETHLockboxTest is CommonTest { address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); + // Mock the SuperchainConfig on the portal to be the same as the SuperchainConfig on the + // lockbox. + vm.mockCall( + address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(superchainConfig) + ); + // Set the portal as an authorized portal if needed - if (!ethLockbox.authorizedPortals(address(_portal))) { + if (!ethLockbox.authorizedPortals(_portal)) { vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(_portal); } @@ -287,7 +305,7 @@ contract ETHLockboxTest is CommonTest { // Look for the emit of the `ETHUnlocked` event vm.expectEmit(address(ethLockbox)); - emit ETHUnlocked(address(optimismPortal2), _value); + emit ETHUnlocked(optimismPortal2, _value); // Call the `unlockETH` function with the portal vm.prank(address(optimismPortal2)); @@ -324,6 +342,28 @@ contract ETHLockboxTest is CommonTest { ethLockbox.authorizePortal(_portal); } + /// @notice Tests the authorizePortal function reverts when the portal has a different + /// SuperchainConfig than the one configured in the lockbox. + /// @param _portal The portal to authorize. + function testFuzz_authorizePortal_differentSuperchainConfig_reverts(IOptimismPortal2 _portal) public { + assumeNotForgeAddress(address(_portal)); + + // Mock the portal to have the right proxyAdminOwner. + vm.mockCall( + address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) + ); + + // Mock the portal to have the wrong SuperchainConfig. + vm.mockCall(address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(address(0))); + + // Expect the revert with `DifferentSuperchainConfig` selector + vm.expectRevert(IETHLockbox.ETHLockbox_DifferentSuperchainConfig.selector); + + // Call the `authorizePortal` function + vm.prank(proxyAdminOwner); + ethLockbox.authorizePortal(_portal); + } + /// @notice Tests the `authorizeLockbox` function succeeds using the `optimismPortal2` address as the portal. function test_authorizePortal_succeeds() public { // Calculate the correct storage slot for the mapping value @@ -336,14 +376,14 @@ contract ETHLockboxTest is CommonTest { // Expect the `PortalAuthorized` event to be emitted vm.expectEmit(address(ethLockbox)); - emit PortalAuthorized(address(optimismPortal2)); + emit PortalAuthorized(optimismPortal2); // Call the `authorizePortal` function with the portal vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(optimismPortal2); // Assert the portal is authorized - assertTrue(ethLockbox.authorizedPortals(address(optimismPortal2))); + assertTrue(ethLockbox.authorizedPortals(optimismPortal2)); } /// @notice Tests the `authorizeLockbox` function succeeds @@ -355,16 +395,22 @@ contract ETHLockboxTest is CommonTest { address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); + // Mock the SuperchainConfig on the portal to be the same as the SuperchainConfig on the + // Lockbox. + vm.mockCall( + address(_portal), abi.encodeCall(IOptimismPortal.superchainConfig, ()), abi.encode(superchainConfig) + ); + // Expect the `PortalAuthorized` event to be emitted vm.expectEmit(address(ethLockbox)); - emit PortalAuthorized(address(_portal)); + emit PortalAuthorized(_portal); // Call the `authorizePortal` function with the portal vm.prank(proxyAdminOwner); ethLockbox.authorizePortal(_portal); // Assert the portal is authorized - assertTrue(ethLockbox.authorizedPortals(address(_portal))); + assertTrue(ethLockbox.authorizedPortals(_portal)); } /// @notice Tests the `authorizeLockbox` function reverts when the caller is not the proxy admin. @@ -406,14 +452,14 @@ contract ETHLockboxTest is CommonTest { // Expect the `LockboxAuthorized` event to be emitted vm.expectEmit(address(ethLockbox)); - emit LockboxAuthorized(_lockbox); + emit LockboxAuthorized(IETHLockbox(_lockbox)); // Authorize the lockbox vm.prank(proxyAdminOwner); ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); // Assert the lockbox is authorized - assertTrue(ethLockbox.authorizedLockboxes(_lockbox)); + assertTrue(ethLockbox.authorizedLockboxes(IETHLockbox(_lockbox))); } /// @notice Tests the `migrateLiquidity` function reverts when the caller is not the proxy admin. @@ -457,9 +503,7 @@ contract ETHLockboxTest is CommonTest { vm.mockCall( address(_lockbox), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) ); - vm.mockCall( - address(_lockbox), abi.encodeCall(IETHLockbox.authorizedLockboxes, (address(ethLockbox))), abi.encode(true) - ); + vm.mockCall(address(_lockbox), abi.encodeCall(IETHLockbox.authorizedLockboxes, (ethLockbox)), abi.encode(true)); vm.mockCall(address(_lockbox), abi.encodeCall(IETHLockbox.receiveLiquidity, ()), abi.encode(true)); // Deal the balance to the lockbox @@ -471,7 +515,7 @@ contract ETHLockboxTest is CommonTest { // Expect the `LiquidityMigrated` event to be emitted vm.expectEmit(address(ethLockbox)); - emit LiquidityMigrated(_lockbox, ethLockboxBalanceBefore); + emit LiquidityMigrated(IETHLockbox(_lockbox), ethLockboxBalanceBefore); // Call the `migrateLiquidity` function with the lockbox vm.prank(proxyAdminOwner); diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 80880840d8bef..abdc2bb7c5c33 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -32,11 +32,11 @@ import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; +import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IOPContractsManager, @@ -46,7 +46,6 @@ import { IOPContractsManagerUpgrader, IOPContractsManagerContractsContainer } from "interfaces/L1/IOPContractsManager.sol"; -import { IOPContractsManagerLegacyUpgrade } from "interfaces/L1/IOPContractsManagerLegacyUpgrade.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; @@ -148,7 +147,6 @@ contract OPContractsManager_Deploy_Test is DeployOPChain_TestBase { startingAnchorRoot: _doi.startingAnchorRoot(), saltMixer: _doi.saltMixer(), gasLimit: _doi.gasLimit(), - disputeGameUsesSuperRoots: _doi.disputeGameUsesSuperRoots(), disputeGameType: _doi.disputeGameType(), disputeAbsolutePrestate: _doi.disputeAbsolutePrestate(), disputeMaxGameDepth: _doi.disputeMaxGameDepth(), @@ -273,8 +271,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { IOPContractsManager.OpChainConfig({ systemConfigProxy: systemConfig, proxyAdmin: proxyAdmin, - absolutePrestate: absolutePrestate, - disputeGameUsesSuperRoots: false + absolutePrestate: absolutePrestate }) ); @@ -289,35 +286,12 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { faultDisputeGame = IFaultDisputeGame(address(artifacts.mustGetAddress("FaultDisputeGame"))); } - /// @notice Converts the new OpChainConfig struct to the legacy OpChainConfig struct format. - /// This function is used to test Upgrade 13 and 14 paths and can be safely removed - /// after those upgrades are completed. Only difference in the new struct is the added - /// disputeGameUsesSuperRoots boolean. - /// @param _opChainConfigs The new OpChainConfig structs to convert. - /// @return The legacy OpChainConfig structs. - function convertToLegacyConfigs(IOPContractsManager.OpChainConfig[] memory _opChainConfigs) - public - pure - returns (IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory) - { - IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory legacyConfigs = - new IOPContractsManagerLegacyUpgrade.OpChainConfig[](_opChainConfigs.length); - for (uint256 i = 0; i < _opChainConfigs.length; i++) { - legacyConfigs[i] = IOPContractsManagerLegacyUpgrade.OpChainConfig({ - systemConfigProxy: _opChainConfigs[i].systemConfigProxy, - proxyAdmin: _opChainConfigs[i].proxyAdmin, - absolutePrestate: _opChainConfigs[i].absolutePrestate - }); - } - return legacyConfigs; - } - function expectEmitUpgraded(address impl, address proxy) public { vm.expectEmit(proxy); emit Upgraded(impl); } - function runV200UpgradeAndChecks(address _delegateCaller) public { + function runUpgrade13UpgradeAndChecks(address _delegateCaller) public { // The address below corresponds with the address of the v2.0.0-rc.1 OPCM on mainnet. IOPContractsManager deployedOPCM = IOPContractsManager(address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76)); IOPCMImplementationsWithoutLockbox.Implementations memory impls = @@ -379,8 +353,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(_delegateCaller).dcForward( - address(deployedOPCM), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (convertToLegacyConfigs(opChainConfigs))) + address(deployedOPCM), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) ); VmSafe.Gas memory gas = vm.lastCallGas(); @@ -427,7 +400,6 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } function runUpgrade14UpgradeAndChecks(address _delegateCaller) public { - // TODO(#14665): Replace this address with once the final OPCM is deployed for Upgrade 14. IOPContractsManager deployedOPCM = IOPContractsManager(address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F)); IOPCMImplementationsWithoutLockbox.Implementations memory impls = IOPCMImplementationsWithoutLockbox(address(deployedOPCM)).implementations(); @@ -461,8 +433,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.etch(_delegateCaller, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(_delegateCaller).dcForward( - address(deployedOPCM), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (convertToLegacyConfigs(opChainConfigs))) + address(deployedOPCM), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs)) ); VmSafe.Gas memory gas = vm.lastCallGas(); @@ -590,7 +561,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { // Make sure that the OptimismPortal is upgraded to the right version. It must also have a // reference to the new AnchorStateRegistry. - assertEq(ISemver(address(optimismPortal2)).version(), "4.0.0"); + assertEq(ISemver(address(optimismPortal2)).version(), "4.1.0"); assertEq(impls.optimismPortalImpl, EIP1967Helper.getImplementation(address(optimismPortal2))); assertEq(address(optimismPortal2.anchorStateRegistry()), address(newAsrProxy)); DeployUtils.assertInitialized({ @@ -609,7 +580,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { function runUpgradeTestAndChecks(address _delegateCaller) public { // TODO(#14691): Remove this function once Upgrade 15 is deployed on Mainnet. - runV200UpgradeAndChecks(_delegateCaller); + runUpgrade13UpgradeAndChecks(_delegateCaller); // TODO(#14691): Remove this function once Upgrade 15 is deployed on Mainnet. runUpgrade14UpgradeAndChecks(_delegateCaller); runUpgrade15UpgradeAndChecks(_delegateCaller); @@ -677,13 +648,86 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { // Try to upgrade the current OPChain runUpgradeTestAndChecks(upgrader); } + + /// @notice Tests that the absolute prestate can be overridden using the upgrade config. + function test_upgrade_absolutePrestateOverride_succeeds() public { + // Run Upgrade 13 and 14 to get us to a state where we can run Upgrade 15. + // Can remove these two calls as Upgrade 13 and 14 are executed in prod. + runUpgrade13UpgradeAndChecks(upgrader); + runUpgrade14UpgradeAndChecks(upgrader); + + // Get the pdg and fdg before the upgrade + Claim pdgPrestateBefore = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateBefore = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the prestate is not zero. + assertNotEq(pdgPrestateBefore.raw(), bytes32(0)); + assertNotEq(fdgPrestateBefore.raw(), bytes32(0)); + + // Set the absolute prestate input to something non-zero. + opChainConfigs[0].absolutePrestate = Claim.wrap(bytes32(uint256(1))); + + // Now run Upgrade 15. + runUpgrade15UpgradeAndChecks(upgrader); + + // Get the absolute prestate after the upgrade + Claim pdgPrestateAfter = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateAfter = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the absolute prestate is the non-zero value we set. + assertEq(pdgPrestateAfter.raw(), bytes32(uint256(1))); + assertEq(fdgPrestateAfter.raw(), bytes32(uint256(1))); + } + + /// @notice Tests that the old absolute prestate is used if the upgrade config does not set an + /// absolute prestate. + function test_upgrade_absolutePrestateNotSet_succeeds() public { + // Run Upgrade 13 and 14 to get us to a state where we can run Upgrade 15. + // Can remove these two calls as Upgrade 13 and 14 are executed in prod. + runUpgrade13UpgradeAndChecks(upgrader); + runUpgrade14UpgradeAndChecks(upgrader); + + // Get the pdg and fdg before the upgrade + Claim pdgPrestateBefore = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateBefore = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the prestate is not zero. + assertNotEq(pdgPrestateBefore.raw(), bytes32(0)); + assertNotEq(fdgPrestateBefore.raw(), bytes32(0)); + + // Set the absolute prestate input to zero. + opChainConfigs[0].absolutePrestate = Claim.wrap(bytes32(0)); + + // Now run Upgrade 15. + runUpgrade15UpgradeAndChecks(upgrader); + + // Get the absolute prestate after the upgrade + Claim pdgPrestateAfter = IPermissionedDisputeGame( + address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON)) + ).absolutePrestate(); + Claim fdgPrestateAfter = + IFaultDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.CANNON))).absolutePrestate(); + + // Assert that the absolute prestate is the same as before the upgrade. + assertEq(pdgPrestateAfter.raw(), pdgPrestateBefore.raw()); + assertEq(fdgPrestateAfter.raw(), fdgPrestateBefore.raw()); + } } contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harness { // Upgrade to U14 first function setUp() public override { super.setUp(); - runV200UpgradeAndChecks(upgrader); + runUpgrade13UpgradeAndChecks(upgrader); } function test_upgrade_notDelegateCalled_reverts() public { @@ -705,8 +749,24 @@ contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harn ); } + /// @notice Tests that upgrade reverts when absolutePrestate is zero and the existing game also + /// has an absolute prestate of zero. function test_upgrade_absolutePrestateNotSet_reverts() public { + // Set the config to try to update the absolutePrestate to zero. opChainConfigs[0].absolutePrestate = Claim.wrap(bytes32(0)); + + // Get the address of the PermissionedDisputeGame. + IPermissionedDisputeGame pdg = + IPermissionedDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON))); + + // Mock the PDG to return a prestate of zero. + vm.mockCall( + address(pdg), + abi.encodeCall(IPermissionedDisputeGame.absolutePrestate, ()), + abi.encode(Claim.wrap(bytes32(0))) + ); + + // Expect the upgrade to revert with PrestateNotSet. vm.expectRevert(IOPContractsManager.PrestateNotSet.selector); DelegateCaller(upgrader).dcForward(address(opcm), abi.encodeCall(IOPContractsManager.upgrade, (opChainConfigs))); } @@ -929,7 +989,6 @@ contract OPContractsManager_AddGameType_Test is Test { l2ChainId: 100, saltMixer: "hello", gasLimit: 30_000_000, - disputeGameUsesSuperRoots: false, disputeGameType: GameType.wrap(1), disputeAbsolutePrestate: Claim.wrap( bytes32(hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") @@ -1286,7 +1345,6 @@ contract OPContractsManager_UpdatePrestate_Test is Test { l2ChainId: 100, saltMixer: "hello", gasLimit: 30_000_000, - disputeGameUsesSuperRoots: false, disputeGameType: GameType.wrap(1), disputeAbsolutePrestate: Claim.wrap( bytes32(hex"038512e02c4c3f7bdaec27d00edf55b7155e0905301e1a88083e4e0a6764d54c") @@ -1308,10 +1366,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { function test_updatePrestate_pdgOnlyWithValidInput_succeeds() public { IOPContractsManager.OpChainConfig[] memory inputs = new IOPContractsManager.OpChainConfig[](1); inputs[0] = IOPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, - chainDeployOutput.opChainProxyAdmin, - Claim.wrap(bytes32(hex"ABBA")), - false + chainDeployOutput.systemConfigProxy, chainDeployOutput.opChainProxyAdmin, Claim.wrap(bytes32(hex"ABBA")) ); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); @@ -1342,10 +1397,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { IOPContractsManager.OpChainConfig[] memory inputs = new IOPContractsManager.OpChainConfig[](1); inputs[0] = IOPContractsManager.OpChainConfig( - chainDeployOutput.systemConfigProxy, - chainDeployOutput.opChainProxyAdmin, - Claim.wrap(bytes32(hex"ABBA")), - false + chainDeployOutput.systemConfigProxy, chainDeployOutput.opChainProxyAdmin, Claim.wrap(bytes32(hex"ABBA")) ); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); @@ -1382,8 +1434,7 @@ contract OPContractsManager_UpdatePrestate_Test is Test { inputs[0] = IOPContractsManager.OpChainConfig({ systemConfigProxy: chainDeployOutput.systemConfigProxy, proxyAdmin: chainDeployOutput.opChainProxyAdmin, - absolutePrestate: Claim.wrap(bytes32(0)), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(0)) }); address proxyAdminOwner = chainDeployOutput.opChainProxyAdmin.owner(); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index c0c05e6f327e4..35b8fb014c69c 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -101,7 +101,7 @@ contract OptimismPortal2_Test is CommonTest { // Call the upgrade function. vm.prank(Predeploys.PROXY_ADMIN); - optimismPortal2.upgrade(IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox)); // Assert the portal is properly upgraded. assertEq(address(optimismPortal2.ethLockbox()), address(ethLockbox)); @@ -469,27 +469,52 @@ contract OptimismPortal2_Test is CommonTest { assertEq(accountAccesses[2].storageAccesses.length, 0); } - /// @dev Tests that `updateLockbox` reverts if the caller is not the proxy admin owner. - function testFuzz_updateLockbox_notProxyAdminOwner_reverts(address _caller) external { + /// @dev Tests that `migrateToSuperRoots` reverts if the caller is not the proxy admin owner. + function testFuzz_migrateToSuperRoots_notProxyAdminOwner_reverts(address _caller) external { vm.assume(_caller != optimismPortal2.proxyAdminOwner()); vm.expectRevert(IOptimismPortal.OptimismPortal_Unauthorized.selector); vm.prank(_caller); - optimismPortal2.updateLockbox(IETHLockbox(address(1))); + optimismPortal2.migrateToSuperRoots(IETHLockbox(address(1)), IAnchorStateRegistry(address(1))); } - /// @dev Tests that `updateLockbox` updates the ETHLockbox contract. - function testFuzz_updateLockbox_succeeds(address _newLockbox) external { + /// @dev Tests that `migrateToSuperRoots` reverts if the new registry is the same as the + /// current one. + /// @param _newLockbox The new ETHLockbox to migrate to. + function testFuzz_migrateToSuperRoots_usingSameRegistry_reverts(address _newLockbox) external { + vm.assume(_newLockbox != address(optimismPortal2.ethLockbox())); + + // Use the same registry as the current one. + IAnchorStateRegistry newAnchorStateRegistry = optimismPortal2.anchorStateRegistry(); + + // Trigger the call from the right address. + address caller = optimismPortal2.proxyAdminOwner(); + + // Expect the migration to revert. + vm.expectRevert(IOptimismPortal.OptimismPortal_MigratingToSameRegistry.selector); + vm.prank(caller); + optimismPortal2.migrateToSuperRoots(IETHLockbox(_newLockbox), newAnchorStateRegistry); + } + + /// @dev Tests that `migrateToSuperRoots` updates the ETHLockbox contract, updates the + /// AnchorStateRegistry, and sets the superRootsActive flag to true. + /// @param _newLockbox The new ETHLockbox to migrate to. + /// @param _newAnchorStateRegistry The new AnchorStateRegistry to migrate to. + function testFuzz_migrateToSuperRoots_succeeds(address _newLockbox, address _newAnchorStateRegistry) external { address oldLockbox = address(optimismPortal2.ethLockbox()); + address oldAnchorStateRegistry = address(optimismPortal2.anchorStateRegistry()); vm.assume(_newLockbox != oldLockbox); + vm.assume(_newAnchorStateRegistry != oldAnchorStateRegistry); vm.expectEmit(address(optimismPortal2)); - emit LockboxUpdated(oldLockbox, _newLockbox); + emit PortalMigrated(oldLockbox, _newLockbox, oldAnchorStateRegistry, _newAnchorStateRegistry); vm.prank(optimismPortal2.proxyAdminOwner()); - optimismPortal2.updateLockbox(IETHLockbox(_newLockbox)); + optimismPortal2.migrateToSuperRoots(IETHLockbox(_newLockbox), IAnchorStateRegistry(_newAnchorStateRegistry)); assertEq(address(optimismPortal2.ethLockbox()), _newLockbox); + assertEq(address(optimismPortal2.anchorStateRegistry()), _newAnchorStateRegistry); + assertTrue(optimismPortal2.superRootsActive()); } } @@ -2137,15 +2162,12 @@ contract OptimismPortal2_upgrade_Test is CommonTest { vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); // Trigger upgrade(). - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); // Verify that the initialized slot was updated. bytes32 initializedSlotAfter = vm.load(address(optimismPortal2), bytes32(slot.slot)); assertEq(initializedSlotAfter, bytes32(uint256(2))); - // Verify that superRootsActive was set to true. - assertEq(optimismPortal2.superRootsActive(), true); - // Verify that the AnchorStateRegistry was set. assertEq(address(optimismPortal2.anchorStateRegistry()), address(0xdeadbeef)); } @@ -2159,11 +2181,11 @@ contract OptimismPortal2_upgrade_Test is CommonTest { vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); // Trigger first upgrade. - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); // Try to trigger second upgrade. vm.expectRevert("Initializable: contract is already initialized"); - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } /// @notice Tests that the upgrade() function reverts if called after initialization. @@ -2180,7 +2202,7 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Try to trigger upgrade(). vm.expectRevert("Initializable: contract is already initialized"); - optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), true); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } } diff --git a/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol b/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol index 8b4944be7077a..06e20c078c122 100644 --- a/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol +++ b/packages/contracts-bedrock/test/opcm/UpgradeOPChain.t.sol @@ -55,8 +55,7 @@ contract UpgradeOPChainInput_Test is Test { configs[0] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(systemConfig1), proxyAdmin: IProxyAdmin(proxyAdmin1), - absolutePrestate: Claim.wrap(bytes32(uint256(1))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(uint256(1))) }); // Setup mock addresses and contracts for second config @@ -68,8 +67,7 @@ contract UpgradeOPChainInput_Test is Test { configs[1] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(systemConfig2), proxyAdmin: IProxyAdmin(proxyAdmin2), - absolutePrestate: Claim.wrap(bytes32(uint256(2))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(uint256(2))) }); input.set(input.opChainConfigs.selector, configs); @@ -113,8 +111,7 @@ contract UpgradeOPChainInput_Test is Test { configs[0] = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(mockSystemConfig), proxyAdmin: IProxyAdmin(mockProxyAdmin), - absolutePrestate: Claim.wrap(bytes32(uint256(1))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(uint256(1))) }); vm.expectRevert("UpgradeOPCMInput: unknown selector"); @@ -150,8 +147,7 @@ contract UpgradeOPChain_Test is Test { config = OPContractsManager.OpChainConfig({ systemConfigProxy: ISystemConfig(makeAddr("systemConfigProxy")), proxyAdmin: IProxyAdmin(makeAddr("proxyAdmin")), - absolutePrestate: Claim.wrap(keccak256("absolutePrestate")), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(keccak256("absolutePrestate")) }); OPContractsManager.OpChainConfig[] memory configs = new OPContractsManager.OpChainConfig[](1); configs[0] = config; diff --git a/packages/contracts-bedrock/test/setup/Events.sol b/packages/contracts-bedrock/test/setup/Events.sol index 022258e02baf9..64c63e65e6a1f 100644 --- a/packages/contracts-bedrock/test/setup/Events.sol +++ b/packages/contracts-bedrock/test/setup/Events.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +// Libraries +import { Types } from "src/libraries/Types.sol"; import "src/dispute/lib/Types.sol"; -import { Types } from "src/libraries/Types.sol"; +// Interfaces +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; /// @title Events /// @dev Contains various events that are tested against. This contract needs to @@ -109,5 +111,7 @@ contract Events { event ETHMigrated(address indexed lockbox, uint256 ethBalance); - event LockboxUpdated(address oldLockbox, address newLockbox); + event PortalMigrated( + address oldLockbox, address newLockbox, address oldAnchorStateRegistry, address newAnchorStateRegistry + ); } diff --git a/packages/contracts-bedrock/test/setup/ForkLive.s.sol b/packages/contracts-bedrock/test/setup/ForkLive.s.sol index 6f3d663000ee6..70839e22bbfe4 100644 --- a/packages/contracts-bedrock/test/setup/ForkLive.s.sol +++ b/packages/contracts-bedrock/test/setup/ForkLive.s.sol @@ -26,7 +26,6 @@ import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { IOPContractsManagerLegacyUpgrade } from "interfaces/L1/IOPContractsManagerLegacyUpgrade.sol"; /// @title ForkLive /// @notice This script is called by Setup.sol as a preparation step for the foundry test suite, and is run as an @@ -190,8 +189,7 @@ contract ForkLive is Deployer { opChains[0] = IOPContractsManager.OpChainConfig({ systemConfigProxy: systemConfig, proxyAdmin: proxyAdmin, - absolutePrestate: Claim.wrap(bytes32(keccak256("absolutePrestate"))), - disputeGameUsesSuperRoots: false + absolutePrestate: Claim.wrap(bytes32(keccak256("absolutePrestate"))) }); // Temporarily replace the upgrader with a DelegateCaller so we can test the upgrade, @@ -199,27 +197,14 @@ contract ForkLive is Deployer { bytes memory upgraderCode = address(upgrader).code; vm.etch(upgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); - // Some upgrades require the legacy format. - IOPContractsManagerLegacyUpgrade.OpChainConfig[] memory legacyConfigs = - new IOPContractsManagerLegacyUpgrade.OpChainConfig[](opChains.length); - for (uint256 i = 0; i < opChains.length; i++) { - legacyConfigs[i] = IOPContractsManagerLegacyUpgrade.OpChainConfig({ - systemConfigProxy: opChains[i].systemConfigProxy, - proxyAdmin: opChains[i].proxyAdmin, - absolutePrestate: opChains[i].absolutePrestate - }); - } - // Start by doing Upgrade 13. DelegateCaller(upgrader).dcForward( - address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (legacyConfigs)) + address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), abi.encodeCall(IOPContractsManager.upgrade, (opChains)) ); // Then do Upgrade 14. DelegateCaller(upgrader).dcForward( - address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F), - abi.encodeCall(IOPContractsManagerLegacyUpgrade.upgrade, (legacyConfigs)) + address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F), abi.encodeCall(IOPContractsManager.upgrade, (opChains)) ); // Then do the final upgrade. diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 236294d0b0fc9..5b157dea22246 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -216,7 +216,7 @@ contract Specification_Test is CommonTest { _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("donateETH()") }); - _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("updateLockbox(address)") }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("migrateToSuperRoots(address,address)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: IOptimismPortalInterop.finalizeWithdrawalTransaction.selector, @@ -229,7 +229,7 @@ contract Specification_Test is CommonTest { }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("finalizedWithdrawals(bytes32)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("guardian()") }); - _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("initialize(address,address,address,address,bool)") }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("initialize(address,address,address,address)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("l2Sender()") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("minimumGasLimit(uint64)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("params()") }); @@ -264,7 +264,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("ethLockbox()") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("migrateLiquidity()") }); _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("proxyAdminOwner()") }); - _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("upgrade(address,address,bool)") }); + _addSpec({ _name: "OptimismPortalInterop", _sel: _getSel("upgrade(address,address)") }); _addSpec({ _name: "OptimismPortalInterop", _sel: IOptimismPortalInterop.setConfig.selector, @@ -276,7 +276,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortal2", _sel: _getSel("checkWithdrawal(bytes32,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("depositTransaction(address,uint256,uint64,bool,bytes)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("donateETH()") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("updateLockbox(address)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("migrateToSuperRoots(address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: IOptimismPortal2.finalizeWithdrawalTransaction.selector, @@ -289,7 +289,7 @@ contract Specification_Test is CommonTest { }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("finalizedWithdrawals(bytes32)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("guardian()") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("initialize(address,address,address,address,bool)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("initialize(address,address,address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("l2Sender()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("minimumGasLimit(uint64)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("params()") }); @@ -321,7 +321,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortal2", _sel: _getSel("respectedGameTypeUpdatedAt()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("proofSubmitters(bytes32,uint256)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("numProofSubmitters(bytes32)") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(address,address,bool)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("ethLockbox()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("migrateLiquidity()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("proxyAdminOwner()") }); diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 3eb79187e65ff..fc02910cac9ee 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -124,7 +124,7 @@ contract Initializer_Test is CommonTest { name: "OptimismPortal2Impl", target: EIP1967Helper.getImplementation(address(optimismPortal2)), initCalldata: abi.encodeCall( - optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox, false) + optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox) ) }) ); @@ -134,7 +134,7 @@ contract Initializer_Test is CommonTest { name: "OptimismPortal2Proxy", target: address(optimismPortal2), initCalldata: abi.encodeCall( - optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox, false) + optimismPortal2.initialize, (systemConfig, superchainConfig, anchorStateRegistry, ethLockbox) ) }) );