diff --git a/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol b/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol new file mode 100644 index 0000000000000..9fbbb6f92541f --- /dev/null +++ b/packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title IL2ContractsManager +/// @notice Interface for the L2ContractsManager contract. +interface IL2ContractsManager { + /// @notice Executes the upgrade for all predeploys. + /// @dev This function MUST be called via DELEGATECALL from the L2ProxyAdmin. + function upgrade() external; +} diff --git a/packages/contracts-bedrock/interfaces/L2/IL2ProxyAdmin.sol b/packages/contracts-bedrock/interfaces/L2/IL2ProxyAdmin.sol new file mode 100644 index 0000000000000..8f5f3f0dd9868 --- /dev/null +++ b/packages/contracts-bedrock/interfaces/L2/IL2ProxyAdmin.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Interfaces +import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { ISemver } from "interfaces/universal/ISemver.sol"; + +/// @title IL2ProxyAdmin +interface IL2ProxyAdmin is IProxyAdmin, ISemver { + /// @notice Emitted when the predeploys are upgraded. + /// @param l2ContractsManager Address of the L2ContractsManager contract. + event PredeploysUpgraded(address indexed l2ContractsManager); + + /// @notice Thrown when the caller is not the depositor account. + error L2ProxyAdmin__Unauthorized(); + + /// @notice Thrown when the upgrade fails. + error L2ProxyAdmin__UpgradeFailed(bytes data); + + function __constructor__(address _owner) external; + /// @notice Upgrades the predeploys via delegatecall to the L2ContractsManager contract. + /// @param _l2ContractsManager Address of the L2ContractsManager contract. + function upgradePredeploys(address _l2ContractsManager) external; +} diff --git a/packages/contracts-bedrock/scripts/L2Genesis.s.sol b/packages/contracts-bedrock/scripts/L2Genesis.s.sol index c189309fddd70..c8c77182be695 100644 --- a/packages/contracts-bedrock/scripts/L2Genesis.s.sol +++ b/packages/contracts-bedrock/scripts/L2Genesis.s.sol @@ -202,7 +202,7 @@ contract L2Genesis is Script { /// @notice Set up the accounts that correspond to the predeploys. /// The Proxy bytecode should be set. All proxied predeploys should have - /// the 1967 admin slot set to the ProxyAdmin predeploy. All defined predeploys + /// the 1967 admin slot set to the L2ProxyAdmin predeploy. All defined predeploys /// should have their implementations set. /// Warning: the predeploy accounts have contract code, but 0 nonce value, contrary /// to the expected nonce of 1 per EIP-161. This is because the legacy go genesis @@ -249,7 +249,7 @@ contract L2Genesis is Script { setL1Block(_input.useCustomGasToken); // 15 setL2ToL1MessagePasser(_input.useCustomGasToken); // 16 setOptimismMintableERC721Factory(_input); // 17 - setProxyAdmin(_input); // 18 + setL2ProxyAdmin(_input); // 18 setBaseFeeVault(_input); // 19 setL1FeeVault(_input); // 1A setOperatorFeeVault(_input); // 1B @@ -272,12 +272,13 @@ contract L2Genesis is Script { function setInteropPredeployProxies() internal { } - function setProxyAdmin(Input memory _input) internal { - // Note the ProxyAdmin implementation itself is behind a proxy that owns itself. + function setL2ProxyAdmin(Input memory _input) internal { + // Note the L2ProxyAdmin implementation itself is behind a proxy that owns itself. address impl = _setImplementationCode(Predeploys.PROXY_ADMIN); bytes32 _ownerSlot = bytes32(0); + // TODO(#19182): Remove this once the L2ProxyAdmin is initializable. // there is no initialize() function, so we just set the storage manually. vm.store(Predeploys.PROXY_ADMIN, _ownerSlot, bytes32(uint256(uint160(_input.opChainProxyAdminOwner)))); // update the proxy to not be uninitialized (although not standard initialize pattern) diff --git a/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json b/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json new file mode 100644 index 0000000000000..28eecaa8bee59 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/abi/L2ProxyAdmin.json @@ -0,0 +1,361 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "_newAdmin", + "type": "address" + } + ], + "name": "changeProxyAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + } + ], + "name": "getProxyAdmin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_proxy", + "type": "address" + } + ], + "name": "getProxyImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "implementationName", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "isUpgrading", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "proxyType", + "outputs": [ + { + "internalType": "enum ProxyAdmin.ProxyType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "address", + "name": "_address", + "type": "address" + } + ], + "name": "setAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IAddressManager", + "name": "_address", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + } + ], + "name": "setImplementationName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_address", + "type": "address" + }, + { + "internalType": "enum ProxyAdmin.ProxyType", + "name": "_type", + "type": "uint8" + } + ], + "name": "setProxyType", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_upgrading", + "type": "bool" + } + ], + "name": "setUpgrading", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "upgrade", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_proxy", + "type": "address" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "upgradeAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_l2ContractsManager", + "type": "address" + } + ], + "name": "upgradePredeploys", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "l2ContractsManager", + "type": "address" + } + ], + "name": "PredeploysUpgraded", + "type": "event" + }, + { + "inputs": [], + "name": "L2ProxyAdmin__Unauthorized", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "L2ProxyAdmin__UpgradeFailed", + "type": "error" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index c1b2cd9b2f531..699396cfa41c8 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -69,7 +69,7 @@ }, "src/L2/FeeSplitter.sol:FeeSplitter": { "initCodeHash": "0xdaae3903628f760e36da47c8f8d75d20962d1811fb5129cb09eb01803e67c095", - "sourceCodeHash": "0x95dd8da08e907fa398c98710bb12fda9fb50d9688c5d2144fd9a424c99e672c5" + "sourceCodeHash": "0xdc50fe7643d26950527dac75d69ddbdebf0b5c4f061fe2938eada849e179a217" }, "src/L2/GasPriceOracle.sol:GasPriceOracle": { "initCodeHash": "0xf72c23d9c3775afd7b645fde429d09800622d329116feb5ff9829634655123ca", @@ -89,7 +89,7 @@ }, "src/L2/L1Withdrawer.sol:L1Withdrawer": { "initCodeHash": "0x6efb9055142e90b408c6312074243769df0d365f6f984e226e0320bec55a45b8", - "sourceCodeHash": "0x6a12e541b47b79f19d1061ff7b64ffdcffa1e8d06225cca6798daca53fd96890" + "sourceCodeHash": "0x7e438cbbe9a8248887b8c21f68c811f90a5cae4902cbbf7b0a1f6cd644dc42d9" }, "src/L2/L2CrossDomainMessenger.sol:L2CrossDomainMessenger": { "initCodeHash": "0xe160be403df12709c371c33195d1b9c3b5e9499e902e86bdabc8eed749c3fd61", @@ -99,6 +99,10 @@ "initCodeHash": "0x863f0f5b410983f3e51cd97c60a3a42915141b7452864d0e176571d640002b81", "sourceCodeHash": "0xc05bfcfadfd09a56cfea68e7c1853faa36d114d9a54cd307348be143e442c35a" }, + "src/L2/L2ProxyAdmin.sol:L2ProxyAdmin": { + "initCodeHash": "0x85b054c8105191d272014459858020a90fcf7db401ef0fb028999f967461d25a", + "sourceCodeHash": "0x0d402be0c35dcdd3f6642c2949932705dd09c8e2d08a06c328ccbf8ed6c65808" + }, "src/L2/L2StandardBridge.sol:L2StandardBridge": { "initCodeHash": "0xba5b288a396b34488ba7be68473305529c7da7c43e5f1cfc48d6a4aecd014103", "sourceCodeHash": "0x9dd26676cd1276c807ffd4747236783c5170d0919c70693e70b7e4c4c2675429" @@ -165,7 +169,7 @@ }, "src/L2/SuperchainRevSharesCalculator.sol:SuperchainRevSharesCalculator": { "initCodeHash": "0xdfff95660d2d470e198054bb1717a30a45a806d2eaa3720fb43acaa3356c9a3e", - "sourceCodeHash": "0x4f494790d6044882ca0150bb28bb4abbf45cd2617bbdae0ee13b0085961ca788" + "sourceCodeHash": "0x741d4fe13a88e0f2667ecb1c0c8a8550059800f8ef63241d6ffd14bf0a53ae5e" }, "src/L2/SuperchainTokenBridge.sol:SuperchainTokenBridge": { "initCodeHash": "0xb0d25dc03b9c84b07b263921c2b717e6caad3f4297fa939207e35978d7d25abe", diff --git a/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json b/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json new file mode 100644 index 0000000000000..a0b6f46bf85e6 --- /dev/null +++ b/packages/contracts-bedrock/snapshots/storageLayout/L2ProxyAdmin.json @@ -0,0 +1,37 @@ +[ + { + "bytes": "20", + "label": "_owner", + "offset": 0, + "slot": "0", + "type": "address" + }, + { + "bytes": "32", + "label": "proxyType", + "offset": 0, + "slot": "1", + "type": "mapping(address => enum ProxyAdmin.ProxyType)" + }, + { + "bytes": "32", + "label": "implementationName", + "offset": 0, + "slot": "2", + "type": "mapping(address => string)" + }, + { + "bytes": "20", + "label": "addressManager", + "offset": 0, + "slot": "3", + "type": "contract IAddressManager" + }, + { + "bytes": "1", + "label": "upgrading", + "offset": 20, + "slot": "3", + "type": "bool" + } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/src/L2/FeeSplitter.sol b/packages/contracts-bedrock/src/L2/FeeSplitter.sol index b69b8a2691c6e..58008ece962cb 100644 --- a/packages/contracts-bedrock/src/L2/FeeSplitter.sol +++ b/packages/contracts-bedrock/src/L2/FeeSplitter.sol @@ -7,7 +7,7 @@ import { Types } from "src/libraries/Types.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; // Interfaces -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { ISharesCalculator } from "interfaces/L2/ISharesCalculator.sol"; import { IFeeVault } from "interfaces/L2/IFeeVault.sol"; @@ -188,7 +188,7 @@ contract FeeSplitter is ISemver, Initializable { /// @notice Updates the fee disbursement interval. Only callable by the ProxyAdmin owner. /// @param _newFeeDisbursementInterval The new fee disbursement interval in seconds. function setFeeDisbursementInterval(uint128 _newFeeDisbursementInterval) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert FeeSplitter_OnlyProxyAdminOwner(); } if (_newFeeDisbursementInterval == 0) { @@ -205,7 +205,7 @@ contract FeeSplitter is ISemver, Initializable { /// @notice Updates the share calculator contract. Only callable by the ProxyAdmin owner. /// @param _newSharesCalculator The new share calculator contract. function setSharesCalculator(ISharesCalculator _newSharesCalculator) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert FeeSplitter_OnlyProxyAdminOwner(); } if (address(_newSharesCalculator) == address(0)) revert FeeSplitter_SharesCalculatorCannotBeZero(); diff --git a/packages/contracts-bedrock/src/L2/FeeVault.sol b/packages/contracts-bedrock/src/L2/FeeVault.sol index 7443d2b57f97f..866390b41181c 100644 --- a/packages/contracts-bedrock/src/L2/FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/FeeVault.sol @@ -8,7 +8,7 @@ import { Types } from "src/libraries/Types.sol"; // Interfaces import { IL2ToL1MessagePasser } from "interfaces/L2/IL2ToL1MessagePasser.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; // External // import from openzeppelin-contracts-v5 @@ -104,7 +104,7 @@ abstract contract FeeVault is Initializable { /// @dev If integrating the FeeSplitter contract, the minimum withdrawal amount must be set to 0 to /// avoid blocking withdrawals and disbursements for all vaults if one vault doesn't reach the threshold. function setMinWithdrawalAmount(uint256 _newMinWithdrawalAmount) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert FeeVault_OnlyProxyAdminOwner(); } @@ -117,7 +117,7 @@ abstract contract FeeVault is Initializable { /// @notice Updates the recipient of vault fees when they are withdrawn from the vault. /// @param _newRecipient The new recipient address. function setRecipient(address _newRecipient) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert FeeVault_OnlyProxyAdminOwner(); } @@ -132,7 +132,7 @@ abstract contract FeeVault is Initializable { /// withdraw them to an address on the same chain. /// @param _newWithdrawalNetwork The new withdrawal network. function setWithdrawalNetwork(Types.WithdrawalNetwork _newWithdrawalNetwork) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert FeeVault_OnlyProxyAdminOwner(); } diff --git a/packages/contracts-bedrock/src/L2/L1Withdrawer.sol b/packages/contracts-bedrock/src/L2/L1Withdrawer.sol index 8b55fdea67ed4..fbfc6fac6c01c 100644 --- a/packages/contracts-bedrock/src/L2/L1Withdrawer.sol +++ b/packages/contracts-bedrock/src/L2/L1Withdrawer.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; import { ISemver } from "interfaces/universal/ISemver.sol"; import { IL2CrossDomainMessenger } from "interfaces/L2/IL2CrossDomainMessenger.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; /// @title L1Withdrawer @@ -82,7 +82,7 @@ contract L1Withdrawer is ISemver { /// @notice Updates the minimum withdrawal amount. Only callable by the ProxyAdmin owner. /// @param _newMinWithdrawalAmount The new minimum withdrawal amount. function setMinWithdrawalAmount(uint256 _newMinWithdrawalAmount) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert L1Withdrawer_OnlyProxyAdminOwner(); } uint256 oldMinWithdrawalAmount = minWithdrawalAmount; @@ -95,7 +95,7 @@ contract L1Withdrawer is ISemver { /// when the withdrawal is finalized. /// @param _newRecipient The new recipient address. function setRecipient(address _newRecipient) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert L1Withdrawer_OnlyProxyAdminOwner(); } address oldRecipient = recipient; @@ -107,7 +107,7 @@ contract L1Withdrawer is ISemver { /// @param _newWithdrawalGasLimit The new withdrawal gas limit. /// @dev If target on L1 is `FeesDepositor`, the gas limit should be at or above 800k gas. function setWithdrawalGasLimit(uint32 _newWithdrawalGasLimit) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert L1Withdrawer_OnlyProxyAdminOwner(); } uint32 oldWithdrawalGasLimit = withdrawalGasLimit; diff --git a/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol b/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol new file mode 100644 index 0000000000000..7d7dfe2579ac4 --- /dev/null +++ b/packages/contracts-bedrock/src/L2/L2ProxyAdmin.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Interfaces +import { IL2ContractsManager } from "interfaces/L2/IL2ContractsManager.sol"; +import { ISemver } from "interfaces/universal/ISemver.sol"; + +// Libraries +import { Constants } from "src/libraries/Constants.sol"; + +// Contracts +import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; + +/// @custom:proxied true +/// @custom:predeploy 0x4200000000000000000000000000000000000018 +/// @title L2ProxyAdmin +/// @notice The L2ProxyAdmin is the administrative contract responsible for managing proxy upgrades +/// for L2 predeploy contracts. +/// @dev It extends the standard ProxyAdmin with an upgradePredeploys() function that orchestates +/// batch upgrades of multiple predeploys by delegating to an L2ContractsManager contract. +contract L2ProxyAdmin is ProxyAdmin, ISemver { + /// @notice Emitted when the predeploys are upgraded. + /// @param l2ContractsManager Address of the L2ContractsManager contract. + event PredeploysUpgraded(address indexed l2ContractsManager); + + /// @notice Thrown when the caller is not the depositor account. + error L2ProxyAdmin__Unauthorized(); + + /// @notice Thrown when the upgrade fails. + error L2ProxyAdmin__UpgradeFailed(bytes data); + + /// @notice The semantic version of the contract. + /// @custom:semver 1.0.0 + string public constant version = "1.0.0"; + + /// @notice The constructor for the L2ProxyAdmin contract. + /// @param _owner Address of the initial owner of this contract. + constructor(address _owner) ProxyAdmin(_owner) { } + + /// @notice Upgrades the predeploys via delegatecall to the l2ContractsManager contract. + /// @param _l2ContractsManager Address of the l2ContractsManager contract. + function upgradePredeploys(address _l2ContractsManager) external { + if (msg.sender != Constants.DEPOSITOR_ACCOUNT) revert L2ProxyAdmin__Unauthorized(); + + (bool success, bytes memory data) = + _l2ContractsManager.delegatecall(abi.encodeCall(IL2ContractsManager.upgrade, ())); + + if (!success) revert L2ProxyAdmin__UpgradeFailed(data); + + emit PredeploysUpgraded(_l2ContractsManager); + } +} diff --git a/packages/contracts-bedrock/src/L2/SuperchainRevSharesCalculator.sol b/packages/contracts-bedrock/src/L2/SuperchainRevSharesCalculator.sol index 7270140a34f80..a9ba651d4ddae 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainRevSharesCalculator.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainRevSharesCalculator.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.25; import { Predeploys } from "src/libraries/Predeploys.sol"; // Interfaces -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { ISharesCalculator } from "interfaces/L2/ISharesCalculator.sol"; @@ -103,7 +103,7 @@ contract SuperchainRevSharesCalculator is ISemver, ISharesCalculator { /// @notice Sets the share recipient. Only callable by the ProxyAdmin owner. /// @param _newShareRecipient The new share recipient address. function setShareRecipient(address payable _newShareRecipient) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert SharesCalculator_OnlyProxyAdminOwner(); } address oldShareRecipient = shareRecipient; @@ -114,7 +114,7 @@ contract SuperchainRevSharesCalculator is ISemver, ISharesCalculator { /// @notice Sets the remainder recipient. Only callable by the ProxyAdmin owner. /// @param _newRemainderRecipient The new remainder recipient address. function setRemainderRecipient(address payable _newRemainderRecipient) external { - if (msg.sender != IProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { + if (msg.sender != IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()) { revert SharesCalculator_OnlyProxyAdminOwner(); } address oldRemainderRecipient = remainderRecipient; diff --git a/packages/contracts-bedrock/src/libraries/Predeploys.sol b/packages/contracts-bedrock/src/libraries/Predeploys.sol index 890e46e2b71a3..0186f8fe80588 100644 --- a/packages/contracts-bedrock/src/libraries/Predeploys.sol +++ b/packages/contracts-bedrock/src/libraries/Predeploys.sol @@ -61,7 +61,7 @@ library Predeploys { /// @notice Address of the OptimismMintableERC721Factory predeploy. address internal constant OPTIMISM_MINTABLE_ERC721_FACTORY = 0x4200000000000000000000000000000000000017; - /// @notice Address of the ProxyAdmin predeploy. + /// @notice Address of the L2ProxyAdmin predeploy. address internal constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018; /// @notice Address of the BaseFeeVault predeploy. @@ -139,7 +139,7 @@ library Predeploys { if (_addr == L1_BLOCK_ATTRIBUTES) return "L1Block"; if (_addr == L2_TO_L1_MESSAGE_PASSER) return "L2ToL1MessagePasser"; if (_addr == OPTIMISM_MINTABLE_ERC721_FACTORY) return "OptimismMintableERC721Factory"; - if (_addr == PROXY_ADMIN) return "ProxyAdmin"; + if (_addr == PROXY_ADMIN) return "L2ProxyAdmin"; if (_addr == BASE_FEE_VAULT) return "BaseFeeVault"; if (_addr == L1_FEE_VAULT) return "L1FeeVault"; if (_addr == OPERATOR_FEE_VAULT) return "OperatorFeeVault"; diff --git a/packages/contracts-bedrock/test/L2/FeeSplitter.t.sol b/packages/contracts-bedrock/test/L2/FeeSplitter.t.sol index 35edca9a27852..63c51f0c11a8b 100644 --- a/packages/contracts-bedrock/test/L2/FeeSplitter.t.sol +++ b/packages/contracts-bedrock/test/L2/FeeSplitter.t.sol @@ -17,7 +17,7 @@ import { Types } from "src/libraries/Types.sol"; // Interfaces import { IFeeSplitter } from "interfaces/L2/IFeeSplitter.sol"; import { ISharesCalculator } from "interfaces/L2/ISharesCalculator.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { IFeeVault } from "interfaces/L2/IFeeVault.sol"; /// @title FeeSplitter_TestInit @@ -45,8 +45,8 @@ contract FeeSplitter_TestInit is CommonTest { super.enableRevenueShare(); super.setUp(); - // Get the owner from ProxyAdmin - _owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + // Get the owner from L2ProxyAdmin + _owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); // Initialize fee vaults array _feeVaults[0] = Predeploys.SEQUENCER_FEE_WALLET; diff --git a/packages/contracts-bedrock/test/L2/FeeVault.t.sol b/packages/contracts-bedrock/test/L2/FeeVault.t.sol index aa8a572335bb9..df774026642b5 100644 --- a/packages/contracts-bedrock/test/L2/FeeVault.t.sol +++ b/packages/contracts-bedrock/test/L2/FeeVault.t.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.15; import { CommonTest } from "test/setup/CommonTest.sol"; // Interfaces -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { IFeeVault } from "interfaces/L2/IFeeVault.sol"; import { IL2ToL1MessagePasser } from "interfaces/L2/IL2ToL1MessagePasser.sol"; import { IL2ToL1MessagePasserCGT } from "interfaces/L2/IL2ToL1MessagePasserCGT.sol"; @@ -30,7 +30,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { /// @notice Helper function to set up L2 withdrawal configuration. function _setupL2Withdrawal() internal { // Set the withdrawal network to L2 - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setWithdrawalNetwork(Types.WithdrawalNetwork.L2); } @@ -72,7 +72,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { function testFuzz_withdraw_notEnough_reverts(uint256 _minWithdrawalAmount) external { // Set the minimum withdrawal amount _minWithdrawalAmount = bound(_minWithdrawalAmount, 1, type(uint256).max); - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setMinWithdrawalAmount(_minWithdrawalAmount); // Set the balance to be less than the minimum withdrawal amount @@ -87,15 +87,15 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); // Setup L1 withdrawal - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setWithdrawalNetwork(Types.WithdrawalNetwork.L1); // Set recipient - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setRecipient(recipient); // Set minimum withdrawal amount - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setMinWithdrawalAmount(minWithdrawalAmount); // Set the balance to be greater than the minimum withdrawal amount @@ -151,15 +151,15 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { skipIfSysFeatureDisabled(Features.CUSTOM_GAS_TOKEN); // Setup L1 withdrawal - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setWithdrawalNetwork(Types.WithdrawalNetwork.L1); // Set recipient - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setRecipient(recipient); // Set minimum withdrawal amount - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); feeVault.setMinWithdrawalAmount(minWithdrawalAmount); // Set the balance to be greater than the minimum withdrawal amount @@ -221,7 +221,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { /// @notice Tests that the owner can successfully set minimum withdrawal amount with fuzz testing. function testFuzz_setMinWithdrawalAmount_succeeds(uint256 _newMinWithdrawalAmount) external { - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.prank(owner); IFeeVault(payable(address(feeVault))).setMinWithdrawalAmount(_newMinWithdrawalAmount); @@ -232,7 +232,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { /// @notice Tests that non-owner cannot set minimum withdrawal amount with fuzz testing. function testFuzz_setMinWithdrawalAmount_onlyOwner_reverts(address _caller, uint256 _newAmount) external { - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.assume(_caller != owner); uint256 initialAmount = feeVault.minWithdrawalAmount(); @@ -247,7 +247,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { /// @notice Tests that the owner can successfully set recipient with fuzz testing. function testFuzz_setRecipient_succeeds(address _newRecipient) external { - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.prank(owner); IFeeVault(payable(address(feeVault))).setRecipient(_newRecipient); @@ -258,7 +258,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { /// @notice Tests that non-owner cannot set recipient with fuzz testing. function testFuzz_setRecipient_onlyOwner_reverts(address _caller, address _newRecipient) external { - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.assume(_caller != owner); address initialRecipient = feeVault.recipient(); @@ -277,7 +277,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { _networkValue = uint8(bound(_networkValue, 0, 1)); Types.WithdrawalNetwork newNetwork = Types.WithdrawalNetwork(_networkValue); - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.prank(owner); IFeeVault(payable(address(feeVault))).setWithdrawalNetwork(newNetwork); @@ -288,7 +288,7 @@ abstract contract FeeVault_Uncategorized_Test is CommonTest { /// @notice Tests that non-owner cannot set withdrawal network with fuzz testing. function testFuzz_setWithdrawalNetwork_onlyOwner_reverts(address _caller, uint8 _networkValue) external { - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.assume(_caller != owner); // Bound to valid enum values diff --git a/packages/contracts-bedrock/test/L2/L2ProxyAdmin.t.sol b/packages/contracts-bedrock/test/L2/L2ProxyAdmin.t.sol new file mode 100644 index 0000000000000..8a792387725d1 --- /dev/null +++ b/packages/contracts-bedrock/test/L2/L2ProxyAdmin.t.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Testing +import { CommonTest } from "test/setup/CommonTest.sol"; + +// Libraries +import { Constants } from "src/libraries/Constants.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; + +// Interfaces +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; + +// Contracts +import { L2ProxyAdmin } from "src/L2/L2ProxyAdmin.sol"; +import { IL2ContractsManager } from "interfaces/L2/IL2ContractsManager.sol"; + +/// @title L2ProxyAdmin_TestInit +/// @notice Reusable test initialization for `L2ProxyAdmin` tests. +abstract contract L2ProxyAdmin_TestInit is CommonTest { + IL2ProxyAdmin public l2ProxyAdmin; + address public owner; + + // Events + event PredeploysUpgraded(address indexed l2ContractsManager); + + /// @notice Test setup. + function setUp() public virtual override { + super.setUp(); + l2ProxyAdmin = IL2ProxyAdmin(Predeploys.PROXY_ADMIN); + owner = l2ProxyAdmin.owner(); + } + + /// @notice Helper function to setup a mock and expect a call to it. + function _mockAndExpect(address _receiver, bytes memory _calldata, bytes memory _returned) internal { + vm.mockCall(_receiver, _calldata, _returned); + vm.expectCall(_receiver, _calldata); + } +} + +/// @title L2ProxyAdmin_Constructor_Test +/// @notice Tests the `constructor` function of the `L2ProxyAdmin` contract. +contract L2ProxyAdmin_Constructor_Test is L2ProxyAdmin_TestInit { + /// @notice Tests that the `constructor` function succeeds. + function test_constructor_succeeds(address _owner) public { + // Deploy the L2ProxyAdmin contract + l2ProxyAdmin = IL2ProxyAdmin(address(new L2ProxyAdmin(_owner))); + // It sets the owner to the correct address + assertEq(l2ProxyAdmin.owner(), _owner); + } +} + +/// @title L2ProxyAdmin_Version_Test +/// @notice Tests the `version` function of the `L2ProxyAdmin` contract. +contract L2ProxyAdmin_Version_Test is L2ProxyAdmin_TestInit { + /// @notice Tests that the `version` function returns a non-empty string. + function test_version_succeeds() public view { + assertGt(bytes(l2ProxyAdmin.version()).length, 0, "Version should be non-empty"); + } +} + +/// @title L2ProxyAdmin_UpgradePredeploys_Test +/// @notice Tests the `upgradePredeploys` function of the `L2ProxyAdmin` contract. +contract L2ProxyAdmin_UpgradePredeploys_Test is L2ProxyAdmin_TestInit { + /// @notice Tests that upgradePredeploys reverts when called by unauthorized caller. + function testFuzz_upgradePredeploys_unauthorizedCaller_reverts( + address _caller, + address _l2ContractsManager + ) + public + { + vm.assume(_caller != Constants.DEPOSITOR_ACCOUNT); + + // Expect the revert with L2ProxyAdmin__Unauthorized + vm.expectRevert(L2ProxyAdmin.L2ProxyAdmin__Unauthorized.selector); + + // Call upgradePredeploys with unauthorized caller + vm.prank(_caller); + l2ProxyAdmin.upgradePredeploys(_l2ContractsManager); + } + + /// @notice Tests that upgradePredeploys succeeds when called by DEPOSITOR_ACCOUNT. + function testFuzz_upgradePredeploys_succeeds(address _l2ContractsManager) public { + assumeAddressIsNot(_l2ContractsManager, AddressType.Precompile, AddressType.ForgeAddress); + + // Mock the delegatecall to return success + _mockAndExpect(_l2ContractsManager, abi.encodeCall(IL2ContractsManager.upgrade, ()), abi.encode()); + + // Expect the PredeploysUpgraded event + vm.expectEmit(address(l2ProxyAdmin)); + emit PredeploysUpgraded(_l2ContractsManager); + + // Call upgradePredeploys with authorized caller + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l2ProxyAdmin.upgradePredeploys(_l2ContractsManager); + } + + /// @notice Tests that upgradePredeploys reverts when delegatecall fails. + function testFuzz_upgradePredeploys_delegatecallFails_reverts(address _l2ContractsManager) public { + assumeAddressIsNot(_l2ContractsManager, AddressType.Precompile, AddressType.ForgeAddress); + + // Mock the delegatecall to return failure + vm.mockCallRevert(_l2ContractsManager, abi.encodeCall(IL2ContractsManager.upgrade, ()), bytes("error")); + + // Expect the revert with L2ProxyAdmin__UpgradeFailed + vm.expectRevert(abi.encodeWithSelector(L2ProxyAdmin.L2ProxyAdmin__UpgradeFailed.selector, bytes("error"))); + + // Call upgradePredeploys with authorized caller + vm.prank(Constants.DEPOSITOR_ACCOUNT); + l2ProxyAdmin.upgradePredeploys(_l2ContractsManager); + } +} diff --git a/packages/contracts-bedrock/test/L2/LegacyFeeSplitter.t.sol b/packages/contracts-bedrock/test/L2/LegacyFeeSplitter.t.sol index a876aa4c2d754..e2cc3fb914434 100644 --- a/packages/contracts-bedrock/test/L2/LegacyFeeSplitter.t.sol +++ b/packages/contracts-bedrock/test/L2/LegacyFeeSplitter.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; import { FeeSplitter_TestInit } from "test/L2/FeeSplitter.t.sol"; import { LegacyFeeSplitter } from "test/mocks/LegacyFeeSplitter.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { IFeeVault } from "interfaces/L2/IFeeVault.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -18,7 +18,7 @@ contract LegacyFeeSplitter_DisburseFees_Test is FeeSplitter_TestInit { legacyFeeSplitter = new LegacyFeeSplitter(); // Setup the legacy splitter as the recipient in the vaults - address owner = IProxyAdmin(Predeploys.PROXY_ADMIN).owner(); + address owner = IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner(); vm.startPrank(owner); IFeeVault(payable(Predeploys.SEQUENCER_FEE_WALLET)).setRecipient(address(legacyFeeSplitter)); diff --git a/packages/contracts-bedrock/test/invariants/CustomGasToken.t.sol b/packages/contracts-bedrock/test/invariants/CustomGasToken.t.sol index 334d8d44a5672..13a9897f76d53 100644 --- a/packages/contracts-bedrock/test/invariants/CustomGasToken.t.sol +++ b/packages/contracts-bedrock/test/invariants/CustomGasToken.t.sol @@ -14,7 +14,7 @@ import { SafeSend } from "src/universal/SafeSend.sol"; // Contracts import { ILiquidityController } from "interfaces/L2/ILiquidityController.sol"; import { INativeAssetLiquidity } from "interfaces/L2/INativeAssetLiquidity.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; /// @title CGT_Minter /// @notice An actor with the minter role (can mint and burn) @@ -202,7 +202,7 @@ contract CustomGasToken_Invariants_Test is CommonTest { randomActor.initAddresses(address(actor_minter), address(actor_funder)); // Authorize the minter actor (simple access control in unit tests) - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); liquidityController.authorizeMinter(address(actor_minter)); // Create the initial supply diff --git a/packages/contracts-bedrock/test/invariants/FeeSplit.t.sol b/packages/contracts-bedrock/test/invariants/FeeSplit.t.sol index f8d36eb6b1973..935bb4ad401c0 100644 --- a/packages/contracts-bedrock/test/invariants/FeeSplit.t.sol +++ b/packages/contracts-bedrock/test/invariants/FeeSplit.t.sol @@ -5,7 +5,7 @@ import { StdUtils } from "forge-std/StdUtils.sol"; import { Vm } from "forge-std/Vm.sol"; import { CommonTest } from "test/setup/CommonTest.sol"; import { IFeeVault } from "interfaces/L2/IFeeVault.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { IFeeSplitter } from "interfaces/L2/IFeeSplitter.sol"; import { IL1Withdrawer } from "interfaces/L2/IL1Withdrawer.sol"; @@ -124,7 +124,7 @@ contract FeeSplitter_Preconditions is CommonTest { function setMinAmount(uint256 _minAmount, uint256 _vaultIndex) public { _vaultIndex = bound(_vaultIndex, 0, 3); - vm.prank(IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + vm.prank(IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); if (_vaultIndex == 0) { IFeeVault(payable(Predeploys.SEQUENCER_FEE_WALLET)).setMinWithdrawalAmount(_minAmount); diff --git a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol index e157c1cff9970..11b940ad25fb0 100644 --- a/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol +++ b/packages/contracts-bedrock/test/scripts/L2Genesis.t.sol @@ -20,7 +20,7 @@ import { IL1FeeVault } from "interfaces/L2/IL1FeeVault.sol"; import { IOperatorFeeVault } from "interfaces/L2/IOperatorFeeVault.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; import { IOptimismMintableERC721Factory } from "interfaces/L2/IOptimismMintableERC721Factory.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; +import { IL2ProxyAdmin } from "interfaces/L2/IL2ProxyAdmin.sol"; import { IGovernanceToken } from "interfaces/governance/IGovernanceToken.sol"; import { IGasPriceOracle } from "interfaces/L2/IGasPriceOracle.sol"; import { IFeeSplitter } from "interfaces/L2/IFeeSplitter.sol"; @@ -43,14 +43,14 @@ abstract contract L2Genesis_TestInit is Test { function testProxyAdmin() internal view { // Verify owner in the proxy - assertEq(input.opChainProxyAdminOwner, IProxyAdmin(Predeploys.PROXY_ADMIN).owner()); + assertEq(input.opChainProxyAdminOwner, IL2ProxyAdmin(Predeploys.PROXY_ADMIN).owner()); // Verify owner in the implementation to catch storage shifting issues // The implementation is stored in the code namespace address proxyAdminImpl = Predeploys.predeployToCodeNamespace(Predeploys.PROXY_ADMIN); assertEq( input.opChainProxyAdminOwner, - IProxyAdmin(proxyAdminImpl).owner(), + IL2ProxyAdmin(proxyAdminImpl).owner(), "ProxyAdmin implementation owner should match expected" ); } diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index b83c68cf5ff60..4df515570d072 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -387,6 +387,7 @@ abstract contract Setup is FeatureFlags { labelPredeploy(Predeploys.SEQUENCER_FEE_WALLET); labelPredeploy(Predeploys.L2_ERC721_BRIDGE); labelPredeploy(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); + labelPredeploy(Predeploys.PROXY_ADMIN); labelPredeploy(Predeploys.BASE_FEE_VAULT); labelPredeploy(Predeploys.L1_FEE_VAULT); labelPredeploy(Predeploys.OPERATOR_FEE_VAULT);