diff --git a/script/releases/Env.sol b/script/releases/Env.sol index 98c805dfbf..bdf186ec80 100644 --- a/script/releases/Env.sol +++ b/script/releases/Env.sol @@ -15,6 +15,10 @@ import "src/contracts/core/RewardsCoordinator.sol"; import "src/contracts/interfaces/IRewardsCoordinator.sol"; import "src/contracts/core/StrategyManager.sol"; +/// slashEscrow/ +import "src/contracts/core/SlashEscrow.sol"; +import "src/contracts/core/SlashEscrowFactory.sol"; + /// permissions/ import "src/contracts/permissions/PauserRegistry.sol"; import "src/contracts/permissions/PermissionController.sol"; @@ -76,6 +80,10 @@ library Env { return _envAddress("operationsMultisig"); } + function communityMultisig() internal view returns (address) { + return _envAddress("communityMultisig"); + } + function protocolCouncilMultisig() internal view returns (address) { return _envAddress("protocolCouncilMultisig"); } @@ -88,6 +96,10 @@ library Env { return _envAddress("proxyAdmin"); } + function slashEscrowProxyAdmin() internal view returns (address) { + return _envAddress("slashEscrowProxyAdmin"); + } + function ethPOS() internal view returns (IETHPOSDeposit) { return IETHPOSDeposit(_envAddress("ethPOS")); } @@ -148,6 +160,10 @@ library Env { return _envU256("REWARDS_COORDINATOR_PAUSE_STATUS"); } + function SLASH_ESCROW_DELAY() internal view returns (uint32) { + return _envU32("SLASH_ESCROW_DELAY"); + } + /** * core/ */ @@ -318,6 +334,27 @@ library Env { return StrategyFactory(_deployedImpl(type(StrategyFactory).name)); } + /** + * slashEscrow/ + */ + function slashEscrow( + DeployedImpl + ) internal view returns (SlashEscrow) { + return SlashEscrow(_deployedImpl(type(SlashEscrow).name)); + } + + function slashEscrowFactory( + DeployedProxy + ) internal view returns (SlashEscrowFactory) { + return SlashEscrowFactory(_deployedProxy(type(SlashEscrowFactory).name)); + } + + function slashEscrowFactory( + DeployedImpl + ) internal view returns (SlashEscrowFactory) { + return SlashEscrowFactory(_deployedImpl(type(SlashEscrowFactory).name)); + } + /** * token/ */ diff --git a/script/releases/v.1.5.0-redistribution/1-deployContracts.s.sol b/script/releases/v.1.5.0-redistribution/1-deployContracts.s.sol new file mode 100644 index 0000000000..1e97b88350 --- /dev/null +++ b/script/releases/v.1.5.0-redistribution/1-deployContracts.s.sol @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {EOADeployer} from "zeus-templates/templates/EOADeployer.sol"; +import "../Env.sol"; + +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/** + * Purpose: use an EOA to deploy all of the new contracts for this upgrade. + * This upgrade deploys a `SlashEscrowFactory` and a `SlashEscrow`. In addition it also upgrades: + * - `AllocationManager` + * - `DelegationManager` + * - `StrategyManager` + * - `EigenPodManager` + * - Strategies (`EigenStrategy`, `StrategyBase`, `StrategyBaseTVLLimits`) + */ +contract Deploy is EOADeployer { + using Env for *; + + function _runAsEOA() internal override { + vm.startBroadcast(); + + /// SlashEscrow + deployImpl({name: type(SlashEscrow).name, deployedTo: address(new SlashEscrow())}); + + /// SlashEscrowFactory + + // For the `SlashEscrowFactory`, we use a different proxy admin where the community multisig is the admin + address communityMultisig = Env.communityMultisig(); + ProxyAdmin proxyAdmin = new ProxyAdmin(); + proxyAdmin.transferOwnership(communityMultisig); + + deployImpl({ + name: type(SlashEscrowFactory).name, + deployedTo: address( + new SlashEscrowFactory({ + _allocationManager: Env.proxy.allocationManager(), + _strategyManager: Env.proxy.strategyManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _slashEscrowImplementation: Env.impl.slashEscrow(), + _version: Env.deployVersion() + }) + ) + }); + + deployProxy({ + name: type(SlashEscrowFactory).name, + deployedTo: address( + new TransparentUpgradeableProxy({ + _logic: address(Env.impl.slashEscrowFactory()), + admin_: address(proxyAdmin), + _data: abi.encodeCall( + SlashEscrowFactory.initialize, + ( + Env.executorMultisig(), // initialOwner + 0, // initialPausedStatus + Env.SLASH_ESCROW_DELAY() // initialGlobalDelayBlocks + ) + ) + }) + ) + }); + + // Core contracts: AllocationManager, DelegationManager, StrategyManager, EigenPodManager, Strategies + + // AllocationManager + deployImpl({ + name: type(AllocationManager).name, + deployedTo: address( + new AllocationManager({ + _delegation: Env.proxy.delegationManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _permissionController: Env.proxy.permissionController(), + _DEALLOCATION_DELAY: Env.MIN_WITHDRAWAL_DELAY(), + _ALLOCATION_CONFIGURATION_DELAY: Env.ALLOCATION_CONFIGURATION_DELAY(), + _version: Env.deployVersion() + }) + ) + }); + + // DelegationManager + deployImpl({ + name: type(DelegationManager).name, + deployedTo: address( + new DelegationManager({ + _strategyManager: Env.proxy.strategyManager(), + _eigenPodManager: Env.proxy.eigenPodManager(), + _allocationManager: Env.proxy.allocationManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _permissionController: Env.proxy.permissionController(), + _MIN_WITHDRAWAL_DELAY: Env.MIN_WITHDRAWAL_DELAY(), + _version: Env.deployVersion() + }) + ) + }); + + // StrategyManager + deployImpl({ + name: type(StrategyManager).name, + deployedTo: address( + new StrategyManager({ + _delegation: Env.proxy.delegationManager(), + _slashEscrowFactory: Env.proxy.slashEscrowFactory(), + _pauserRegistry: Env.impl.pauserRegistry(), + _version: Env.deployVersion() + }) + ) + }); + + // EigenPodManager + deployImpl({ + name: type(EigenPodManager).name, + deployedTo: address( + new EigenPodManager({ + _ethPOS: Env.ethPOS(), + _eigenPodBeacon: Env.beacon.eigenPod(), + _delegationManager: Env.proxy.delegationManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _version: Env.deployVersion() + }) + ) + }); + + // Strategies: EigenStrategy, StrategyBase (for strategies deployed via factory), StrategyBaseTVLLimits + deployImpl({ + name: type(EigenStrategy).name, + deployedTo: address( + new EigenStrategy({ + _strategyManager: Env.proxy.strategyManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _version: Env.deployVersion() + }) + ) + }); + + // for strategies deployed via factory + deployImpl({ + name: type(StrategyBase).name, + deployedTo: address( + new StrategyBase({ + _strategyManager: Env.proxy.strategyManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _version: Env.deployVersion() + }) + ) + }); + + deployImpl({ + name: type(StrategyBaseTVLLimits).name, + deployedTo: address( + new StrategyBaseTVLLimits({ + _strategyManager: Env.proxy.strategyManager(), + _pauserRegistry: Env.impl.pauserRegistry(), + _version: Env.deployVersion() + }) + ) + }); + + vm.stopBroadcast(); + + // Update environment variables + zUpdate("slashEscrowProxyAdmin", address(proxyAdmin)); + } + + function testScript() public virtual { + _runAsEOA(); + + // Validate slashEscrowFactory-specific variable + SlashEscrowFactory slashEscrowFactory = Env.proxy.slashEscrowFactory(); + assertTrue(slashEscrowFactory.owner() == Env.executorMultisig(), "sef.owner invalid"); + assertTrue(slashEscrowFactory.paused() == 0, "sef.paused invalid"); + assertTrue(slashEscrowFactory.getGlobalEscrowDelay() == Env.SLASH_ESCROW_DELAY(), "sef.globalDelay invalid"); + + _validateNewImplAddresses({areMatching: false}); + _validateProxyAdmins(); + _validateImplConstructors(); + _validateImplsInitialized(); + _validateVersion(); + } + + /// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy + /// admin reports as the current implementation address. + /// + /// Note: The upgrade script can call this with `areMatching == true` to check that these impl + /// addresses _are_ matches. + function _validateNewImplAddresses( + bool areMatching + ) internal view { + /// can't check `SlashEscrowFactory` because it wasn't previously deployed + + function (bool, string memory) internal pure assertion = areMatching ? _assertTrue : _assertFalse; + + assertion( + _getProxyImpl(address(Env.proxy.delegationManager())) == address(Env.impl.delegationManager()), + "delegationManager impl failed" + ); + + assertion( + _getProxyImpl(address(Env.proxy.allocationManager())) == address(Env.impl.allocationManager()), + "allocationManager impl failed" + ); + + /// strategies/ + + assertion( + _getProxyImpl(address(Env.proxy.eigenStrategy())) == address(Env.impl.eigenStrategy()), + "eigenStrategy impl failed" + ); + + assertion( + Env.beacon.strategyBase().implementation() == address(Env.impl.strategyBase()), "strategyBase impl failed" + ); + + uint256 count = Env.instance.strategyBaseTVLLimits_Count(); + for (uint256 i = 0; i < count; i++) { + assertion( + _getProxyImpl(address(Env.instance.strategyBaseTVLLimits(i))) + == address(Env.impl.strategyBaseTVLLimits()), + "strategyBaseTVLLimits impl failed" + ); + } + } + + /// @dev Ensure each deployed TUP/beacon is owned by the proxyAdmin/executorMultisig + /// @dev Ensure the `SlashEscrowProxyAdmin` is owned by the slashEscrowProxyAdmin/communityMultisig + function _validateProxyAdmins() internal view { + address pa = Env.proxyAdmin(); + address slashEscrowProxyAdmin = Env.slashEscrowProxyAdmin(); + assertFalse(slashEscrowProxyAdmin == pa, "slashEscrowProxyAdmin invalid"); + + assertTrue( + _getProxyAdmin(address(Env.proxy.delegationManager())) == pa, "delegationManager proxyAdmin incorrect" + ); + + assertTrue( + _getProxyAdmin(address(Env.proxy.allocationManager())) == pa, "allocationManager proxyAdmin incorrect" + ); + + assertTrue(_getProxyAdmin(address(Env.proxy.strategyManager())) == pa, "strategyManager proxyAdmin incorrect"); + + assertTrue(_getProxyAdmin(address(Env.proxy.eigenPodManager())) == pa, "eigenPodManager proxyAdmin incorrect"); + + /// strategies/ + + assertTrue(_getProxyAdmin(address(Env.proxy.eigenStrategy())) == pa, "eigenStrategy proxyAdmin incorrect"); + + assertTrue(Env.beacon.strategyBase().owner() == Env.executorMultisig(), "strategyBase beacon owner incorrect"); + + uint256 count = Env.instance.strategyBaseTVLLimits_Count(); + for (uint256 i = 0; i < count; i++) { + assertTrue( + _getProxyAdmin(address(Env.instance.strategyBaseTVLLimits(i))) == pa, + "strategyBaseTVLLimits proxyAdmin incorrect" + ); + } + + // SlashEscrowProxyAdmin - proxyAdmin is unique and its admin is the community multisig + assertTrue( + _getSlashEscrowProxyAdmin(address(Env.proxy.slashEscrowFactory())) == slashEscrowProxyAdmin, + "slashEscrowFactory proxyAdmin incorrect" + ); + assertTrue( + Ownable(address(slashEscrowProxyAdmin)).owner() == Env.communityMultisig(), + "slashEscrowProxyAdmin owner incorrect" + ); + } + + /// @dev Validate the immutables set in the new implementation constructors + function _validateImplConstructors() internal view { + // SlashEscrow has no constructor + { + SlashEscrowFactory slashEscrowFactory = Env.impl.slashEscrowFactory(); + assertTrue(slashEscrowFactory.allocationManager() == Env.proxy.allocationManager(), "sef.alm invalid"); + assertTrue(slashEscrowFactory.strategyManager() == Env.proxy.strategyManager(), "sef.sm invalid"); + assertTrue(slashEscrowFactory.pauserRegistry() == Env.impl.pauserRegistry(), "sef.pR invalid"); + assertTrue(slashEscrowFactory.slashEscrowImplementation() == Env.impl.slashEscrow(), "sef.se invalid"); + } + + { + DelegationManager delegation = Env.impl.delegationManager(); + assertTrue(delegation.strategyManager() == Env.proxy.strategyManager(), "dm.sm invalid"); + assertTrue(delegation.eigenPodManager() == Env.proxy.eigenPodManager(), "dm.epm invalid"); + assertTrue(delegation.allocationManager() == Env.proxy.allocationManager(), "dm.alm invalid"); + assertTrue(delegation.pauserRegistry() == Env.impl.pauserRegistry(), "dm.pR invalid"); + assertTrue(delegation.permissionController() == Env.proxy.permissionController(), "dm.pc invalid"); + assertTrue( + delegation.minWithdrawalDelayBlocks() == Env.MIN_WITHDRAWAL_DELAY(), "dm.withdrawalDelay invalid" + ); + } + + { + AllocationManager allocationManager = Env.impl.allocationManager(); + assertTrue(allocationManager.delegation() == Env.proxy.delegationManager(), "alm.dm invalid"); + assertTrue(allocationManager.pauserRegistry() == Env.impl.pauserRegistry(), "alm.pR invalid"); + assertTrue(allocationManager.permissionController() == Env.proxy.permissionController(), "alm.pc invalid"); + assertTrue(allocationManager.DEALLOCATION_DELAY() == Env.MIN_WITHDRAWAL_DELAY(), "alm.deallocDelay invalid"); + assertTrue( + allocationManager.ALLOCATION_CONFIGURATION_DELAY() == Env.ALLOCATION_CONFIGURATION_DELAY(), + "alm.configDelay invalid" + ); + } + + { + StrategyManager strategyManager = Env.impl.strategyManager(); + assertTrue(strategyManager.delegation() == Env.proxy.delegationManager(), "sm.dm invalid"); + assertTrue(strategyManager.slashEscrowFactory() == Env.proxy.slashEscrowFactory(), "sm.sef invalid"); + assertTrue(strategyManager.pauserRegistry() == Env.impl.pauserRegistry(), "sm.pR invalid"); + } + + { + EigenPodManager eigenPodManager = Env.impl.eigenPodManager(); + assertTrue(eigenPodManager.ethPOS() == Env.ethPOS(), "epm.ethPOS invalid"); + assertTrue(eigenPodManager.eigenPodBeacon() == Env.beacon.eigenPod(), "epm.epBeacon invalid"); + assertTrue(eigenPodManager.delegationManager() == Env.proxy.delegationManager(), "epm.dm invalid"); + assertTrue(eigenPodManager.pauserRegistry() == Env.impl.pauserRegistry(), "epm.pR invalid"); + } + + { + /// strategies/ + EigenStrategy eigenStrategy = Env.impl.eigenStrategy(); + assertTrue(eigenStrategy.strategyManager() == Env.proxy.strategyManager(), "eigStrat.sm invalid"); + assertTrue(eigenStrategy.pauserRegistry() == Env.impl.pauserRegistry(), "eigStrat.pR invalid"); + + StrategyBase strategyBase = Env.impl.strategyBase(); + assertTrue(strategyBase.strategyManager() == Env.proxy.strategyManager(), "stratBase.sm invalid"); + assertTrue(strategyBase.pauserRegistry() == Env.impl.pauserRegistry(), "stratBase.pR invalid"); + + StrategyBaseTVLLimits strategyBaseTVLLimits = Env.impl.strategyBaseTVLLimits(); + assertTrue( + strategyBaseTVLLimits.strategyManager() == Env.proxy.strategyManager(), "stratBaseTVL.sm invalid" + ); + assertTrue(strategyBaseTVLLimits.pauserRegistry() == Env.impl.pauserRegistry(), "stratBaseTVL.pR invalid"); + } + } + + /// @dev Call initialize on all deployed implementations to ensure initializers are disabled + function _validateImplsInitialized() internal { + bytes memory errInit = "Initializable: contract is already initialized"; + + SlashEscrowFactory slashEscrowFactory = Env.impl.slashEscrowFactory(); + vm.expectRevert(errInit); + slashEscrowFactory.initialize(address(0), 0, 0); + + AllocationManager allocationManager = Env.impl.allocationManager(); + vm.expectRevert(errInit); + allocationManager.initialize(0); + + DelegationManager delegation = Env.impl.delegationManager(); + vm.expectRevert(errInit); + delegation.initialize(0); + + StrategyManager strategyManager = Env.impl.strategyManager(); + vm.expectRevert(errInit); + strategyManager.initialize(address(0), address(0), 0); + + EigenPodManager eigenPodManager = Env.impl.eigenPodManager(); + vm.expectRevert(errInit); + eigenPodManager.initialize(address(0), 0); + + /// strategies/ + EigenStrategy eigenStrategy = Env.impl.eigenStrategy(); + vm.expectRevert(errInit); + eigenStrategy.initialize(IEigen(address(0)), IBackingEigen(address(0))); + + StrategyBase strategyBase = Env.impl.strategyBase(); + vm.expectRevert(errInit); + strategyBase.initialize(IERC20(address(0))); + + StrategyBaseTVLLimits strategyBaseTVLLimits = Env.impl.strategyBaseTVLLimits(); + vm.expectRevert(errInit); + strategyBaseTVLLimits.initialize(0, 0, IERC20(address(0))); + } + + function _validateVersion() internal view { + // On future upgrades, just tick the major/minor/patch to validate + string memory expected = Env.deployVersion(); + + assertEq(Env.impl.slashEscrowFactory().version(), expected, "slashEscrowFactory version mismatch"); + assertEq(Env.impl.delegationManager().version(), expected, "delegationManager version mismatch"); + assertEq(Env.impl.allocationManager().version(), expected, "allocationManager version mismatch"); + assertEq(Env.impl.strategyManager().version(), expected, "strategyManager version mismatch"); + assertEq(Env.impl.eigenPodManager().version(), expected, "eigenPodManager version mismatch"); + assertEq(Env.impl.eigenStrategy().version(), expected, "eigenStrategy version mismatch"); + assertEq(Env.impl.strategyBase().version(), expected, "strategyBase version mismatch"); + assertEq(Env.impl.strategyBaseTVLLimits().version(), expected, "strategyBaseTVLLimits version mismatch"); + } + + /// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)` + function _getProxyImpl( + address proxy + ) internal view returns (address) { + return ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(ITransparentUpgradeableProxy(proxy)); + } + + /// @dev Query and return `proxyAdmin.getProxyAdmin(proxy)` + function _getProxyAdmin( + address proxy + ) internal view returns (address) { + return ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(ITransparentUpgradeableProxy(proxy)); + } + + function _getSlashEscrowProxyAdmin( + address proxy + ) internal view returns (address) { + return ProxyAdmin(Env.slashEscrowProxyAdmin()).getProxyAdmin(ITransparentUpgradeableProxy(proxy)); + } + + function _assertTrue(bool b, string memory err) private pure { + assertTrue(b, err); + } + + function _assertFalse(bool b, string memory err) private pure { + assertFalse(b, err); + } +} diff --git a/script/releases/v1.4.1-slashing/2-queueUpgrade.s.sol b/script/releases/v.1.5.0-redistribution/2-queueUpgrade.s.sol similarity index 66% rename from script/releases/v1.4.1-slashing/2-queueUpgrade.s.sol rename to script/releases/v.1.5.0-redistribution/2-queueUpgrade.s.sol index 0c9082392e..c42f8d51b1 100644 --- a/script/releases/v1.4.1-slashing/2-queueUpgrade.s.sol +++ b/script/releases/v.1.5.0-redistribution/2-queueUpgrade.s.sol @@ -12,7 +12,8 @@ import {TimelockController} from "@openzeppelin/contracts/governance/TimelockCon /** * Purpose: * * enqueue a multisig transaction which; - * - upgrades DM, EPM, EP + * - upgrades DM, ALM, SM, EPM + * - upgrades strategies (EigenStrategy, StrategyBase, StrategyBaseTVLLimits) * This should be run via the protocol council multisig. */ contract QueueUpgrade is MultisigBuilder, Deploy { @@ -35,7 +36,6 @@ contract QueueUpgrade is MultisigBuilder, Deploy { /// @dev Get the calldata to be sent from the timelock to the executor function _getCalldataToExecutor() internal returns (bytes memory) { - // Core - Pods - Strategies - Permissions MultisigCall[] storage executorCalls = Encode.newMultisigCalls().append({ to: Env.proxyAdmin(), data: Encode.proxyAdmin.upgrade({ @@ -43,16 +43,45 @@ contract QueueUpgrade is MultisigBuilder, Deploy { impl: address(Env.impl.delegationManager()) }) }).append({ - to: address(Env.beacon.eigenPod()), - data: Encode.upgradeableBeacon.upgradeTo({newImpl: address(Env.impl.eigenPod())}) + to: Env.proxyAdmin(), + data: Encode.proxyAdmin.upgrade({ + proxy: address(Env.proxy.allocationManager()), + impl: address(Env.impl.allocationManager()) + }) + }).append({ + to: Env.proxyAdmin(), + data: Encode.proxyAdmin.upgrade({ + proxy: address(Env.proxy.strategyManager()), + impl: address(Env.impl.strategyManager()) + }) }).append({ to: Env.proxyAdmin(), data: Encode.proxyAdmin.upgrade({ proxy: address(Env.proxy.eigenPodManager()), impl: address(Env.impl.eigenPodManager()) }) + }).append({ + to: Env.proxyAdmin(), + data: Encode.proxyAdmin.upgrade({ + proxy: address(Env.proxy.eigenStrategy()), + impl: address(Env.impl.eigenStrategy()) + }) + }).append({ + to: address(Env.beacon.strategyBase()), + data: Encode.upgradeableBeacon.upgradeTo({newImpl: address(Env.impl.strategyBase())}) }); + // Add call to upgrade each pre-longtail strategy instance + uint256 count = Env.instance.strategyBaseTVLLimits_Count(); + for (uint256 i = 0; i < count; i++) { + address proxyInstance = address(Env.instance.strategyBaseTVLLimits(i)); + + executorCalls.append({ + to: Env.proxyAdmin(), + data: Encode.proxyAdmin.upgrade({proxy: proxyInstance, impl: address(Env.impl.strategyBaseTVLLimits())}) + }); + } + return Encode.gnosisSafe.execTransaction({ from: address(Env.timelockController()), to: Env.multiSendCallOnly(), diff --git a/script/releases/v.1.5.0-redistribution/3-executeUpgrade.s.sol b/script/releases/v.1.5.0-redistribution/3-executeUpgrade.s.sol new file mode 100644 index 0000000000..a899414860 --- /dev/null +++ b/script/releases/v.1.5.0-redistribution/3-executeUpgrade.s.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "../Env.sol"; +import {QueueUpgrade} from "./2-queueUpgrade.s.sol"; + +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +contract Execute is QueueUpgrade { + using Env for *; + + function _runAsMultisig() internal override prank(Env.protocolCouncilMultisig()) { + bytes memory calldata_to_executor = _getCalldataToExecutor(); + + TimelockController timelock = Env.timelockController(); + timelock.execute({ + target: Env.executorMultisig(), + value: 0, + payload: calldata_to_executor, + predecessor: 0, + salt: 0 + }); + } + + function testScript() public virtual override { + runAsEOA(); + + TimelockController timelock = Env.timelockController(); + bytes memory calldata_to_executor = _getCalldataToExecutor(); + bytes32 txHash = timelock.hashOperation({ + target: Env.executorMultisig(), + value: 0, + data: calldata_to_executor, + predecessor: 0, + salt: 0 + }); + + assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued."); + + // 1- run queueing logic + QueueUpgrade._runAsMultisig(); + _unsafeResetHasPranked(); // reset hasPranked so we can use it again + + assertTrue(timelock.isOperationPending(txHash), "Transaction should be queued."); + assertFalse(timelock.isOperationReady(txHash), "Transaction should NOT be ready for execution."); + assertFalse(timelock.isOperationDone(txHash), "Transaction should NOT be complete."); + + // 2- warp past delay + vm.warp(block.timestamp + timelock.getMinDelay()); // 1 tick after ETA + assertEq(timelock.isOperationReady(txHash), true, "Transaction should be executable."); + + // 3- execute + execute(); + + assertTrue(timelock.isOperationDone(txHash), "Transaction should be complete."); + + _validateNewImplAddresses({areMatching: true}); + _validateProxyAdmins(); + _validateProxyConstructors(); + _validateProxiesInitialized(); + } + + /// @dev Mirrors the checks done in 1-deployContracts, but now we check each contract's + /// proxy, as the upgrade should mean that each proxy can see these methods/immutables + function _validateProxyConstructors() internal view { + SlashEscrowFactory slashEscrowFactory = Env.proxy.slashEscrowFactory(); + assertTrue(slashEscrowFactory.allocationManager() == Env.proxy.allocationManager(), "sef.alm invalid"); + assertTrue(slashEscrowFactory.strategyManager() == Env.proxy.strategyManager(), "sef.sm invalid"); + assertTrue(slashEscrowFactory.pauserRegistry() == Env.impl.pauserRegistry(), "sef.pR invalid"); + assertTrue(slashEscrowFactory.slashEscrowImplementation() == Env.impl.slashEscrow(), "sef.se invalid"); + // Check slashEscrowFactory local vars again for sanity + assertTrue(slashEscrowFactory.owner() == Env.executorMultisig(), "sef.owner invalid"); + assertTrue(slashEscrowFactory.paused() == 0, "sef.paused invalid"); + assertTrue(slashEscrowFactory.getGlobalEscrowDelay() == Env.SLASH_ESCROW_DELAY(), "sef.globalDelay invalid"); + + AllocationManager allocationManager = Env.proxy.allocationManager(); + assertTrue(allocationManager.delegation() == Env.proxy.delegationManager(), "alm.dm invalid"); + assertTrue(allocationManager.pauserRegistry() == Env.impl.pauserRegistry(), "alm.pR invalid"); + assertTrue(allocationManager.permissionController() == Env.proxy.permissionController(), "alm.pc invalid"); + assertTrue(allocationManager.DEALLOCATION_DELAY() == Env.MIN_WITHDRAWAL_DELAY(), "alm.deallocDelay invalid"); + assertTrue( + allocationManager.ALLOCATION_CONFIGURATION_DELAY() == Env.ALLOCATION_CONFIGURATION_DELAY(), + "alm.configDelay invalid" + ); + + DelegationManager delegation = Env.proxy.delegationManager(); + assertTrue(delegation.strategyManager() == Env.proxy.strategyManager(), "dm.sm invalid"); + assertTrue(delegation.eigenPodManager() == Env.proxy.eigenPodManager(), "dm.epm invalid"); + assertTrue(delegation.allocationManager() == Env.proxy.allocationManager(), "dm.alm invalid"); + assertTrue(delegation.pauserRegistry() == Env.impl.pauserRegistry(), "dm.pR invalid"); + assertTrue(delegation.permissionController() == Env.proxy.permissionController(), "dm.pc invalid"); + assertTrue(delegation.minWithdrawalDelayBlocks() == Env.MIN_WITHDRAWAL_DELAY(), "dm.withdrawalDelay invalid"); + + StrategyManager strategyManager = Env.proxy.strategyManager(); + assertTrue(strategyManager.delegation() == Env.proxy.delegationManager(), "sm.dm invalid"); + assertTrue(strategyManager.pauserRegistry() == Env.impl.pauserRegistry(), "sm.pR invalid"); + + EigenPodManager eigenPodManager = Env.proxy.eigenPodManager(); + assertTrue(eigenPodManager.ethPOS() == Env.ethPOS(), "epm.ethPOS invalid"); + assertTrue(eigenPodManager.eigenPodBeacon() == Env.beacon.eigenPod(), "epm.epBeacon invalid"); + assertTrue(eigenPodManager.delegationManager() == Env.proxy.delegationManager(), "epm.dm invalid"); + assertTrue(eigenPodManager.pauserRegistry() == Env.impl.pauserRegistry(), "epm.pR invalid"); + + /// strategies/ + EigenStrategy eigenStrategy = Env.proxy.eigenStrategy(); + assertTrue(eigenStrategy.strategyManager() == Env.proxy.strategyManager(), "eigStrat.sm invalid"); + assertTrue(eigenStrategy.pauserRegistry() == Env.impl.pauserRegistry(), "eigStrat.pR invalid"); + + UpgradeableBeacon strategyBeacon = Env.beacon.strategyBase(); + assertTrue(strategyBeacon.implementation() == address(Env.impl.strategyBase()), "strategyBeacon.impl invalid"); + + uint256 count = Env.instance.strategyBaseTVLLimits_Count(); + for (uint256 i = 0; i < count; i++) { + StrategyBaseTVLLimits strategy = Env.instance.strategyBaseTVLLimits(i); + + assertTrue(strategy.strategyManager() == Env.proxy.strategyManager(), "sFact.sm invalid"); + assertTrue(strategy.pauserRegistry() == Env.impl.pauserRegistry(), "sFact.pR invalid"); + } + } + + /// @dev Call initialize on all proxies to ensure they are initialized + /// Additionally, validate initialization variables + function _validateProxiesInitialized() internal { + bytes memory errInit = "Initializable: contract is already initialized"; + + // SlashEscrow is immutable and uninitializable + SlashEscrowFactory slashEscrowFactory = Env.proxy.slashEscrowFactory(); + vm.expectRevert(errInit); + slashEscrowFactory.initialize(address(0), 0, 0); + assertTrue(slashEscrowFactory.owner() == Env.executorMultisig(), "sef.owner invalid"); + assertTrue(slashEscrowFactory.paused() == 0, "sef.paused invalid"); + + AllocationManager allocationManager = Env.proxy.allocationManager(); + vm.expectRevert(errInit); + allocationManager.initialize(0); + assertTrue(allocationManager.paused() == 0, "alm.paused invalid"); + + DelegationManager delegation = Env.proxy.delegationManager(); + vm.expectRevert(errInit); + delegation.initialize(0); + assertTrue(delegation.paused() == 0, "dm.paused invalid"); + + StrategyManager strategyManager = Env.proxy.strategyManager(); + vm.expectRevert(errInit); + strategyManager.initialize(address(0), address(0), 0); + assertTrue(strategyManager.owner() == Env.executorMultisig(), "sm.owner invalid"); + assertTrue(strategyManager.paused() == 0, "sm.paused invalid"); + assertTrue( + strategyManager.strategyWhitelister() == address(Env.proxy.strategyFactory()), "sm.whitelister invalid" + ); + + EigenPodManager eigenPodManager = Env.proxy.eigenPodManager(); + vm.expectRevert(errInit); + eigenPodManager.initialize(address(0), 0); + assertTrue(eigenPodManager.owner() == Env.executorMultisig(), "epm.owner invalid"); + // For sepolia, eigenpodmanager is paused + if (block.chainid != 11_155_111) { + assertTrue(eigenPodManager.paused() == 0, "epm.paused invalid"); + } else { + assertTrue(eigenPodManager.paused() == 487, "epm.paused invalid"); + } + + EigenStrategy eigenStrategy = Env.proxy.eigenStrategy(); + vm.expectRevert(errInit); + eigenStrategy.initialize(IEigen(address(0)), IBackingEigen(address(0))); + assertTrue(eigenStrategy.paused() == 0, "eigenStrat.paused invalid"); + assertTrue(eigenStrategy.EIGEN() == Env.proxy.eigen(), "eigenStrat.EIGEN invalid"); + assertTrue(eigenStrategy.underlyingToken() == Env.proxy.beigen(), "eigenStrat.underlying invalid"); + + // StrategyBase proxies are initialized when deployed by factory + + uint256 count = Env.instance.strategyBaseTVLLimits_Count(); + for (uint256 i = 0; i < count; i++) { + StrategyBaseTVLLimits strategy = Env.instance.strategyBaseTVLLimits(i); + + emit log_named_address("strat", address(strategy)); + + vm.expectRevert(errInit); + strategy.initialize(0, 0, IERC20(address(0))); + assertTrue(strategy.maxPerDeposit() == type(uint256).max, "stratTVLLim.maxPerDeposit invalid"); + assertTrue(strategy.maxTotalDeposits() == type(uint256).max, "stratTVLLim.maxPerDeposit invalid"); + } + } +} diff --git a/script/releases/v1.4.1-slashing/upgrade.json b/script/releases/v.1.5.0-redistribution/upgrade.json similarity index 79% rename from script/releases/v1.4.1-slashing/upgrade.json rename to script/releases/v.1.5.0-redistribution/upgrade.json index 0095463e99..75d1055b88 100644 --- a/script/releases/v1.4.1-slashing/upgrade.json +++ b/script/releases/v.1.5.0-redistribution/upgrade.json @@ -1,7 +1,7 @@ { - "name": "slashing-auditFixes-cantina", - "from": "1.4.0", - "to": "1.4.1", + "name": "redistribution", + "from": ">=1.3.0", + "to": "1.5.0", "phases": [ { "type": "eoa", diff --git a/script/releases/v1.4.1-slashing/1-deployContracts.s.sol b/script/releases/v1.4.1-slashing/1-deployContracts.s.sol deleted file mode 100644 index 875a61e5c5..0000000000 --- a/script/releases/v1.4.1-slashing/1-deployContracts.s.sol +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {EOADeployer} from "zeus-templates/templates/EOADeployer.sol"; -import "../Env.sol"; - -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -/** - * Purpose: use an EOA to deploy all of the new contracts for this upgrade. - */ -contract Deploy is EOADeployer { - using Env for *; - - function _runAsEOA() internal override { - vm.startBroadcast(); - - // We are upgrading 3 contracts: DelegationManager, EigenPodManager, and EigenPod - deployImpl({ - name: type(DelegationManager).name, - deployedTo: address( - new DelegationManager({ - _strategyManager: Env.proxy.strategyManager(), - _eigenPodManager: Env.proxy.eigenPodManager(), - _allocationManager: Env.proxy.allocationManager(), - _pauserRegistry: Env.impl.pauserRegistry(), - _permissionController: Env.proxy.permissionController(), - _MIN_WITHDRAWAL_DELAY: Env.MIN_WITHDRAWAL_DELAY(), - _version: Env.deployVersion() - }) - ) - }); - - deployImpl({ - name: type(EigenPodManager).name, - deployedTo: address( - new EigenPodManager({ - _ethPOS: Env.ethPOS(), - _eigenPodBeacon: Env.beacon.eigenPod(), - _delegationManager: Env.proxy.delegationManager(), - _pauserRegistry: Env.impl.pauserRegistry(), - _version: Env.deployVersion() - }) - ) - }); - - deployImpl({ - name: type(EigenPod).name, - deployedTo: address( - new EigenPod({ - _ethPOS: Env.ethPOS(), - _eigenPodManager: Env.proxy.eigenPodManager(), - _GENESIS_TIME: Env.EIGENPOD_GENESIS_TIME(), - _version: Env.deployVersion() - }) - ) - }); - - vm.stopBroadcast(); - } - - function testScript() public virtual { - _runAsEOA(); - - _validateNewImplAddresses({areMatching: false}); - _validateProxyAdmins(); - _validateImplConstructors(); - _validateImplsInitialized(); - _validateVersion(); - } - - /// @dev Validate that the `Env.impl` addresses are updated to be distinct from what the proxy - /// admin reports as the current implementation address. - /// - /// Note: The upgrade script can call this with `areMatching == true` to check that these impl - /// addresses _are_ matches. - function _validateNewImplAddresses( - bool areMatching - ) internal view { - /// core/ - - function (bool, string memory) internal pure assertion = areMatching ? _assertTrue : _assertFalse; - - assertion( - _getProxyImpl(address(Env.proxy.delegationManager())) == address(Env.impl.delegationManager()), - "delegationManager impl failed" - ); - - assertion(Env.beacon.eigenPod().implementation() == address(Env.impl.eigenPod()), "eigenPod impl failed"); - - assertion( - _getProxyImpl(address(Env.proxy.eigenPodManager())) == address(Env.impl.eigenPodManager()), - "eigenPodManager impl failed" - ); - } - - /// @dev Ensure each deployed TUP/beacon is owned by the proxyAdmin/executorMultisig - function _validateProxyAdmins() internal view { - address pa = Env.proxyAdmin(); - - assertTrue( - _getProxyAdmin(address(Env.proxy.delegationManager())) == pa, "delegationManager proxyAdmin incorrect" - ); - - assertTrue(Env.beacon.eigenPod().owner() == Env.executorMultisig(), "eigenPod beacon owner incorrect"); - - assertTrue(_getProxyAdmin(address(Env.proxy.eigenPodManager())) == pa, "eigenPodManager proxyAdmin incorrect"); - } - - /// @dev Validate the immutables set in the new implementation constructors - function _validateImplConstructors() internal view { - { - DelegationManager delegation = Env.impl.delegationManager(); - assertTrue(delegation.strategyManager() == Env.proxy.strategyManager(), "dm.sm invalid"); - assertTrue(delegation.eigenPodManager() == Env.proxy.eigenPodManager(), "dm.epm invalid"); - assertTrue(delegation.allocationManager() == Env.proxy.allocationManager(), "dm.alm invalid"); - assertTrue(delegation.pauserRegistry() == Env.impl.pauserRegistry(), "dm.pR invalid"); - assertTrue(delegation.permissionController() == Env.proxy.permissionController(), "dm.pc invalid"); - assertTrue( - delegation.minWithdrawalDelayBlocks() == Env.MIN_WITHDRAWAL_DELAY(), "dm.withdrawalDelay invalid" - ); - } - - { - /// pods/ - EigenPod eigenPod = Env.impl.eigenPod(); - assertTrue(eigenPod.ethPOS() == Env.ethPOS(), "ep.ethPOS invalid"); - assertTrue(eigenPod.eigenPodManager() == Env.proxy.eigenPodManager(), "ep.epm invalid"); - assertTrue(eigenPod.GENESIS_TIME() == Env.EIGENPOD_GENESIS_TIME(), "ep.genesis invalid"); - - EigenPodManager eigenPodManager = Env.impl.eigenPodManager(); - assertTrue(eigenPodManager.ethPOS() == Env.ethPOS(), "epm.ethPOS invalid"); - assertTrue(eigenPodManager.eigenPodBeacon() == Env.beacon.eigenPod(), "epm.epBeacon invalid"); - assertTrue(eigenPodManager.delegationManager() == Env.proxy.delegationManager(), "epm.dm invalid"); - assertTrue(eigenPodManager.pauserRegistry() == Env.impl.pauserRegistry(), "epm.pR invalid"); - } - } - - /// @dev Call initialize on all deployed implementations to ensure initializers are disabled - function _validateImplsInitialized() internal { - bytes memory errInit = "Initializable: contract is already initialized"; - - DelegationManager delegation = Env.impl.delegationManager(); - vm.expectRevert(errInit); - delegation.initialize(0); - - /// pods/ - EigenPod eigenPod = Env.impl.eigenPod(); - vm.expectRevert(errInit); - eigenPod.initialize(address(0)); - - EigenPodManager eigenPodManager = Env.impl.eigenPodManager(); - vm.expectRevert(errInit); - eigenPodManager.initialize(address(0), 0); - } - - function _validateVersion() internal view { - // On future upgrades, just tick the major/minor/patch to validate - string memory expected = Env.deployVersion(); - - assertEq(Env.impl.delegationManager().version(), expected, "delegationManager version mismatch"); - assertEq(Env.impl.eigenPod().version(), expected, "eigenPod version mismatch"); - assertEq(Env.impl.eigenPodManager().version(), expected, "eigenPodManager version mismatch"); - } - - /// @dev Query and return `proxyAdmin.getProxyImplementation(proxy)` - function _getProxyImpl( - address proxy - ) internal view returns (address) { - return ProxyAdmin(Env.proxyAdmin()).getProxyImplementation(ITransparentUpgradeableProxy(proxy)); - } - - /// @dev Query and return `proxyAdmin.getProxyAdmin(proxy)` - function _getProxyAdmin( - address proxy - ) internal view returns (address) { - return ProxyAdmin(Env.proxyAdmin()).getProxyAdmin(ITransparentUpgradeableProxy(proxy)); - } - - function _assertTrue(bool b, string memory err) private pure { - assertTrue(b, err); - } - - function _assertFalse(bool b, string memory err) private pure { - assertFalse(b, err); - } -} diff --git a/script/releases/v1.4.1-slashing/3-executeUpgrade.s.sol b/script/releases/v1.4.1-slashing/3-executeUpgrade.s.sol deleted file mode 100644 index 0fe0d09b0d..0000000000 --- a/script/releases/v1.4.1-slashing/3-executeUpgrade.s.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import "../Env.sol"; -import {QueueUpgrade} from "./2-queueUpgrade.s.sol"; - -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; - -contract Execute is QueueUpgrade { - using Env for *; - - function _runAsMultisig() internal override prank(Env.protocolCouncilMultisig()) { - bytes memory calldata_to_executor = _getCalldataToExecutor(); - - TimelockController timelock = Env.timelockController(); - timelock.execute({ - target: Env.executorMultisig(), - value: 0, - payload: calldata_to_executor, - predecessor: 0, - salt: 0 - }); - } - - function testScript() public virtual override { - runAsEOA(); - - TimelockController timelock = Env.timelockController(); - bytes memory calldata_to_executor = _getCalldataToExecutor(); - bytes32 txHash = timelock.hashOperation({ - target: Env.executorMultisig(), - value: 0, - data: calldata_to_executor, - predecessor: 0, - salt: 0 - }); - - assertFalse(timelock.isOperationPending(txHash), "Transaction should NOT be queued."); - - // 1- run queueing logic - QueueUpgrade._runAsMultisig(); - _unsafeResetHasPranked(); // reset hasPranked so we can use it again - - assertTrue(timelock.isOperationPending(txHash), "Transaction should be queued."); - assertFalse(timelock.isOperationReady(txHash), "Transaction should NOT be ready for execution."); - assertFalse(timelock.isOperationDone(txHash), "Transaction should NOT be complete."); - - // 2- warp past delay - vm.warp(block.timestamp + timelock.getMinDelay()); // 1 tick after ETA - assertEq(timelock.isOperationReady(txHash), true, "Transaction should be executable."); - - // 3- execute - execute(); - - assertTrue(timelock.isOperationDone(txHash), "Transaction should be complete."); - - _validateNewImplAddresses({areMatching: true}); - _validateProxyAdmins(); - _validateProxyConstructors(); - _validateProxiesInitialized(); - } - - /// @dev Mirrors the checks done in 1-deployContracts, but now we check each contract's - /// proxy, as the upgrade should mean that each proxy can see these methods/immutables - function _validateProxyConstructors() internal view { - DelegationManager delegation = Env.proxy.delegationManager(); - assertTrue(delegation.strategyManager() == Env.proxy.strategyManager(), "dm.sm invalid"); - assertTrue(delegation.eigenPodManager() == Env.proxy.eigenPodManager(), "dm.epm invalid"); - assertTrue(delegation.allocationManager() == Env.proxy.allocationManager(), "dm.alm invalid"); - assertTrue(delegation.pauserRegistry() == Env.impl.pauserRegistry(), "dm.pR invalid"); - assertTrue(delegation.permissionController() == Env.proxy.permissionController(), "dm.pc invalid"); - assertTrue(delegation.minWithdrawalDelayBlocks() == Env.MIN_WITHDRAWAL_DELAY(), "dm.withdrawalDelay invalid"); - - UpgradeableBeacon eigenPodBeacon = Env.beacon.eigenPod(); - assertTrue(eigenPodBeacon.implementation() == address(Env.impl.eigenPod()), "eigenPodBeacon.impl invalid"); - - EigenPodManager eigenPodManager = Env.proxy.eigenPodManager(); - assertTrue(eigenPodManager.ethPOS() == Env.ethPOS(), "epm.ethPOS invalid"); - assertTrue(eigenPodManager.eigenPodBeacon() == Env.beacon.eigenPod(), "epm.epBeacon invalid"); - assertTrue(eigenPodManager.delegationManager() == Env.proxy.delegationManager(), "epm.dm invalid"); - assertTrue(eigenPodManager.pauserRegistry() == Env.impl.pauserRegistry(), "epm.pR invalid"); - } - - /// @dev Call initialize on all proxies to ensure they are initialized - /// Additionally, validate initialization variables - function _validateProxiesInitialized() internal { - bytes memory errInit = "Initializable: contract is already initialized"; - - DelegationManager delegation = Env.proxy.delegationManager(); - vm.expectRevert(errInit); - delegation.initialize(0); - // assertTrue(delegation.owner() == Env.executorMultisig(), "dm.owner invalid"); - assertTrue(delegation.paused() == 0, "dm.paused invalid"); - - EigenPodManager eigenPodManager = Env.proxy.eigenPodManager(); - vm.expectRevert(errInit); - eigenPodManager.initialize(address(0), 0); - assertTrue(eigenPodManager.owner() == Env.executorMultisig(), "epm.owner invalid"); - if (keccak256(bytes(Env.env())) != keccak256(bytes("testnet-sepolia"))) { - assertTrue(eigenPodManager.paused() == 0, "epm.paused invalid"); - } - } -}