diff --git a/.zeus b/.zeus new file mode 100644 index 000000000..acc61f3cd --- /dev/null +++ b/.zeus @@ -0,0 +1,4 @@ +{ + "zeusHost": "https://github.com/Layr-Labs/eigenlayer-contracts-metadata", + "migrationDirectory": "script/releases" +} diff --git a/foundry.toml b/foundry.toml index d294bafcc..1cdda3124 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,6 +6,7 @@ fs_permissions = [{ access = "read-write", path = "./"}, { access = "read-write" gas_reports = ["*"] # ignore upgrade testing in scripts by default no_match_test = "queueUpgrade" +no_match_path = "script/releases/**/*.sol" # A list of ignored solc error codes diff --git a/lib/zeus-templates b/lib/zeus-templates index 66f7f78ca..87e5faca6 160000 --- a/lib/zeus-templates +++ b/lib/zeus-templates @@ -1 +1 @@ -Subproject commit 66f7f78ca14389229edff88c1d08025c1c7a9e9b +Subproject commit 87e5faca6fde0ba39ab63ad7f3cf275c92779823 diff --git a/script/releases/EigenLabsUpgrade.s.sol b/script/releases/EigenLabsUpgrade.s.sol new file mode 100644 index 000000000..c18c60f96 --- /dev/null +++ b/script/releases/EigenLabsUpgrade.s.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {ZeusScript} from "zeus-templates/utils/ZeusScript.sol"; +import {EncGnosisSafe} from "zeus-templates/utils/EncGnosisSafe.sol"; +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; + +library EigenLabsUpgrade { + using EncGnosisSafe for *; + + function _ethPos( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("ethPOS"); + } + + function _eigenpodGenesisTime( + ZeusScript self + ) internal view returns (uint64) { + return self.zUint64("EIGENPOD_GENESIS_TIME"); + } + + function _eigenPodManagerPendingImpl( + ZeusScript self + ) internal view returns (address) { + return self.zDeployedContract("EigenPodManager_pendingImpl"); + } + + function _operationsMultisig( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("operationsMultisig"); + } + + function _pauserRegistry( + ZeusScript self + ) internal view returns (address) { + return self.zDeployedContract("PauserRegistry"); + } + + function _proxyAdmin( + ZeusScript self + ) internal view returns (address) { + return self.zDeployedContract("ProxyAdmin"); + } + + function _eigenPodManagerProxy( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("EigenPodManager_proxy"); + } + + function _eigenPodBeacon( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("EigenPod_beacon"); + } + + function _eigenPodPendingImpl( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("EigenPod_pendingImpl"); + } + + function _multiSendCallOnly( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("MultiSendCallOnly"); + } + + function _timelock( + ZeusScript self + ) internal view returns (TimelockController) { + return TimelockController(payable(self.zAddress("timelockController"))); + } + + function _executorMultisig( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("executorMultisig"); + } + + function _protocolCouncilMultisig( + ZeusScript self + ) internal view returns (address) { + return self.zAddress("protocolCouncilMultisig"); + } +} diff --git a/script/releases/release-template/1-eoa.s.sol b/script/releases/release-template/1-eoa.s.sol deleted file mode 100644 index 912aa9278..000000000 --- a/script/releases/release-template/1-eoa.s.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {EOADeployer} from "zeus-templates/templates/EOADeployer.sol"; - -contract Deploy is EOADeployer { - Deployment[] private _deployments; - - function _deploy() internal override returns (Deployment[] memory) { - vm.startBroadcast(); - - ////////////////////////// - // deploy your contracts here - ////////////////////////// - - vm.stopBroadcast(); - - return _deployments; - } - - function zeusTest() public override { - // Test function implementation - } -} diff --git a/script/releases/release-template/2-multisig.s.sol b/script/releases/release-template/2-multisig.s.sol deleted file mode 100644 index 86f682818..000000000 --- a/script/releases/release-template/2-multisig.s.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {MultisigCall, MultisigCallUtils, MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; -import {ITimelock} from "zeus-templates/interfaces/ITimelock.sol"; - -contract Queue is MultisigBuilder { - using MultisigCallUtils for MultisigCall[]; - - MultisigCall[] private _executorCalls; - MultisigCall[] private _opsCalls; - - function _queue() internal returns (MultisigCall[] memory) { - - // Example of a call to a contract - remove this - _executorCalls.append({ - to: address(0), - data: abi.encodeWithSelector(bytes4(keccak256("test(uint256)")), uint256(0)) - }); - - ////////////////////////// - // construct executor data here - ////////////////////////// - - return _executorCalls; - } - - /** - * @dev Can be overridden so that executing script can inherit this contract. - */ - function _execute() internal virtual override returns (MultisigCall[] memory) { - // get the queue data - MultisigCall[] memory calls = _queue(); - - address multiSendCallOnly = zeusAddress("MultiSendCallOnly"); - address timelock = zeusAddress("Timelock"); - - // encode calls for executor - bytes memory executorCalldata = calls.makeExecutorCalldata(multiSendCallOnly, timelock); - - address executorMultisig = zeusAddress("ExecutorMultisig"); - - // encode executor data for timelock - bytes memory timelockCalldata = abi.encodeWithSelector( - ITimelock.queueTransaction.selector, executorMultisig, 0, "", executorCalldata, type(uint256).max - ); - - _opsCalls.append(timelock, timelockCalldata); - - // encode timelock data for ops multisig - return _opsCalls; - } - - function zeusTest() public override { - // Test function implementation - } -} diff --git a/script/releases/release-template/3-multisig.s.sol b/script/releases/release-template/3-multisig.s.sol deleted file mode 100644 index a77f4df6c..000000000 --- a/script/releases/release-template/3-multisig.s.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {MultisigCall, MultisigCallUtils, MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; -import {SafeTx, SafeTxUtils} from "zeus-templates/utils/SafeTxUtils.sol"; -import {ITimelock} from "zeus-templates/interfaces/ITimelock.sol"; -import {Queue} from "./2-multisig.s.sol"; - -contract Execute is Queue { - using MultisigCallUtils for MultisigCall[]; - using SafeTxUtils for SafeTx; - - MultisigCall[] private _opsCalls; - - /** - * @dev Overrides the previous _execute function to execute the queued transactions. - */ - function _execute() internal override returns (MultisigCall[] memory) { - MultisigCall[] memory _executorCalls = _queue(); - - address multiSendCallOnly = zeusAddress("MultiSendCallOnly"); - address timelock = zeusAddress("Timelock"); - - bytes memory executorCalldata = _executorCalls.makeExecutorCalldata(multiSendCallOnly, timelock); - - // execute queued transaction - _opsCalls.append({ - to: timelock, - value: 0, - data: abi.encodeWithSelector(ITimelock.executeTransaction.selector, executorCalldata) - }); - - ////////////////////////// - // add more opsCalls here - ////////////////////////// - - return _opsCalls; - } -} diff --git a/script/releases/release-template/README.md b/script/releases/release-template/README.md deleted file mode 100644 index 1e1575c37..000000000 --- a/script/releases/release-template/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Release Template - -This template is an example of the deploy-queue-upgrade structure that can be used for executing EigenLayer upgrades via the Ops Timelock Multisig, as described [here](https://docs.eigenlayer.xyz/eigenlayer/security/multisig-governance). - -Zeus can take this template and instantiate a new release template, allowing for quick setup of a common upgrade process. - -Note that the names follow the syntax `#-`, where `#` is the sequence number within the release (i.e. the order in which actions are taken), and `` is the signing strategy to be used for that script. - -## 1-eoa.s.sol - -This contract allows for deployments to be broadcast. An EOA is expected to take these actions. - -## 2-multisig.s.sol - -This contract allows for actions to be written from the perspective of the Executor Multisig, which will then be wrapped for the Timelock, then for the Ops Multisig to execute. - -## 3-multisig.s.sol - -The final execution step can be written in this contract. It will reuse the logic from the previous step by importing the contract, then allow for additional calls to be appended from the perspective of the Ops Multisig. \ No newline at end of file diff --git a/script/releases/v0.1-eigenpod-example/.env.example b/script/releases/v0.1-eigenpod-example/.env.example deleted file mode 100644 index 2cd181039..000000000 --- a/script/releases/v0.1-eigenpod-example/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -ZEUS_DEPLOYED_ethPOS="0x4242424242424242424242424242424242424242" -ZEUS_ENV_EIGENPOD_GENESIS_TIME="1695902400" - -ZEUS_DEPLOYED_EigenPodManager_pendingImpl="0x10EBa780CCd9E5e9FFBe529C25046c076Be91048" -ZEUS_DEPLOYED_OperationsMultisig="0xDA29BB71669f46F2a779b4b62f03644A84eE3479" -ZEUS_DEPLOYED_PauserRegistry="0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1" -ZEUS_DEPLOYED_ProxyAdmin="0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B" -ZEUS_DEPLOYED_EigenPodManager_proxy="0xB8d8952f572e67B11e43bC21250967772fa883Ff" -ZEUS_DEPLOYED_EigenPod_beacon="0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC" -ZEUS_DEPLOYED_EigenPod_pendingImpl="0x8Da4b953cbFb715624D98C0D2b4a7978462eFd38" -ZEUS_DEPLOYED_MultiSendCallOnly="0x40A2aCCbd92BCA938b02010E17A5b8929b49130D" -ZEUS_DEPLOYED_Timelock="0x0000000000000000000000000000000000000000" -ZEUS_DEPLOYED_ExecutorMultisig="0xDA29BB71669f46F2a779b4b62f03644A84eE3479" \ No newline at end of file diff --git a/script/releases/v0.1-eigenpod-example/1-eoa.s.sol b/script/releases/v0.1-eigenpod-example/1-eoa.s.sol deleted file mode 100644 index ad67e32da..000000000 --- a/script/releases/v0.1-eigenpod-example/1-eoa.s.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import "zeus-templates/templates/EOADeployer.sol"; - -import "src/contracts/pods/EigenPod.sol"; -import "src/contracts/pods/EigenPodManager.sol"; -import "src/contracts/interfaces/IEigenPodManager.sol"; - -import "zeus-templates/utils/StringUtils.sol"; - -contract DeployEigenPodAndManager is EOADeployer { - using StringUtils for string; - - Deployment[] private _deployments; - - /// @notice deploys an EigenPod and returns the deployed address - function _deploy() internal override returns (Deployment[] memory) { - vm.startBroadcast(); - - // create a defunct EigenPodManager - address newEigenPodManager = address( - new EigenPodManager( - IETHPOSDeposit(address(1)), - IBeacon(address(2)), - IStrategyManager(address(3)), - ISlasher(address(4)), - IDelegationManager(address(5)) - ) - ); - - _deployments.push(singleton(newEigenPodManager, type(EigenPodManager).name)); - - address newEigenPod = address( - new EigenPod( - IETHPOSDeposit(_ethPos()), - IEigenPodManager(newEigenPodManager), // update EigenPodManager address - getUint64("EIGENPOD_GENESIS_TIME") // set genesis time - ) - ); - - // create and record new EigenPod pointing to defunct EigenPodManager - _deployments.push(singleton(newEigenPod, type(EigenPod).name)); - - vm.stopBroadcast(); - - return _deployments; - } - - function zeusTest() public override { - _deploy(); - - Deployment memory eigenPodManager = _deployments[0]; - Deployment memory eigenPod = _deployments[1]; - - require(eigenPodManager.deployedTo != address(0), "EigenPodManager deployment failed"); - - require(eigenPod.deployedTo != address(0), "EigenPod deployment failed"); - } -} diff --git a/script/releases/v0.1-eigenpod-example/2-multisig.s.sol b/script/releases/v0.1-eigenpod-example/2-multisig.s.sol deleted file mode 100644 index 54450ea6e..000000000 --- a/script/releases/v0.1-eigenpod-example/2-multisig.s.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {MultisigCall, MultisigCallUtils, MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; -import {SafeTx, SafeTxUtils} from "zeus-templates/utils/SafeTxUtils.sol"; - -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {IUpgradeableBeacon} from "script/interfaces/IUpgradeableBeacon.sol"; -import {ITimelock} from "zeus-templates/interfaces/ITimelock.sol"; -import "src/contracts/pods/EigenPodManager.sol"; - -contract QueueEigenPodAndManager is MultisigBuilder { - using MultisigCallUtils for MultisigCall[]; - using SafeTxUtils for SafeTx; - - MultisigCall[] private _executorCalls; - MultisigCall[] private _opsCalls; - - function _queue() internal returns (MultisigCall[] memory) { - // construct initialization data for eigenPodManager - bytes memory eigenPodManagerData = abi.encodeWithSelector( - EigenPodManager.initialize.selector, - _operationsMultisig(), // set opsMultisig as new direct owner - _pauserRegistry(), // set pauser registry - uint256(0) // set all 0 bits, nothing paused - ); - - // upgrade eigenPodManager - _executorCalls.append({ - to: _proxyAdmin(), - data: abi.encodeWithSelector( - ProxyAdmin.upgradeAndCall.selector, - _eigenPodManagerProxy(), - _eigenPodManagerPendingImpl(), - eigenPodManagerData // initialize impl here - ) - }); - - // upgrade eigenPod beacon implementation - _executorCalls.append({ - to: _eigenPodBeacon(), - data: abi.encodeWithSelector(IUpgradeableBeacon.upgradeTo.selector, _eigenPodPendingImpl()) - }); - - return _executorCalls; - } - - function _execute() internal virtual override returns (MultisigCall[] memory) { - // get the queue data - MultisigCall[] memory calls = _queue(); - - // encode calls for executor - bytes memory executorCalldata = calls.makeExecutorCalldata(_multiSendCallOnly(), _timelock()); - - // encode executor data for timelock - bytes memory timelockCalldata = abi.encodeWithSelector( - ITimelock.queueTransaction.selector, _executorMultisig(), 0, "", executorCalldata, type(uint256).max - ); - - _opsCalls.append(_timelock(), timelockCalldata); - - // encode timelock data for ops multisig - return _opsCalls; - } - - function zeusTest() public virtual override { - // Test function implementation - } -} diff --git a/script/releases/v0.1-eigenpod-example/3-multisig.s.sol b/script/releases/v0.1-eigenpod-example/3-multisig.s.sol deleted file mode 100644 index 028fa669f..000000000 --- a/script/releases/v0.1-eigenpod-example/3-multisig.s.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; - -import "src/contracts/pods/EigenPodManager.sol"; - -import "./2-multisig.s.sol"; - -contract ExecuteEigenPodAndManager is QueueEigenPodAndManager { - using MultisigCallUtils for MultisigCall[]; - using SafeTxUtils for *; - - MultisigCall[] private _multisigCalls; - - function _execute() internal override returns (MultisigCall[] memory) { - MultisigCall[] memory _executorCalls = _queue(); - - // steals logic from queue() to perform execute() - // likely the first step of any _execute() after a _queue() - bytes memory executorCalldata = _executorCalls.makeExecutorCalldata(_multiSendCallOnly(), _timelock()); - - // execute queued transaction upgrading eigenPodManager and eigenPod - _multisigCalls.append({ - to: _timelock(), - value: 0, - data: abi.encodeWithSelector(ITimelock.executeTransaction.selector, executorCalldata) - }); - - // after queued transaction, renounce ownership from eigenPodManager - _multisigCalls.append({ - to: _eigenPodManagerProxy(), - value: 0, - data: abi.encodeWithSelector(EigenPodManager(_eigenPodManagerProxy()).renounceOwnership.selector) - }); - - return _multisigCalls; - } - - function zeusTest() public override { - // Test function implementation - } -} diff --git a/script/releases/v0.1-eigenpod-example/README.md b/script/releases/v0.1-eigenpod-example/README.md deleted file mode 100644 index 250e3b74b..000000000 --- a/script/releases/v0.1-eigenpod-example/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# v0.1-eigenpod-example - -This is an example of how to write a deploy-queue-upgrade series of scripts using [Zeus](https://github.com/Layr-Labs/zeus). **THIS IS PURELY DEMONSTRATIVE. DO NOT USE THIS FOR PRODUCTION PURPOSES.** - -## How to Run - -To begin, run the following command from this directory: - -```sh -export $(cat .env.example | xargs) -``` - -This will populate your environment with example environment variables. - -If running with Zeus, then Zeus should auto-populate your environment for you, and this step is not necessary. - -### Commands - -For the deploy script (the first script), run: - -```sh -forge script 1-eoa.s.sol -s "deploy()" -``` - -You can expect an output similar to: - -```txt -Script ran successfully. -Gas used: 6407912 - -== Return == -0: struct EOADeployer.Deployment[] [Deployment({ deployedTo: 0x90193C961A926261B756D1E5bb255e67ff9498A1, overrideName: "", singleton: true }), Deployment({ deployedTo: 0xA8452Ec99ce0C64f20701dB7dD3abDb607c00496, overrideName: "", singleton: true })] -``` - -To run the queue script (the second script), run: - -```sh -forge script script/releases/v0.1-eigenpod-example/2-multisig.s.sol -s "execute()" -``` - -Example output: - -```txt -Script ran successfully. -Gas used: 1005076 - -== Return == -0: struct SafeTx SafeTx({ to: 0x40A2aCCbd92BCA938b02010E17A5b8929b49130D, value: 0, data: 0x8d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000419000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c43a66f901000000000000000000000000da29bb71669f46f2a779b4b62f03644a84ee3479000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c48d80ff0a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000040a2accbd92bca938b02010e17a5b8929b49130d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002248d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d2001bef05c7303d44e0e2fcd2a19d993eded4c51b5b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001049623609d000000000000000000000000b8d8952f572e67b11e43bc21250967772fa883ff00000000000000000000000010eba780ccd9e5e9ffbe529c25046c076be91048000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000641794bb3c000000000000000000000000da29bb71669f46f2a779b4b62f03644a84ee34790000000000000000000000009ab2feaf0465f0ed51fc2b663ef228b418c9dad10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092cc4a800a1513e85c481dddf3a06c6921211eac000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000243659cfe60000000000000000000000008da4b953cbfb715624d98c0d2b4a7978462efd380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, op: 1 }) -``` - -For the execute step (the third step), run: - -```sh -forge script script/releases/v0.1-eigenpod-example/3-multisig.s.sol -s "execute()" -``` - -Example output: - -```txt -Script ran successfully. -Gas used: 984745 - -== Return == -0: struct SafeTx SafeTx({ to: 0x40A2aCCbd92BCA938b02010E17A5b8929b49130D, value: 0, data: 0x8d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003240825f38f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002c48d80ff0a000000000000000000000000000000000000000000000000000000000000006000000000000000000000000040a2accbd92bca938b02010e17a5b8929b49130d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002248d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d2001bef05c7303d44e0e2fcd2a19d993eded4c51b5b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001049623609d000000000000000000000000b8d8952f572e67b11e43bc21250967772fa883ff00000000000000000000000010eba780ccd9e5e9ffbe529c25046c076be91048000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000641794bb3c000000000000000000000000da29bb71669f46f2a779b4b62f03644a84ee34790000000000000000000000009ab2feaf0465f0ed51fc2b663ef228b418c9dad10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092cc4a800a1513e85c481dddf3a06c6921211eac000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000243659cfe60000000000000000000000008da4b953cbfb715624d98c0d2b4a7978462efd380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b8d8952f572e67b11e43bc21250967772fa883ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004715018a60000000000000000000000000000, op: 1 }) -``` \ No newline at end of file diff --git a/script/releases/v0.5.1-rewardsv2/1-eoa.s.sol b/script/releases/v0.5.1-rewardsv2/1-eoa.s.sol new file mode 100644 index 000000000..adf756034 --- /dev/null +++ b/script/releases/v0.5.1-rewardsv2/1-eoa.s.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {EOADeployer} from "zeus-templates/templates/EOADeployer.sol"; +import {RewardsCoordinator} from "src/contracts/core/RewardsCoordinator.sol"; +import {IDelegationManager} from "src/contracts/interfaces/IDelegationManager.sol"; +import {DelegationManager} from "src/contracts/core/DelegationManager.sol"; +import {StrategyManager} from "src/contracts/core/StrategyManager.sol"; +import {EigenLabsUpgrade} from "../EigenLabsUpgrade.s.sol"; +import {Test, console} from "forge-std/Test.sol"; +import {IPauserRegistry} from "src/contracts/interfaces/IPauserRegistry.sol"; + +contract Deploy is EOADeployer { + using EigenLabsUpgrade for *; + + function _runAsEOA() internal override { + zUpdateUint16(string("REWARDS_COORDINATOR_DEFAULT_OPERATOR_SPLIT_BIPS"), uint16(1000)); + + vm.startBroadcast(); + deploySingleton( + address( + new RewardsCoordinator( + IDelegationManager(zDeployedProxy(type(DelegationManager).name)), + StrategyManager(zDeployedProxy(type(StrategyManager).name)), + zUint32("REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS"), + zUint32("REWARDS_COORDINATOR_MAX_REWARDS_DURATION"), + zUint32("REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH"), + zUint32("REWARDS_COORDINATOR_MAX_FUTURE_LENGTH"), + zUint32("REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP") + ) + ), + this.impl(type(RewardsCoordinator).name) + ); + + vm.stopBroadcast(); + } + + function testDeploy() public virtual { + // Deploy RewardsCoordinator Implementation + address oldImpl = zDeployedImpl(type(RewardsCoordinator).name); + runAsEOA(); + address newImpl = zDeployedImpl(type(RewardsCoordinator).name); + assertTrue(oldImpl != newImpl, "impl should be different"); + + Deployment[] memory deploys = deploys(); + + // sanity check that zDeployedImpl is returning our deployment. + assertEq(deploys[0].deployedTo, zDeployedImpl(type(RewardsCoordinator).name)); + + RewardsCoordinator rewardsCoordinatorImpl = RewardsCoordinator(zDeployedImpl(type(RewardsCoordinator).name)); + + address owner = this._operationsMultisig(); + IPauserRegistry pauserRegistry = IPauserRegistry(this._pauserRegistry()); + uint64 initPausedStatus = zUint64("REWARDS_COORDINATOR_INIT_PAUSED_STATUS"); + address rewardsUpdater = zAddress("REWARDS_COORDINATOR_UPDATER"); + uint32 activationDelay = zUint32("REWARDS_COORDINATOR_ACTIVATION_DELAY"); + uint16 defaultOperatorSplitBips = zUint16("REWARDS_COORDINATOR_DEFAULT_OPERATOR_SPLIT_BIPS"); + + // Ensure that the implementation contract cannot be initialized. + vm.expectRevert("Initializable: contract is already initialized"); + rewardsCoordinatorImpl.initialize( + owner, + pauserRegistry, + initPausedStatus, + rewardsUpdater, + activationDelay, + defaultOperatorSplitBips + ); + + // Assert Immutables and State Variables set through initialize + assertEq(rewardsCoordinatorImpl.owner(), address(0), "expected owner"); + assertEq(address(rewardsCoordinatorImpl.pauserRegistry()), address(0), "expected pauserRegistry"); + assertEq(address(rewardsCoordinatorImpl.rewardsUpdater()), address(0), "expected rewardsUpdater"); + assertEq(rewardsCoordinatorImpl.activationDelay(), 0, "expected activationDelay"); + assertEq(rewardsCoordinatorImpl.defaultOperatorSplitBips(), 0, "expected defaultOperatorSplitBips"); + + assertEq( + address(rewardsCoordinatorImpl.delegationManager()), + zDeployedProxy(type(DelegationManager).name), + "expected delegationManager" + ); + assertEq( + address(rewardsCoordinatorImpl.strategyManager()), + zDeployedProxy(type(StrategyManager).name), + "expected strategyManager" + ); + + assertEq( + rewardsCoordinatorImpl.CALCULATION_INTERVAL_SECONDS(), + zUint32("REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS"), + "expected REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS" + ); + assertGt( + rewardsCoordinatorImpl.CALCULATION_INTERVAL_SECONDS(), + 0, + "expected non-zero REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS" + ); + + assertEq(rewardsCoordinatorImpl.MAX_REWARDS_DURATION(), zUint32("REWARDS_COORDINATOR_MAX_REWARDS_DURATION")); + assertGt(rewardsCoordinatorImpl.MAX_REWARDS_DURATION(), 0); + + assertEq( + rewardsCoordinatorImpl.MAX_RETROACTIVE_LENGTH(), + zUint32("REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH") + ); + assertGt(rewardsCoordinatorImpl.MAX_RETROACTIVE_LENGTH(), 0); + + assertEq(rewardsCoordinatorImpl.MAX_FUTURE_LENGTH(), zUint32("REWARDS_COORDINATOR_MAX_FUTURE_LENGTH")); + assertGt(rewardsCoordinatorImpl.MAX_FUTURE_LENGTH(), 0); + + assertEq( + rewardsCoordinatorImpl.GENESIS_REWARDS_TIMESTAMP(), + zUint32("REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP") + ); + assertGt(rewardsCoordinatorImpl.GENESIS_REWARDS_TIMESTAMP(), 0); + + assertEq(deploys.length, 1, "expected exactly 1 deployment"); + assertEq( + keccak256(bytes(deploys[0].name)), + keccak256(bytes(this.impl(type(RewardsCoordinator).name))), + "zeusTest: Deployment name is not RewardsCoordinator" + ); + assertTrue(deploys[0].singleton == true, "zeusTest: RewardsCoordinator should be a singleton."); + assertNotEq(deploys[0].deployedTo, address(0), "zeusTest: Should deploy to non-zero address."); + } +} diff --git a/script/releases/v0.5.1-rewardsv2/2-multisig.s.sol b/script/releases/v0.5.1-rewardsv2/2-multisig.s.sol new file mode 100644 index 000000000..d42a3cc33 --- /dev/null +++ b/script/releases/v0.5.1-rewardsv2/2-multisig.s.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {MultisigCall, MultisigCallUtils, MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {Deploy} from "./1-eoa.s.sol"; +import {RewardsCoordinator} from "src/contracts/core/RewardsCoordinator.sol"; +import {EigenLabsUpgrade} from "../EigenLabsUpgrade.s.sol"; +import {IPauserRegistry} from "src/contracts/interfaces/IPauserRegistry.sol"; +import {ITimelock} from "zeus-templates/interfaces/ITimelock.sol"; +import {console} from "forge-std/console.sol"; +import {EncGnosisSafe} from "zeus-templates/utils/EncGnosisSafe.sol"; +import {MultisigCallUtils, MultisigCall} from "zeus-templates/utils/MultisigCallUtils.sol"; +import {IMultiSend} from "zeus-templates/interfaces/IMultiSend.sol"; +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; + +/** + * Purpose: enqueue a multisig transaction which tells the ProxyAdmin to upgrade RewardsCoordinator. + */ +contract Queue is MultisigBuilder, Deploy { + using MultisigCallUtils for MultisigCall[]; + using EigenLabsUpgrade for *; + using EncGnosisSafe for *; + using MultisigCallUtils for *; + + MultisigCall[] private _executorCalls; + MultisigCall[] private _opsCalls; + + function _getMultisigTransactionCalldata() internal view returns (bytes memory) { + ProxyAdmin pa = ProxyAdmin(this._proxyAdmin()); + + bytes memory proxyAdminCalldata = abi.encodeCall( + pa.upgrade, + ( + TransparentUpgradeableProxy(payable(zDeployedProxy(type(RewardsCoordinator).name))), + zDeployedImpl(type(RewardsCoordinator).name) + ) + ); + + bytes memory executorMultisigCalldata = address(this._timelock()).calldataToExecTransaction( + this._proxyAdmin(), + proxyAdminCalldata, + EncGnosisSafe.Operation.Call + ); + + return (executorMultisigCalldata); + } + + function _runAsMultisig() internal virtual override { + bytes memory executorMultisigCalldata = _getMultisigTransactionCalldata(); + + TimelockController timelock = TimelockController(payable(this._timelock())); + timelock.schedule( + this._executorMultisig(), + 0 /* value */, + executorMultisigCalldata, + 0 /* predecessor */, + bytes32(0) /* salt */, + timelock.getMinDelay() + ); + } + + function testDeploy() public virtual override { + runAsEOA(); + + zSetMultisigContext(this._operationsMultisig()); // this test will run as if the ops multisig ran the step + execute(); + TimelockController timelock = TimelockController(payable(this._timelock())); + + bytes memory multisigTxnData = _getMultisigTransactionCalldata(); + bytes32 txHash = timelock.hashOperation(this._executorMultisig(), 0, multisigTxnData, 0, 0); + + assertEq(timelock.isOperationPending(txHash), true, "Transaction should be queued."); + } +} diff --git a/script/releases/v0.5.1-rewardsv2/3-multisig.s.sol b/script/releases/v0.5.1-rewardsv2/3-multisig.s.sol new file mode 100644 index 000000000..d98950bbf --- /dev/null +++ b/script/releases/v0.5.1-rewardsv2/3-multisig.s.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {MultisigCall, MultisigCallUtils, MultisigBuilder} from "zeus-templates/templates/MultisigBuilder.sol"; +import {SafeTx, SafeTxUtils} from "zeus-templates/utils/SafeTxUtils.sol"; +import {Queue} from "./2-multisig.s.sol"; +import {EigenLabsUpgrade} from "../EigenLabsUpgrade.s.sol"; +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; +import {RewardsCoordinator} from "src/contracts/core/RewardsCoordinator.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {IPauserRegistry} from "src/contracts/interfaces/IPauserRegistry.sol"; +import {DelegationManager} from "src/contracts/core/DelegationManager.sol"; +import {StrategyManager} from "src/contracts/core/StrategyManager.sol"; +import {console} from "forge-std/console.sol"; + +contract Execute is Queue { + using MultisigCallUtils for MultisigCall[]; + using SafeTxUtils for SafeTx; + using EigenLabsUpgrade for *; + + /** + * @dev Overrides the previous _execute function to execute the queued transactions. + */ + function _runAsMultisig() internal override { + bytes memory executorMultisigCalldata = _getMultisigTransactionCalldata(); + TimelockController timelock = TimelockController(payable(this._timelock())); + timelock.execute( + this._executorMultisig(), + 0 /* value */, + executorMultisigCalldata, + 0 /* predecessor */, + bytes32(0) /* salt */ + ); + } + + function testDeploy() public override { + // save the previous implementation address to assert its change later + address prevRewardsCoordinator = zDeployedImpl(type(RewardsCoordinator).name); + + // 0. Deploy the Implementation contract. + runAsEOA(); + + // 1. run the queue script. + vm.startPrank(this._operationsMultisig()); + super._runAsMultisig(); + vm.stopPrank(); + + RewardsCoordinator rewardsCoordinatorProxy = RewardsCoordinator(zDeployedProxy(type(RewardsCoordinator).name)); + uint256 pausedStatusBefore = rewardsCoordinatorProxy.paused(); + TimelockController timelock = this._timelock(); + + // 2. run the execute script above. + bytes memory multisigTxnData = _getMultisigTransactionCalldata(); + bytes32 txHash = timelock.hashOperation(this._executorMultisig(), 0, multisigTxnData, 0, 0); + + assertEq(timelock.isOperationPending(txHash), true, "Transaction should be queued and pending."); + vm.warp(block.timestamp + timelock.getMinDelay()); // 1 tick after ETA. + + assertEq(timelock.isOperationReady(txHash), true, "Transaction should be executable."); + + zSetMultisigContext(this._protocolCouncilMultisig()); + execute(); + + // 3. assert that the execute did something + assertEq(timelock.isOperationDone(txHash), true, "Transaction should be executed."); + + // assert that the proxy implementation was updated. + ProxyAdmin admin = ProxyAdmin(this._proxyAdmin()); + address rewardsCoordinatorImpl = admin.getProxyImplementation( + TransparentUpgradeableProxy(payable(zDeployedProxy(type(RewardsCoordinator).name))) + ); + assertEq(rewardsCoordinatorImpl, zDeployedImpl(type(RewardsCoordinator).name)); + assertNotEq(prevRewardsCoordinator, rewardsCoordinatorImpl, "expected rewardsCoordinatorImpl to be different"); + + uint256 pausedStatusAfter = rewardsCoordinatorProxy.paused(); + address owner = this._operationsMultisig(); + IPauserRegistry pauserRegistry = IPauserRegistry(this._pauserRegistry()); + uint64 initPausedStatus = zUint64("REWARDS_COORDINATOR_INIT_PAUSED_STATUS"); + address rewardsUpdater = zAddress("REWARDS_COORDINATOR_UPDATER"); + uint32 activationDelay = zUint32("REWARDS_COORDINATOR_ACTIVATION_DELAY"); + uint16 defaultOperatorSplitBips = zUint16("REWARDS_COORDINATOR_DEFAULT_OPERATOR_SPLIT_BIPS"); + + // Ensure that the proxy contract cannot be re-initialized. + vm.expectRevert("Initializable: contract is already initialized"); + rewardsCoordinatorProxy.initialize( + owner, + pauserRegistry, + initPausedStatus, + rewardsUpdater, + activationDelay, + defaultOperatorSplitBips + ); + + // Assert Immutables and State Variables set through initialize + assertEq(rewardsCoordinatorProxy.owner(), owner, "expected owner"); + assertEq(address(rewardsCoordinatorProxy.pauserRegistry()), address(pauserRegistry), "expected pauserRegistry"); + assertEq(address(rewardsCoordinatorProxy.rewardsUpdater()), rewardsUpdater, "expected rewardsUpdater"); + assertEq(rewardsCoordinatorProxy.activationDelay(), activationDelay, "expected activationDelay"); + assertEq( + rewardsCoordinatorProxy.defaultOperatorSplitBips(), + defaultOperatorSplitBips, + "expected defaultOperatorSplitBips" + ); + assertEq( + pausedStatusBefore, + pausedStatusAfter, + "expected paused status to be the same before and after initialization" + ); + assertEq( + address(rewardsCoordinatorProxy.delegationManager()), + zDeployedProxy(type(DelegationManager).name), + "expected delegationManager" + ); + assertEq( + address(rewardsCoordinatorProxy.strategyManager()), + zDeployedProxy(type(StrategyManager).name), + "expected strategyManager" + ); + + assertEq( + rewardsCoordinatorProxy.CALCULATION_INTERVAL_SECONDS(), + zUint32("REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS"), + "expected REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS" + ); + assertGt( + rewardsCoordinatorProxy.CALCULATION_INTERVAL_SECONDS(), + 0, + "expected non-zero REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS" + ); + + assertEq(rewardsCoordinatorProxy.MAX_REWARDS_DURATION(), zUint32("REWARDS_COORDINATOR_MAX_REWARDS_DURATION")); + assertGt(rewardsCoordinatorProxy.MAX_REWARDS_DURATION(), 0); + + assertEq( + rewardsCoordinatorProxy.MAX_RETROACTIVE_LENGTH(), + zUint32("REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH") + ); + assertGt(rewardsCoordinatorProxy.MAX_RETROACTIVE_LENGTH(), 0); + + assertEq(rewardsCoordinatorProxy.MAX_FUTURE_LENGTH(), zUint32("REWARDS_COORDINATOR_MAX_FUTURE_LENGTH")); + assertGt(rewardsCoordinatorProxy.MAX_FUTURE_LENGTH(), 0); + + assertEq( + rewardsCoordinatorProxy.GENESIS_REWARDS_TIMESTAMP(), + zUint32("REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP") + ); + assertGt(rewardsCoordinatorProxy.GENESIS_REWARDS_TIMESTAMP(), 0); + } +} diff --git a/script/releases/v0.5.1-rewardsv2/upgrade.json b/script/releases/v0.5.1-rewardsv2/upgrade.json new file mode 100644 index 000000000..988515c70 --- /dev/null +++ b/script/releases/v0.5.1-rewardsv2/upgrade.json @@ -0,0 +1,19 @@ +{ + "name": "rewards-v2", + "from": "~0.0.0", + "to": "0.5.1", + "phases": [ + { + "type": "eoa", + "filename": "1-eoa.s.sol" + }, + { + "type": "multisig", + "filename": "2-multisig.s.sol" + }, + { + "type": "multisig", + "filename": "3-multisig.s.sol" + } + ] +}