diff --git a/.env.example b/.env.example index 8985dacc60..948f166c8b 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ RPC_MAINNET="https://eth.llamarpc.com" # RPC_MAINNET="https://mainnet.infura.io/v3/API-KEY" RPC_GOERLI="https://ethereum-goerli.publicnode.com" +RPC_HOLESKY="" ETHERSCAN_API_KEY="API-KEY" diff --git a/certora/specs/pods/EigenPodManager.spec b/certora/specs/pods/EigenPodManager.spec index 8d405c5fb5..37bdc41827 100644 --- a/certora/specs/pods/EigenPodManager.spec +++ b/certora/specs/pods/EigenPodManager.spec @@ -46,7 +46,6 @@ methods { function slasher() external returns (address) envfree; function hasPod(address podOwner) external returns (bool) envfree; function numPods() external returns (uint256) envfree; - function maxPods() external returns (uint256) envfree; function podOwnerShares(address podOwner) external returns (int256) envfree; function beaconChainETHStrategy() external returns (address) envfree; diff --git a/docs/core/EigenPodManager.md b/docs/core/EigenPodManager.md index f770782deb..24dabe1f5f 100644 --- a/docs/core/EigenPodManager.md +++ b/docs/core/EigenPodManager.md @@ -527,24 +527,9 @@ This method loops over up to `maxNumberOfDelayedWithdrawalsToClaim` withdrawals, ### System Configuration -* [`EigenPodManager.setMaxPods`](#eigenpodmanagersetmaxpods) * [`EigenPodManager.updateBeaconChainOracle`](#eigenpodmanagerupdatebeaconchainoracle) * [`DelayedWithdrawalRouter.setWithdrawalDelayBlocks`](#delayedwithdrawalroutersetwithdrawaldelayblocks) -#### `EigenPodManager.setMaxPods` - -```solidity -function setMaxPods(uint256 newMaxPods) external onlyUnpauser -``` - -Allows the unpauser to update the maximum number of `EigenPods` that the `EigenPodManager` can create. - -*Effects*: -* Updates `EigenPodManager.maxPods` - -*Requirements*: -* Caller MUST be the unpauser - #### `EigenPodManager.updateBeaconChainOracle` ```solidity diff --git a/script/configs/holesky/M2_deploy_from_scratch.holesky.config.json b/script/configs/holesky/M2_deploy_from_scratch.holesky.config.json new file mode 100644 index 0000000000..d143bf043c --- /dev/null +++ b/script/configs/holesky/M2_deploy_from_scratch.holesky.config.json @@ -0,0 +1,58 @@ +{ + "chainInfo": { + "chainId": 17000 + }, + "multisig_addresses": { + "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", + "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", + "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + }, + "numStrategies": 2, + "strategies": { + "numStrategies": 2, + "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "strategiesToDeploy": [ + { + "token_address": "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "token_name": "Liquid staked Ether 2.0", + "token_symbol": "stETH" + }, + { + "token_address": "0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1", + "token_name": "Rocket Pool ETH", + "token_symbol": "rETH" + } + ] + }, + "strategyManager": { + "init_strategy_whitelister": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "init_paused_status": 0 + }, + "delegationManager": { + "init_paused_status": 0, + "init_minWithdrawalDelayBlocks": 50400 + }, + "avsDirectory": { + "init_paused_status": 0 + }, + "slasher": { + "init_paused_status": 0 + }, + "eigenPod": { + "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000000000000, + "GENESIS_TIME": 1695902400 + }, + "eigenPodManager": { + "init_paused_status": 0, + "deneb_fork_timestamp": "1707305664" + }, + "delayedWithdrawalRouter": { + "init_paused_status": 0, + "init_withdrawalDelayBlocks": 50400 + }, + "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242", + "beaconOracleAddress": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25" +} \ No newline at end of file diff --git a/script/configs/holesky/M2_deploy_preprod.holesky.config.json b/script/configs/holesky/M2_deploy_preprod.holesky.config.json new file mode 100644 index 0000000000..469ef613ec --- /dev/null +++ b/script/configs/holesky/M2_deploy_preprod.holesky.config.json @@ -0,0 +1,58 @@ +{ + "chainInfo": { + "chainId": 17000 + }, + "multisig_addresses": { + "pauserMultisig": "0x0000000000000000000000000000000000000000", + "communityMultisig": "0x0000000000000000000000000000000000000000", + "operationsMultisig": "0x0000000000000000000000000000000000000000", + "executorMultisig": "0x0000000000000000000000000000000000000000", + "timelock": "0x0000000000000000000000000000000000000000" + }, + "numStrategies": 2, + "strategies": { + "numStrategies": 2, + "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "strategiesToDeploy": [ + { + "token_address": "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "token_name": "Liquid staked Ether 2.0", + "token_symbol": "stETH" + }, + { + "token_address": "0x7322c24752f79c05FFD1E2a6FCB97020C1C264F1", + "token_name": "Rocket Pool ETH", + "token_symbol": "rETH" + } + ] + }, + "strategyManager": { + "init_strategy_whitelister": "0x0000000000000000000000000000000000000000", + "init_paused_status": 0 + }, + "delegationManager": { + "init_paused_status": 0, + "init_minWithdrawalDelayBlocks": 50400 + }, + "avsDirectory": { + "init_paused_status": 0 + }, + "slasher": { + "init_paused_status": 0 + }, + "eigenPod": { + "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000000000000, + "GENESIS_TIME": 1695902400 + }, + "eigenPodManager": { + "init_paused_status": 0, + "deneb_fork_timestamp": "1707305664" + }, + "delayedWithdrawalRouter": { + "init_paused_status": 0, + "init_withdrawalDelayBlocks": 50400 + }, + "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242", + "beaconOracleAddress": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25" +} \ No newline at end of file diff --git a/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol b/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol new file mode 100644 index 0000000000..0105406fc8 --- /dev/null +++ b/script/deploy/holesky/M2_Deploy_From_Scratch.s.sol @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "../../utils/ExistingDeploymentParser.sol"; + +/** + * @notice Script used for the first deployment of EigenLayer core contracts to Holesky + * forge script script/deploy/holesky/M2_Deploy_From_Scratch.s.sol --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv + * forge script script/deploy/holesky/M2_Deploy_From_Scratch.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv + * + */ +contract M2_Deploy_Holesky_From_Scratch is ExistingDeploymentParser { + function run() external virtual { + _parseInitialDeploymentParams("script/configs/holesky/M2_deploy_from_scratch.holesky.config.json"); + + // START RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.startBroadcast(); + + emit log_named_address("Deployer Address", msg.sender); + + _deployFromScratch(); + + // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.stopBroadcast(); + + // Sanity Checks + _verifyContractPointers(); + _verifyImplementations(); + _verifyContractsInitialized({isInitialDeployment: true}); + _verifyInitializationParams(); + + logAndOutputContractAddresses("script/output/holesky/M2_deploy_from_scratch.holesky.config.json"); + } + + /** + * @notice Deploy EigenLayer contracts from scratch for Holesky + */ + function _deployFromScratch() internal { + // Deploy ProxyAdmin, later set admins for all proxies to be executorMultisig + eigenLayerProxyAdmin = new ProxyAdmin(); + + // Set multisigs as pausers, executorMultisig as unpauser + address[] memory pausers = new address[](3); + pausers[0] = executorMultisig; + pausers[1] = operationsMultisig; + pausers[2] = pauserMultisig; + address unpauser = executorMultisig; + eigenLayerPauserReg = new PauserRegistry(pausers, unpauser); + + /** + * First, deploy upgradeable proxy contracts that **will point** to the implementations. Since the implementation contracts are + * not yet deployed, we give these proxies an empty contract as the initial implementation, to act as if they have no code. + */ + emptyContract = new EmptyContract(); + avsDirectory = AVSDirectory( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + delegationManager = DelegationManager( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + strategyManager = StrategyManager( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + slasher = Slasher( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + eigenPodManager = EigenPodManager( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + delayedWithdrawalRouter = DelayedWithdrawalRouter( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + + // Deploy EigenPod Contracts + eigenPodImplementation = new EigenPod( + IETHPOSDeposit(ETHPOSDepositAddress), + delayedWithdrawalRouter, + eigenPodManager, + EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, + EIGENPOD_GENESIS_TIME + ); + + eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation)); + avsDirectoryImplementation = new AVSDirectory(delegationManager); + delegationManagerImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); + strategyManagerImplementation = new StrategyManager(delegationManager, eigenPodManager, slasher); + slasherImplementation = new Slasher(strategyManager, delegationManager); + eigenPodManagerImplementation = new EigenPodManager( + IETHPOSDeposit(ETHPOSDepositAddress), + eigenPodBeacon, + strategyManager, + slasher, + delegationManager + ); + delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager); + + // Third, upgrade the proxy contracts to point to the implementations + IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); + uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); + // AVSDirectory + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryImplementation), + abi.encodeWithSelector( + AVSDirectory.initialize.selector, + executorMultisig, // initialOwner + eigenLayerPauserReg, + AVS_DIRECTORY_INIT_PAUSED_STATUS + ) + ); + // DelegationManager + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(delegationManager))), + address(delegationManagerImplementation), + abi.encodeWithSelector( + DelegationManager.initialize.selector, + executorMultisig, // initialOwner + eigenLayerPauserReg, + DELEGATION_MANAGER_INIT_PAUSED_STATUS, + DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS, + initializeStrategiesToSetDelayBlocks, + initializeWithdrawalDelayBlocks + ) + ); + // StrategyManager + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(strategyManager))), + address(strategyManagerImplementation), + abi.encodeWithSelector( + StrategyManager.initialize.selector, + executorMultisig, //initialOwner + STRATEGY_MANAGER_WHITELISTER, //initial whitelister + eigenLayerPauserReg, + STRATEGY_MANAGER_INIT_PAUSED_STATUS + ) + ); + // Slasher + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(slasher))), + address(slasherImplementation), + abi.encodeWithSelector( + Slasher.initialize.selector, + executorMultisig, + eigenLayerPauserReg, + SLASHER_INIT_PAUSED_STATUS + ) + ); + // EigenPodManager + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenPodManager))), + address(eigenPodManagerImplementation), + abi.encodeWithSelector( + EigenPodManager.initialize.selector, + beaconOracle, + msg.sender, // initialOwner is msg.sender for now to set forktimestamp later + eigenLayerPauserReg, + EIGENPOD_MANAGER_INIT_PAUSED_STATUS + ) + ); + // Delayed Withdrawal Router + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), + address(delayedWithdrawalRouterImplementation), + abi.encodeWithSelector( + DelayedWithdrawalRouter.initialize.selector, + executorMultisig, // initialOwner + eigenLayerPauserReg, + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS + ) + ); + + // Deploy Strategies + baseStrategyImplementation = new StrategyBaseTVLLimits(strategyManager); + uint256 numStrategiesToDeploy = strategiesToDeploy.length; + for (uint256 i = 0; i < numStrategiesToDeploy; i++) { + StrategyUnderlyingTokenConfig memory strategyConfig = strategiesToDeploy[i]; + + // Deploy and upgrade strategy + StrategyBaseTVLLimits strategy = StrategyBaseTVLLimits( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(strategy))), + address(baseStrategyImplementation), + abi.encodeWithSelector( + StrategyBaseTVLLimits.initialize.selector, + STRATEGY_MAX_PER_DEPOSIT, + STRATEGY_MAX_TOTAL_DEPOSITS, + IERC20(strategyConfig.tokenAddress), + eigenLayerPauserReg + ) + ); + + deployedStrategyArray.push(strategy); + } + + // Fork timestamp config + eigenPodManager.setDenebForkTimestamp(EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP); + + // Transfer ownership + eigenLayerProxyAdmin.transferOwnership(executorMultisig); + eigenPodManager.transferOwnership(executorMultisig); + eigenPodBeacon.transferOwnership(executorMultisig); + } +} diff --git a/script/deploy/holesky/M2_Deploy_Preprod.s.sol b/script/deploy/holesky/M2_Deploy_Preprod.s.sol new file mode 100644 index 0000000000..be27f3809a --- /dev/null +++ b/script/deploy/holesky/M2_Deploy_Preprod.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "./M2_Deploy_From_Scratch.s.sol"; + +/** + * @notice Script used for the first deployment of EigenLayer core contracts to Holesky + * forge script script/deploy/holesky/M2_Deploy_Preprod.s.sol --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv + * forge script script/deploy/holesky/M2_Deploy_Preprod.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv + * + * Script for dev environment, exact same as M2_Deploy_From_Scratch.s.sol but with an EOAowner + * instead of multisig addresses for permissions. + * Unused config fields: + * - init_strategy_whitelister + * - multisig_addresses(operations, pauser, executor, community) + */ +contract M2_Deploy_Holesky_Preprod is M2_Deploy_Holesky_From_Scratch { + /// @dev EOAowner is the deployer and owner of the contracts + address EOAowner; + + function run() external virtual override { + _parseInitialDeploymentParams("script/configs/holesky/M2_deploy_preprod.holesky.config.json"); + + // Overwrite multisig to be EOAowner + EOAowner = msg.sender; + executorMultisig = EOAowner; + operationsMultisig = EOAowner; + pauserMultisig = EOAowner; + communityMultisig = EOAowner; + STRATEGY_MANAGER_WHITELISTER = EOAowner; + + // START RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.startBroadcast(); + + emit log_named_address("Deployer and EOAowner Address", EOAowner); + + _deployFromScratch(); + + // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.stopBroadcast(); + + // Sanity Checks + _verifyContractPointers(); + _verifyImplementations(); + _verifyContractsInitialized({isInitialDeployment: true}); + _verifyInitializationParams(); // override to check contract.owner() is EOAowner instead + + logAndOutputContractAddresses("script/output/holesky/M2_deploy_preprod.holesky.config.json"); + } +} diff --git a/script/deploy/mainnet/M2Deploy.s.sol b/script/deploy/mainnet/M2Deploy.s.sol index d93455e60e..a39eeb5449 100644 --- a/script/deploy/mainnet/M2Deploy.s.sol +++ b/script/deploy/mainnet/M2Deploy.s.sol @@ -62,7 +62,6 @@ contract M2Deploy is Script, Test { uint256 public withdrawalDelayBlocks; bytes32 public delegationManagerDomainSeparator; uint256 public numPods; - uint256 public maxPods; // Pointers to pre-upgrade values for lstDepositor address public lstDepositor; @@ -122,7 +121,6 @@ contract M2Deploy is Script, Test { withdrawalDelayBlocks = m1StrategyManager(address(strategyManager)).withdrawalDelayBlocks(); delegationManagerDomainSeparator = IDelegationManagerV0(address(delegation)).DOMAIN_SEPARATOR(); numPods = eigenPodManager.numPods(); - maxPods = eigenPodManager.maxPods(); delayedWithdrawalRouter = EigenPod(payable(eigenPodBeacon.implementation())).delayedWithdrawalRouter(); // Set chain-specific values @@ -277,7 +275,7 @@ contract M2Deploy is Script, Test { // Call contracts to ensure that all simple view functions return the same values (e.g. the return value of `StrategyManager.delegation()` hasn’t changed) // StrategyManager: delegation, eigenPodManager, slasher, strategyWhitelister, withdrawalDelayBlocks all unchanged // DelegationManager: DOMAIN_SEPARATOR, strategyManager, slasher, eigenPodManager all unchanged - // EigenPodManager: ethPOS, eigenPodBeacon, strategyManager, slasher, beaconChainOracle, numPods, maxPods all unchanged + // EigenPodManager: ethPOS, eigenPodBeacon, strategyManager, slasher, beaconChainOracle, numPods all unchanged // delegationManager is now correct (added immutable) // Call contracts to make sure they are still “initialized” (ensure that trying to call initializer reverts) function _verifyStorageSlots() internal view { @@ -311,7 +309,6 @@ contract M2Deploy is Script, Test { "eigenPodManager.beaconChainOracle incorrect" ); require(eigenPodManager.numPods() == numPods, "eigenPodManager.numPods incorrect"); - require(eigenPodManager.maxPods() == maxPods, "eigenPodManager.maxPods incorrect"); require(EigenPodManagerStorage(address(eigenPodManager)).delegationManager() == delegation, "eigenPodManager.delegationManager incorrect"); } @@ -339,7 +336,6 @@ contract M2Deploy is Script, Test { cheats.expectRevert(bytes("Initializable: contract is already initialized")); EigenPodManager(address(eigenPodManager)).initialize( - 0, IBeaconChainOracle(address(this)), address(this), PauserRegistry(address(this)), diff --git a/script/utils/ExistingDeploymentParser.sol b/script/utils/ExistingDeploymentParser.sol index 8d2544b265..0338687189 100644 --- a/script/utils/ExistingDeploymentParser.sol +++ b/script/utils/ExistingDeploymentParser.sol @@ -11,6 +11,7 @@ import "../../src/contracts/core/DelegationManager.sol"; import "../../src/contracts/core/AVSDirectory.sol"; import "../../src/contracts/strategies/StrategyBase.sol"; +import "../../src/contracts/strategies/StrategyBaseTVLLimits.sol"; import "../../src/contracts/pods/EigenPod.sol"; import "../../src/contracts/pods/EigenPodManager.sol"; @@ -23,8 +24,13 @@ import "../../src/test/mocks/EmptyContract.sol"; import "forge-std/Script.sol"; import "forge-std/Test.sol"; -contract ExistingDeploymentParser is Script, Test { +struct StrategyUnderlyingTokenConfig { + address tokenAddress; + string tokenName; + string tokenSymbol; +} +contract ExistingDeploymentParser is Script, Test { // EigenLayer Contracts ProxyAdmin public eigenLayerProxyAdmin; PauserRegistry public eigenLayerPauserReg; @@ -32,14 +38,15 @@ contract ExistingDeploymentParser is Script, Test { Slasher public slasherImplementation; AVSDirectory public avsDirectory; AVSDirectory public avsDirectoryImplementation; - DelegationManager public delegation; - DelegationManager public delegationImplementation; + DelegationManager public delegationManager; + DelegationManager public delegationManagerImplementation; StrategyManager public strategyManager; StrategyManager public strategyManagerImplementation; EigenPodManager public eigenPodManager; EigenPodManager public eigenPodManagerImplementation; DelayedWithdrawalRouter public delayedWithdrawalRouter; DelayedWithdrawalRouter public delayedWithdrawalRouterImplementation; + IBeaconChainOracle beaconOracle; UpgradeableBeacon public eigenPodBeacon; EigenPod public eigenPodImplementation; StrategyBase public baseStrategyImplementation; @@ -48,11 +55,51 @@ contract ExistingDeploymentParser is Script, Test { address executorMultisig; address operationsMultisig; + address communityMultisig; + address pauserMultisig; // strategies deployed StrategyBase[] public deployedStrategyArray; + // Strategies to Deploy + uint256 numStrategiesToDeploy; + StrategyUnderlyingTokenConfig[] public strategiesToDeploy; + + // the ETH2 deposit contract -- if not on mainnet, we deploy a mock as stand-in + // IETHPOSDeposit public ethPOSDeposit; + + // // IMMUTABLES TO SET + // uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR; + // uint64 GOERLI_GENESIS_TIME = 1616508000; + + /// @notice Initialization Params for first initial deployment scripts + // StrategyManager + uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS; + address STRATEGY_MANAGER_WHITELISTER; + // SLasher + uint256 SLASHER_INIT_PAUSED_STATUS; + // DelegationManager + uint256 DELEGATION_MANAGER_INIT_PAUSED_STATUS; + uint256 DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS; + // AVSDirectory + uint256 AVS_DIRECTORY_INIT_PAUSED_STATUS; + // EigenPodManager + uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS; + uint64 EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP; + // EigenPod + uint64 EIGENPOD_GENESIS_TIME; + uint64 EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR; + address ETHPOSDepositAddress; + uint256 DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS; + + // one week in blocks -- 50400 + uint32 DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS; + + // Strategy Deployment + uint256 STRATEGY_MAX_PER_DEPOSIT; + uint256 STRATEGY_MAX_TOTAL_DEPOSITS; - function _parseDeployedContracts(string memory existingDeploymentInfoPath) internal { + /// @notice use for parsing already deployed EigenLayer contracts + function _parseDeployedContracts(string memory existingDeploymentInfoPath) internal virtual { // read and log the chainID uint256 currentChainId = block.chainid; emit log_named_uint("You are parsing on ChainID", currentChainId); @@ -67,25 +114,51 @@ contract ExistingDeploymentParser is Script, Test { // read all of the deployed addresses executorMultisig = stdJson.readAddress(existingDeploymentData, ".parameters.executorMultisig"); operationsMultisig = stdJson.readAddress(existingDeploymentData, ".parameters.operationsMultisig"); - - eigenLayerProxyAdmin = ProxyAdmin(stdJson.readAddress(existingDeploymentData, ".addresses.eigenLayerProxyAdmin")); - eigenLayerPauserReg = PauserRegistry(stdJson.readAddress(existingDeploymentData, ".addresses.eigenLayerPauserReg")); + communityMultisig = stdJson.readAddress(existingDeploymentData, ".parameters.communityMultisig"); + pauserMultisig = stdJson.readAddress(existingDeploymentData, ".parameters.pauserMultisig"); + + eigenLayerProxyAdmin = ProxyAdmin( + stdJson.readAddress(existingDeploymentData, ".addresses.eigenLayerProxyAdmin") + ); + eigenLayerPauserReg = PauserRegistry( + stdJson.readAddress(existingDeploymentData, ".addresses.eigenLayerPauserReg") + ); slasher = Slasher(stdJson.readAddress(existingDeploymentData, ".addresses.slasher")); - slasherImplementation = Slasher(stdJson.readAddress(existingDeploymentData, ".addresses.slasherImplementation")); - delegation = DelegationManager(stdJson.readAddress(existingDeploymentData, ".addresses.delegation")); - delegationImplementation = DelegationManager(stdJson.readAddress(existingDeploymentData, ".addresses.delegationImplementation")); + slasherImplementation = Slasher( + stdJson.readAddress(existingDeploymentData, ".addresses.slasherImplementation") + ); + delegationManager = DelegationManager(stdJson.readAddress(existingDeploymentData, ".addresses.delegation")); + delegationManagerImplementation = DelegationManager( + stdJson.readAddress(existingDeploymentData, ".addresses.delegationImplementation") + ); avsDirectory = AVSDirectory(stdJson.readAddress(existingDeploymentData, ".addresses.avsDirectory")); - avsDirectoryImplementation = AVSDirectory(stdJson.readAddress(existingDeploymentData, ".addresses.avsDirectoryImplementation")); + avsDirectoryImplementation = AVSDirectory( + stdJson.readAddress(existingDeploymentData, ".addresses.avsDirectoryImplementation") + ); strategyManager = StrategyManager(stdJson.readAddress(existingDeploymentData, ".addresses.strategyManager")); - strategyManagerImplementation = StrategyManager(stdJson.readAddress(existingDeploymentData, ".addresses.strategyManagerImplementation")); + strategyManagerImplementation = StrategyManager( + stdJson.readAddress(existingDeploymentData, ".addresses.strategyManagerImplementation") + ); eigenPodManager = EigenPodManager(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodManager")); - eigenPodManagerImplementation = EigenPodManager(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodManagerImplementation")); - delayedWithdrawalRouter = DelayedWithdrawalRouter(stdJson.readAddress(existingDeploymentData, ".addresses.delayedWithdrawalRouter")); - delayedWithdrawalRouterImplementation = - DelayedWithdrawalRouter(stdJson.readAddress(existingDeploymentData, ".addresses.delayedWithdrawalRouterImplementation")); + eigenPodManagerImplementation = EigenPodManager( + stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodManagerImplementation") + ); + delayedWithdrawalRouter = DelayedWithdrawalRouter( + stdJson.readAddress(existingDeploymentData, ".addresses.delayedWithdrawalRouter") + ); + delayedWithdrawalRouterImplementation = DelayedWithdrawalRouter( + stdJson.readAddress(existingDeploymentData, ".addresses.delayedWithdrawalRouterImplementation") + ); + beaconOracle = IBeaconChainOracle( + stdJson.readAddress(existingDeploymentData, ".addresses.beaconOracleAddress") + ); eigenPodBeacon = UpgradeableBeacon(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodBeacon")); - eigenPodImplementation = EigenPod(payable(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodImplementation"))); - baseStrategyImplementation = StrategyBase(stdJson.readAddress(existingDeploymentData, ".addresses.baseStrategyImplementation")); + eigenPodImplementation = EigenPod( + payable(stdJson.readAddress(existingDeploymentData, ".addresses.eigenPodImplementation")) + ); + baseStrategyImplementation = StrategyBase( + stdJson.readAddress(existingDeploymentData, ".addresses.baseStrategyImplementation") + ); emptyContract = EmptyContract(stdJson.readAddress(existingDeploymentData, ".addresses.emptyContract")); /* @@ -103,4 +176,463 @@ contract ExistingDeploymentParser is Script, Test { } */ } + + /// @notice use for deploying a new set of EigenLayer contracts + /// Note that this does require multisigs to already be deployed + function _parseInitialDeploymentParams(string memory initialDeploymentParamsPath) internal virtual { + // read and log the chainID + uint256 currentChainId = block.chainid; + emit log_named_uint("You are parsing on ChainID", currentChainId); + + // READ JSON CONFIG DATA + string memory initialDeploymentData = vm.readFile(initialDeploymentParamsPath); + + // check that the chainID matches the one in the config + uint256 configChainId = stdJson.readUint(initialDeploymentData, ".chainInfo.chainId"); + require(configChainId == currentChainId, "You are on the wrong chain for this config"); + + // read beacon oracle + beaconOracle = IBeaconChainOracle(stdJson.readAddress(initialDeploymentData, ".beaconOracleAddress")); + + // read all of the deployed addresses + executorMultisig = stdJson.readAddress(initialDeploymentData, ".multisig_addresses.executorMultisig"); + operationsMultisig = stdJson.readAddress(initialDeploymentData, ".multisig_addresses.operationsMultisig"); + communityMultisig = stdJson.readAddress(initialDeploymentData, ".multisig_addresses.communityMultisig"); + pauserMultisig = stdJson.readAddress(initialDeploymentData, ".multisig_addresses.pauserMultisig"); + + // Strategies to Deploy, load strategy list + numStrategiesToDeploy = stdJson.readUint(initialDeploymentData, ".strategies.numStrategies"); + STRATEGY_MAX_PER_DEPOSIT = stdJson.readUint(initialDeploymentData, ".strategies.MAX_PER_DEPOSIT"); + STRATEGY_MAX_TOTAL_DEPOSITS = stdJson.readUint(initialDeploymentData, ".strategies.MAX_TOTAL_DEPOSITS"); + for (uint256 i = 0; i < numStrategiesToDeploy; ++i) { + // Form the key for the current element + string memory key = string.concat(".strategies.strategiesToDeploy[", vm.toString(i), "]"); + + // Use parseJson with the key to get the value for the current element + bytes memory tokenInfoBytes = stdJson.parseRaw(initialDeploymentData, key); + + // Decode the token information into the Token struct + StrategyUnderlyingTokenConfig memory tokenInfo = abi.decode( + tokenInfoBytes, + (StrategyUnderlyingTokenConfig) + ); + + strategiesToDeploy.push(tokenInfo); + } + + // Read initialize params for upgradeable contracts + STRATEGY_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint( + initialDeploymentData, + ".strategyManager.init_paused_status" + ); + STRATEGY_MANAGER_WHITELISTER = stdJson.readAddress(initialDeploymentData, ".strategyManager.init_strategy_whitelister"); + // Slasher + SLASHER_INIT_PAUSED_STATUS = stdJson.readUint(initialDeploymentData, ".slasher.init_paused_status"); + // DelegationManager + DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS = stdJson.readUint( + initialDeploymentData, + ".delegationManager.init_minWithdrawalDelayBlocks" + ); + DELEGATION_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint( + initialDeploymentData, + ".delegationManager.init_paused_status" + ); + // AVSDirectory + AVS_DIRECTORY_INIT_PAUSED_STATUS = stdJson.readUint(initialDeploymentData, ".avsDirectory.init_paused_status"); + // EigenPodManager + EIGENPOD_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint( + initialDeploymentData, + ".eigenPodManager.init_paused_status" + ); + EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP = uint64(stdJson.readUint( + initialDeploymentData, + ".eigenPodManager.deneb_fork_timestamp" + )); + + // EigenPod + EIGENPOD_GENESIS_TIME = uint64(stdJson.readUint(initialDeploymentData, ".eigenPod.GENESIS_TIME")); + EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = uint64( + stdJson.readUint(initialDeploymentData, ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR") + ); + ETHPOSDepositAddress = stdJson.readAddress(initialDeploymentData, ".ethPOSDepositAddress"); + // DelayedWithdrawalRouter + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = stdJson.readUint( + initialDeploymentData, + ".delayedWithdrawalRouter.init_paused_status" + ); + + // both set to one week in blocks 50400 + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( + stdJson.readUint(initialDeploymentData, ".delayedWithdrawalRouter.init_withdrawalDelayBlocks") + ); + + logInitialDeploymentParams(); + } + + /// @notice Ensure contracts point at each other correctly via constructors + function _verifyContractPointers() internal virtual view { + // AVSDirectory + require( + avsDirectory.delegation() == delegationManager, + "avsDirectory: delegationManager address not set correctly" + ); + // DelegationManager + require(delegationManager.slasher() == slasher, "delegationManager: slasher address not set correctly"); + require( + delegationManager.strategyManager() == strategyManager, + "delegationManager: strategyManager address not set correctly" + ); + require( + delegationManager.eigenPodManager() == eigenPodManager, + "delegationManager: eigenPodManager address not set correctly" + ); + // StrategyManager + require(strategyManager.slasher() == slasher, "strategyManager: slasher address not set correctly"); + require( + strategyManager.delegation() == delegationManager, + "strategyManager: delegationManager address not set correctly" + ); + require( + strategyManager.eigenPodManager() == eigenPodManager, + "strategyManager: eigenPodManager address not set correctly" + ); + // EPM + require( + address(eigenPodManager.ethPOS()) == ETHPOSDepositAddress, + "eigenPodManager: ethPOSDeposit contract address not set correctly" + ); + require( + eigenPodManager.eigenPodBeacon() == eigenPodBeacon, + "eigenPodManager: eigenPodBeacon contract address not set correctly" + ); + require( + eigenPodManager.strategyManager() == strategyManager, + "eigenPodManager: strategyManager contract address not set correctly" + ); + require(eigenPodManager.slasher() == slasher, "eigenPodManager: slasher contract address not set correctly"); + require( + eigenPodManager.delegationManager() == delegationManager, + "eigenPodManager: delegationManager contract address not set correctly" + ); + // DelayedWithdrawalRouter + require( + delayedWithdrawalRouter.eigenPodManager() == eigenPodManager, + "delayedWithdrawalRouterContract: eigenPodManager address not set correctly" + ); + } + + /// @notice verify implementations for Transparent Upgradeable Proxies + function _verifyImplementations() internal virtual view { + require( + eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(avsDirectory)))) == + address(avsDirectoryImplementation), + "avsDirectory: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(delegationManager))) + ) == address(delegationManagerImplementation), + "delegationManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(strategyManager))) + ) == address(strategyManagerImplementation), + "strategyManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(slasher)))) == + address(slasherImplementation), + "slasher: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(eigenPodManager))) + ) == address(eigenPodManagerImplementation), + "eigenPodManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))) + ) == address(delayedWithdrawalRouterImplementation), + "delayedWithdrawalRouter: implementation set incorrectly" + ); + + for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(deployedStrategyArray[i]))) + ) == address(baseStrategyImplementation), + "strategy: implementation set incorrectly" + ); + } + + require( + eigenPodBeacon.implementation() == address(eigenPodImplementation), + "eigenPodBeacon: implementation set incorrectly" + ); + } + + /** + * @notice Verify initialization of Transparent Upgradeable Proxies. Also check + * initialization params if this is the first deployment. + * @param isInitialDeployment True if this is the first deployment of contracts from scratch + */ + function _verifyContractsInitialized(bool isInitialDeployment) internal virtual{ + // AVSDirectory + vm.expectRevert(bytes("Initializable: contract is already initialized")); + avsDirectory.initialize(address(0), eigenLayerPauserReg, AVS_DIRECTORY_INIT_PAUSED_STATUS); + // DelegationManager + vm.expectRevert(bytes("Initializable: contract is already initialized")); + IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); + uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); + delegationManager.initialize( + address(0), + eigenLayerPauserReg, + 0, + 0, // minWithdrawalDelayBLocks + initializeStrategiesToSetDelayBlocks, + initializeWithdrawalDelayBlocks + ); + // StrategyManager + vm.expectRevert(bytes("Initializable: contract is already initialized")); + strategyManager.initialize(address(0), address(0), eigenLayerPauserReg, STRATEGY_MANAGER_INIT_PAUSED_STATUS); + // EigenPodManager + vm.expectRevert(bytes("Initializable: contract is already initialized")); + eigenPodManager.initialize( + beaconOracle, + address(0), + eigenLayerPauserReg, + EIGENPOD_MANAGER_INIT_PAUSED_STATUS + ); + // DelayedWithdrawalRouter + vm.expectRevert(bytes("Initializable: contract is already initialized")); + delayedWithdrawalRouter.initialize( + address(0), + eigenLayerPauserReg, + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS + ); + // Strategies + for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { + vm.expectRevert(bytes("Initializable: contract is already initialized")); + StrategyBaseTVLLimits(address(deployedStrategyArray[i])).initialize( + 0, + 0, + IERC20(address(0)), + eigenLayerPauserReg + ); + } + } + + /// @notice Verify params based on config constants that are updated from calling `_parseInitialDeploymentParams` + function _verifyInitializationParams() internal virtual view { + // AVSDirectory + require( + avsDirectory.pauserRegistry() == eigenLayerPauserReg, + "avsdirectory: pauser registry not set correctly" + ); + require(avsDirectory.owner() == executorMultisig, "avsdirectory: owner not set correctly"); + require( + avsDirectory.paused() == AVS_DIRECTORY_INIT_PAUSED_STATUS, + "avsdirectory: init paused status set incorrectly" + ); + // DelegationManager + require( + delegationManager.pauserRegistry() == eigenLayerPauserReg, + "delegationManager: pauser registry not set correctly" + ); + require(delegationManager.owner() == executorMultisig, "delegationManager: owner not set correctly"); + require( + delegationManager.paused() == DELEGATION_MANAGER_INIT_PAUSED_STATUS, + "delegationManager: init paused status set incorrectly" + ); + require( + delegationManager.minWithdrawalDelayBlocks() == DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS, + "delegationManager: minWithdrawalDelayBlocks not set correctly" + ); + // StrategyManager + require( + strategyManager.pauserRegistry() == eigenLayerPauserReg, + "strategyManager: pauser registry not set correctly" + ); + require(strategyManager.owner() == executorMultisig, "strategyManager: owner not set correctly"); + require( + strategyManager.paused() == STRATEGY_MANAGER_INIT_PAUSED_STATUS, + "strategyManager: init paused status set incorrectly" + ); + require( + strategyManager.strategyWhitelister() == operationsMultisig, + "strategyManager: strategyWhitelister not set correctly" + ); + // EigenPodManager + require( + eigenPodManager.pauserRegistry() == eigenLayerPauserReg, + "eigenPodManager: pauser registry not set correctly" + ); + require(eigenPodManager.owner() == executorMultisig, "eigenPodManager: owner not set correctly"); + require( + eigenPodManager.paused() == EIGENPOD_MANAGER_INIT_PAUSED_STATUS, + "eigenPodManager: init paused status set incorrectly" + ); + // EigenPodBeacon + require(eigenPodBeacon.owner() == executorMultisig, "eigenPodBeacon: owner not set correctly"); + // DelayedWithdrawalRouter + require( + delayedWithdrawalRouter.pauserRegistry() == eigenLayerPauserReg, + "delayedWithdrawalRouter: pauser registry not set correctly" + ); + require( + delayedWithdrawalRouter.owner() == executorMultisig, + "delayedWithdrawalRouter: owner not set correctly" + ); + require( + delayedWithdrawalRouter.paused() == DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, + "delayedWithdrawalRouter: init paused status set incorrectly" + ); + require( + delayedWithdrawalRouter.withdrawalDelayBlocks() == DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS, + "delayedWithdrawalRouter: withdrawalDelayBlocks not set correctly" + ); + // Strategies + for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { + require( + deployedStrategyArray[i].pauserRegistry() == eigenLayerPauserReg, + "StrategyBaseTVLLimits: pauser registry not set correctly" + ); + require( + deployedStrategyArray[i].paused() == 0, + "StrategyBaseTVLLimits: init paused status set incorrectly" + ); + } + + // Pausing Permissions + require(eigenLayerPauserReg.isPauser(operationsMultisig), "pauserRegistry: operationsMultisig is not pauser"); + require(eigenLayerPauserReg.isPauser(executorMultisig), "pauserRegistry: executorMultisig is not pauser"); + require(eigenLayerPauserReg.isPauser(pauserMultisig), "pauserRegistry: pauserMultisig is not pauser"); + require(eigenLayerPauserReg.unpauser() == executorMultisig, "pauserRegistry: unpauser not set correctly"); + } + + function logInitialDeploymentParams() public { + emit log_string("==== Parsed Initilize Params for Initial Deployment ===="); + + emit log_named_address("executorMultisig", executorMultisig); + emit log_named_address("operationsMultisig", operationsMultisig); + emit log_named_address("communityMultisig", communityMultisig); + emit log_named_address("pauserMultisig", pauserMultisig); + + emit log_named_uint("STRATEGY_MANAGER_INIT_PAUSED_STATUS", STRATEGY_MANAGER_INIT_PAUSED_STATUS); + emit log_named_address("STRATEGY_MANAGER_WHITELISTER", STRATEGY_MANAGER_WHITELISTER); + emit log_named_uint("SLASHER_INIT_PAUSED_STATUS", SLASHER_INIT_PAUSED_STATUS); + emit log_named_uint( + "DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS", + DELEGATION_MANAGER_MIN_WITHDRAWAL_DELAY_BLOCKS + ); + emit log_named_uint("DELEGATION_MANAGER_INIT_PAUSED_STATUS", DELEGATION_MANAGER_INIT_PAUSED_STATUS); + emit log_named_uint("AVS_DIRECTORY_INIT_PAUSED_STATUS", AVS_DIRECTORY_INIT_PAUSED_STATUS); + emit log_named_uint("EIGENPOD_MANAGER_INIT_PAUSED_STATUS", EIGENPOD_MANAGER_INIT_PAUSED_STATUS); + emit log_named_uint("EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP", EIGENPOD_MANAGER_DENEB_FORK_TIMESTAMP); + emit log_named_uint("EIGENPOD_GENESIS_TIME", EIGENPOD_GENESIS_TIME); + emit log_named_uint( + "EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR", + EIGENPOD_MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR + ); + emit log_named_address("ETHPOSDepositAddress", ETHPOSDepositAddress); + emit log_named_uint( + "DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS", + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS + ); + emit log_named_uint( + "DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS", + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS + ); + + emit log_string("==== Strategies to Deploy ===="); + for (uint256 i = 0; i < numStrategiesToDeploy; ++i) { + // Decode the token information into the Token struct + StrategyUnderlyingTokenConfig memory tokenInfo = strategiesToDeploy[i]; + + strategiesToDeploy.push(tokenInfo); + emit log_named_address("TOKEN ADDRESS", tokenInfo.tokenAddress); + emit log_named_string("TOKEN NAME", tokenInfo.tokenName); + emit log_named_string("TOKEN SYMBOL", tokenInfo.tokenSymbol); + } + } + + /** + * @notice Log contract addresses and write to output json file + */ + function logAndOutputContractAddresses(string memory outputPath) public { + // WRITE JSON DATA + string memory parent_object = "parent object"; + + string memory deployed_strategies = "strategies"; + for (uint256 i = 0; i < numStrategiesToDeploy; ++i) { + vm.serializeAddress(deployed_strategies, strategiesToDeploy[i].tokenSymbol, address(deployedStrategyArray[i])); + } + string memory deployed_strategies_output = numStrategiesToDeploy == 0 + ? "" + : vm.serializeAddress( + deployed_strategies, + strategiesToDeploy[numStrategiesToDeploy - 1].tokenSymbol, + address(deployedStrategyArray[numStrategiesToDeploy - 1]) + ); + + string memory deployed_addresses = "addresses"; + vm.serializeAddress(deployed_addresses, "eigenLayerProxyAdmin", address(eigenLayerProxyAdmin)); + vm.serializeAddress(deployed_addresses, "eigenLayerPauserReg", address(eigenLayerPauserReg)); + vm.serializeAddress(deployed_addresses, "slasher", address(slasher)); + vm.serializeAddress(deployed_addresses, "slasherImplementation", address(slasherImplementation)); + vm.serializeAddress(deployed_addresses, "avsDirectory", address(avsDirectory)); + vm.serializeAddress(deployed_addresses, "avsDirectoryImplementation", address(avsDirectoryImplementation)); + vm.serializeAddress(deployed_addresses, "delegationManager", address(delegationManager)); + vm.serializeAddress(deployed_addresses, "delegationManagerImplementation", address(delegationManagerImplementation)); + vm.serializeAddress(deployed_addresses, "strategyManager", address(strategyManager)); + vm.serializeAddress( + deployed_addresses, + "strategyManagerImplementation", + address(strategyManagerImplementation) + ); + vm.serializeAddress(deployed_addresses, "eigenPodManager", address(eigenPodManager)); + vm.serializeAddress( + deployed_addresses, + "eigenPodManagerImplementation", + address(eigenPodManagerImplementation) + ); + vm.serializeAddress(deployed_addresses, "delayedWithdrawalRouter", address(delayedWithdrawalRouter)); + vm.serializeAddress( + deployed_addresses, + "delayedWithdrawalRouterImplementation", + address(delayedWithdrawalRouterImplementation) + ); + vm.serializeAddress(deployed_addresses, "beaconOracle", address(beaconOracle)); + vm.serializeAddress(deployed_addresses, "eigenPodBeacon", address(eigenPodBeacon)); + vm.serializeAddress(deployed_addresses, "eigenPodImplementation", address(eigenPodImplementation)); + vm.serializeAddress(deployed_addresses, "baseStrategyImplementation", address(baseStrategyImplementation)); + vm.serializeAddress(deployed_addresses, "emptyContract", address(emptyContract)); + string memory deployed_addresses_output = vm.serializeString( + deployed_addresses, + "strategies", + deployed_strategies_output + ); + + string memory parameters = "parameters"; + vm.serializeAddress(parameters, "executorMultisig", executorMultisig); + vm.serializeAddress(parameters, "operationsMultisig", operationsMultisig); + vm.serializeAddress(parameters, "communityMultisig", communityMultisig); + vm.serializeAddress(parameters, "pauserMultisig", pauserMultisig); + string memory parameters_output = vm.serializeAddress(parameters, "operationsMultisig", operationsMultisig); + + string memory chain_info = "chainInfo"; + vm.serializeUint(chain_info, "deploymentBlock", block.number); + string memory chain_info_output = vm.serializeUint(chain_info, "chainId", block.chainid); + + // serialize all the data + vm.serializeString(parent_object, deployed_addresses, deployed_addresses_output); + vm.serializeString(parent_object, chain_info, chain_info_output); + string memory finalJson = vm.serializeString(parent_object, parameters, parameters_output); + + vm.writeJson(finalJson, outputPath); + + } } diff --git a/script/utils/validateStorage/validateUpgrade.sh b/script/utils/validateStorage/validateUpgrade.sh index 59a708d72d..943f30f6f4 100644 --- a/script/utils/validateStorage/validateUpgrade.sh +++ b/script/utils/validateStorage/validateUpgrade.sh @@ -69,7 +69,7 @@ echo "Comparing storage layouts..." # Add -k operator if present if [ ! -k "$1" ]; then echo "Keeping old storage layout files" - eval "npx ts-node script/upgrade/validateStorage.ts --old onChainLayout.csv --new localLayout.csv --keep" + eval "npx ts-node script/utils/validateStorage/validateStorage.ts --old onChainLayout.csv --new localLayout.csv --keep" else - eval "npx ts-node script/upgrade/validateStorage.ts --old onChainLayout.csv --new localLayout.csv" + eval "npx ts-node script/utils/validateStorage/validateStorage.ts --old onChainLayout.csv --new localLayout.csv" fi \ No newline at end of file diff --git a/src/contracts/interfaces/IEigenPodManager.sol b/src/contracts/interfaces/IEigenPodManager.sol index 020e0d2d97..4860cff408 100644 --- a/src/contracts/interfaces/IEigenPodManager.sol +++ b/src/contracts/interfaces/IEigenPodManager.sol @@ -26,9 +26,6 @@ interface IEigenPodManager is IPausable { /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager event BeaconChainETHDeposited(address indexed podOwner, uint256 amount); - /// @notice Emitted when `maxPods` value is updated from `previousValue` to `newValue` - event MaxPodsUpdated(uint256 previousValue, uint256 newValue); - /// @notice Emitted when the balance of an EigenPod is updated event PodSharesUpdated(address indexed podOwner, int256 sharesDelta); @@ -107,9 +104,6 @@ interface IEigenPodManager is IPausable { /// @notice Returns the number of EigenPods that have been created function numPods() external view returns (uint256); - /// @notice Returns the maximum number of EigenPods that can be created - function maxPods() external view returns (uint256); - /** * @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy. * @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can diff --git a/src/contracts/pods/EigenPod.sol b/src/contracts/pods/EigenPod.sol index 49ab3b0d06..39bc286a0d 100644 --- a/src/contracts/pods/EigenPod.sol +++ b/src/contracts/pods/EigenPod.sol @@ -91,9 +91,12 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen /// @notice This variable tracks any ETH deposited into this contract via the `receive` fallback function uint256 public nonBeaconChainETHBalanceWei; - /// @notice This variable tracks the total amount of partial withdrawals claimed via merkle proofs prior to a switch to ZK proofs for claiming partial withdrawals + /// @notice This variable tracks the total amount of partial withdrawals claimed via merkle proofs prior to a switch to ZK proofs for claiming partial withdrawals uint64 public sumOfPartialWithdrawalsClaimedGwei; + /// @notice Number of validators with proven withdrawal credentials, who do not have proven full withdrawals + uint256 activeValidatorCount; + modifier onlyEigenPodManager() { require(msg.sender == address(eigenPodManager), "EigenPod.onlyEigenPodManager: not eigenPodManager"); _; @@ -479,6 +482,7 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen }); // Proofs complete - update this validator's status, record its proven balance, and save in state: + activeValidatorCount++; validatorInfo.status = VALIDATOR_STATUS.ACTIVE; validatorInfo.validatorIndex = validatorIndex; validatorInfo.mostRecentBalanceUpdateTimestamp = oracleTimestamp; @@ -697,9 +701,12 @@ contract EigenPod is IEigenPod, Initializable, ReentrancyGuardUpgradeable, Eigen * Finally, the validator is fully withdrawn. Update their status and place in state: */ - validatorInfo.restakedBalanceGwei = 0; - validatorInfo.status = VALIDATOR_STATUS.WITHDRAWN; + if (validatorInfo.status != VALIDATOR_STATUS.WITHDRAWN) { + activeValidatorCount--; + validatorInfo.status = VALIDATOR_STATUS.WITHDRAWN; + } + validatorInfo.restakedBalanceGwei = 0; _validatorPubkeyHashToInfo[validatorPubkeyHash] = validatorInfo; emit FullWithdrawalRedeemed(validatorIndex, withdrawalTimestamp, recipient, withdrawalAmountGwei); diff --git a/src/contracts/pods/EigenPodManager.sol b/src/contracts/pods/EigenPodManager.sol index a90177a136..ce42de5445 100644 --- a/src/contracts/pods/EigenPodManager.sol +++ b/src/contracts/pods/EigenPodManager.sol @@ -55,13 +55,11 @@ contract EigenPodManager is } function initialize( - uint256 _maxPods, IBeaconChainOracle _beaconChainOracle, address initialOwner, IPauserRegistry _pauserRegistry, uint256 _initPausedStatus ) external initializer { - _setMaxPods(_maxPods); _updateBeaconChainOracle(_beaconChainOracle); _transferOwnership(initialOwner); _initializePauser(_pauserRegistry, _initPausedStatus); @@ -223,15 +221,6 @@ contract EigenPodManager is ownerToPod[podOwner].withdrawRestakedBeaconChainETH(destination, shares); } - /** - * Sets the maximum number of pods that can be deployed - * @param newMaxPods The new maximum number of pods that can be deployed - * @dev Callable by the unpauser of this contract - */ - function setMaxPods(uint256 newMaxPods) external onlyUnpauser { - _setMaxPods(newMaxPods); - } - /** * @notice Updates the oracle contract that provides the beacon chain state root * @param newBeaconChainOracle is the new oracle contract being pointed to @@ -256,8 +245,6 @@ contract EigenPodManager is // INTERNAL FUNCTIONS function _deployPod() internal returns (IEigenPod) { - // check that the limit of EigenPods has not been hit, and increment the EigenPod count - require(numPods + 1 <= maxPods, "EigenPodManager._deployPod: pod limit reached"); ++numPods; // create the pod IEigenPod pod = IEigenPod( @@ -281,12 +268,6 @@ contract EigenPodManager is emit BeaconOracleUpdated(address(newBeaconChainOracle)); } - /// @notice Internal setter for `maxPods` that also emits an event - function _setMaxPods(uint256 _maxPods) internal { - emit MaxPodsUpdated(maxPods, _maxPods); - maxPods = _maxPods; - } - /** * @notice Calculates the change in a pod owner's delegateable shares as a result of their beacon chain ETH shares changing * from `sharesBefore` to `sharesAfter`. The key concept here is that negative/"deficit" shares are not delegateable. diff --git a/src/contracts/pods/EigenPodManagerStorage.sol b/src/contracts/pods/EigenPodManagerStorage.sol index 76e687fbd4..f637f060ef 100644 --- a/src/contracts/pods/EigenPodManagerStorage.sol +++ b/src/contracts/pods/EigenPodManagerStorage.sol @@ -50,8 +50,9 @@ abstract contract EigenPodManagerStorage is IEigenPodManager { /// @notice The number of EigenPods that have been deployed uint256 public numPods; - /// @notice The maximum number of EigenPods that can be deployed - uint256 public maxPods; + /// @notice Deprecated from old mainnet release. Was initially used to limit growth early on but there is no longer + /// a maximum number of EigenPods that can be deployed. + uint256 private __deprecated_maxPods; // BEGIN STORAGE VARIABLES ADDED AFTER MAINNET DEPLOYMENT -- DO NOT SUGGEST REORDERING TO CONVENTIONAL ORDER /** diff --git a/src/test/DepositWithdraw.t.sol b/src/test/DepositWithdraw.t.sol index accb90382a..dea978d8d6 100644 --- a/src/test/DepositWithdraw.t.sol +++ b/src/test/DepositWithdraw.t.sol @@ -416,7 +416,6 @@ contract DepositWithdrawTests is EigenLayerTestHelper { address(eigenPodManagerImplementation), abi.encodeWithSelector( EigenPodManager.initialize.selector, - type(uint256).max, beaconChainOracleAddress, eigenLayerReputedMultisig, eigenLayerPauserReg, diff --git a/src/test/EigenLayerDeployer.t.sol b/src/test/EigenLayerDeployer.t.sol index ac28592d2b..8b5ada2a8e 100644 --- a/src/test/EigenLayerDeployer.t.sol +++ b/src/test/EigenLayerDeployer.t.sol @@ -303,7 +303,6 @@ contract EigenLayerDeployer is Operators { address(eigenPodManagerImplementation), abi.encodeWithSelector( EigenPodManager.initialize.selector, - type(uint256).max, // maxPods beaconChainOracleAddress, eigenLayerReputedMultisig, eigenLayerPauserReg, diff --git a/src/test/EigenPod.t.sol b/src/test/EigenPod.t.sol index 56cf277cf6..c991fb6963 100644 --- a/src/test/EigenPod.t.sol +++ b/src/test/EigenPod.t.sol @@ -76,9 +76,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { /// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager event BeaconChainETHDeposited(address indexed podOwner, uint256 amount); - /// @notice Emitted when `maxPods` value is updated from `previousValue` to `newValue` - event MaxPodsUpdated(uint256 previousValue, uint256 newValue); - // EIGENPOD EVENTS /// @notice Emitted when an ETH validator stakes via this eigenPod event EigenPodStaked(bytes pubkey); @@ -238,7 +235,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { address(eigenPodManagerImplementation), abi.encodeWithSelector( EigenPodManager.initialize.selector, - type(uint256).max, // maxPods beaconChainOracle, initialOwner, pauserReg, @@ -1334,37 +1330,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { require(numPodsAfter == numPodsBefore + 1, "numPods did not increment correctly"); } - // verifies that the `maxPods` variable is enforced on the `EigenPod.stake` function - function test_maxPodsEnforcementOnStake( - bytes calldata _pubkey, - bytes calldata _signature, - bytes32 _depositDataRoot - ) public { - // set pod limit to current number of pods - cheats.startPrank(unpauser); - EigenPodManager(address(eigenPodManager)).setMaxPods(EigenPodManager(address(eigenPodManager)).numPods()); - cheats.stopPrank(); - - cheats.startPrank(podOwner); - cheats.expectRevert("EigenPodManager._deployPod: pod limit reached"); - eigenPodManager.stake{value: 32 ether}(_pubkey, _signature, _depositDataRoot); - cheats.stopPrank(); - - // set pod limit to *one more than* current number of pods - cheats.startPrank(unpauser); - EigenPodManager(address(eigenPodManager)).setMaxPods(EigenPodManager(address(eigenPodManager)).numPods() + 1); - cheats.stopPrank(); - - IEigenPod newPod = eigenPodManager.getPod(podOwner); - - cheats.startPrank(podOwner); - // successful call - cheats.expectEmit(true, true, true, true, address(newPod)); - emit EigenPodStaked(_pubkey); - eigenPodManager.stake{value: 32 ether}(_pubkey, _signature, _depositDataRoot); - cheats.stopPrank(); - } - // verifies that the `numPod` variable increments correctly on a succesful call to the `EigenPod.createPod` function function test_incrementNumPodsOnCreatePod() public { uint256 numPodsBefore = EigenPodManager(address(eigenPodManager)).numPods(); @@ -1379,53 +1344,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { eigenPodManager.createPod(); } - // verifies that the `maxPods` variable is enforced on the `EigenPod.createPod` function - function test_maxPodsEnforcementOnCreatePod() public { - // set pod limit to current number of pods - cheats.startPrank(unpauser); - uint256 previousValue = EigenPodManager(address(eigenPodManager)).maxPods(); - uint256 newValue = EigenPodManager(address(eigenPodManager)).numPods(); - cheats.expectEmit(true, true, true, true, address(eigenPodManager)); - emit MaxPodsUpdated(previousValue, newValue); - EigenPodManager(address(eigenPodManager)).setMaxPods(newValue); - cheats.stopPrank(); - - cheats.expectRevert("EigenPodManager._deployPod: pod limit reached"); - eigenPodManager.createPod(); - - // set pod limit to *one more than* current number of pods - cheats.startPrank(unpauser); - previousValue = EigenPodManager(address(eigenPodManager)).maxPods(); - newValue = EigenPodManager(address(eigenPodManager)).numPods() + 1; - cheats.expectEmit(true, true, true, true, address(eigenPodManager)); - emit MaxPodsUpdated(previousValue, newValue); - EigenPodManager(address(eigenPodManager)).setMaxPods(newValue); - cheats.stopPrank(); - - // successful call - eigenPodManager.createPod(); - } - - function test_setMaxPods(uint256 newValue) public { - cheats.startPrank(unpauser); - uint256 previousValue = EigenPodManager(address(eigenPodManager)).maxPods(); - cheats.expectEmit(true, true, true, true, address(eigenPodManager)); - emit MaxPodsUpdated(previousValue, newValue); - EigenPodManager(address(eigenPodManager)).setMaxPods(newValue); - cheats.stopPrank(); - - require(EigenPodManager(address(eigenPodManager)).maxPods() == newValue, "maxPods value not set correctly"); - } - - function test_setMaxPods_RevertsWhenNotCalledByUnpauser(address notUnpauser) public fuzzedAddress(notUnpauser) { - cheats.assume(notUnpauser != unpauser); - uint256 newValue = 0; - cheats.startPrank(notUnpauser); - cheats.expectRevert("msg.sender is not permissioned as unpauser"); - EigenPodManager(address(eigenPodManager)).setMaxPods(newValue); - cheats.stopPrank(); - } - function test_validatorPubkeyToInfo() external { bytes memory _pubkey = hex"93a0dd04ccddf3f1b419fdebf99481a2182c17d67cf14d32d6e50fc4bf8effc8db4a04b7c2f3a5975c1b9b74e2841888"; diff --git a/src/test/events/IEigenPodManagerEvents.sol b/src/test/events/IEigenPodManagerEvents.sol index 2f97966848..d974ce2c94 100644 --- a/src/test/events/IEigenPodManagerEvents.sol +++ b/src/test/events/IEigenPodManagerEvents.sol @@ -8,13 +8,9 @@ interface IEigenPodManagerEvents { /// @notice Emitted to notify that the denebForkTimestamp has been set event DenebForkTimestampUpdated(uint64 denebForkTimestamp); - /// @notice Emitted to notify the deployment of an EigenPod event PodDeployed(address indexed eigenPod, address indexed podOwner); - /// @notice Emitted when `maxPods` value is updated from `previousValue` to `newValue` - event MaxPodsUpdated(uint256 previousValue, uint256 newValue); - /// @notice Emitted when the balance of an EigenPod is updated event PodSharesUpdated(address indexed podOwner, int256 sharesDelta); } \ No newline at end of file diff --git a/src/test/harnesses/EigenPodHarness.sol b/src/test/harnesses/EigenPodHarness.sol index 3cdd6409c0..c139620f06 100644 --- a/src/test/harnesses/EigenPodHarness.sol +++ b/src/test/harnesses/EigenPodHarness.sol @@ -20,6 +20,14 @@ contract EPInternalFunctions is EigenPod, Test { _GENESIS_TIME ) {} + function getActiveValidatorCount() public view returns (uint256) { + return activeValidatorCount; + } + + function setActiveValidatorCount(uint _count) public { + activeValidatorCount = _count; + } + function verifyWithdrawalCredentials( uint64 oracleTimestamp, bytes32 beaconStateRoot, diff --git a/src/test/integration/IntegrationDeployer.t.sol b/src/test/integration/IntegrationDeployer.t.sol index 0687a7089f..1be9fdf81a 100644 --- a/src/test/integration/IntegrationDeployer.t.sol +++ b/src/test/integration/IntegrationDeployer.t.sol @@ -235,7 +235,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { address(eigenPodManagerImplementation), abi.encodeWithSelector( EigenPodManager.initialize.selector, - type(uint).max, // maxPods address(beaconChainOracle), eigenLayerReputedMultisig, // initialOwner pauserRegistry, diff --git a/src/test/mocks/EigenPodManagerMock.sol b/src/test/mocks/EigenPodManagerMock.sol index e3c2f0e17a..96da69030b 100644 --- a/src/test/mocks/EigenPodManagerMock.sol +++ b/src/test/mocks/EigenPodManagerMock.sol @@ -84,9 +84,6 @@ contract EigenPodManagerMock is IEigenPodManager, Test { function numPods() external view returns (uint256) {} - function maxPods() external view returns (uint256) {} - - function denebForkTimestamp() external pure returns (uint64) { return type(uint64).max; } diff --git a/src/test/tree/EigenPodManagerUnit.tree b/src/test/tree/EigenPodManagerUnit.tree index c8807c2be3..b09fdad3f9 100644 --- a/src/test/tree/EigenPodManagerUnit.tree +++ b/src/test/tree/EigenPodManagerUnit.tree @@ -6,8 +6,6 @@ ├── when createPod called │ ├── given the user has already created a pod │ │ └── it should revert -│ ├── given that the max number of pods has been deployed -│ │ └── it should revert │ └── given the user has not created a pod │ └── it should deploy a pod ├── when stake is called @@ -15,11 +13,6 @@ │ │ └── it should deploy a pod │ └── given the user has already created a pod │ └── it should call stake on the eigenPod -├── when setMaxPods is called -│ ├── given the user is not the pauser -│ │ └── it should revert -│ └── given the user is the pauser -│ └── it should set the max pods ├── when updateBeaconChainOracle is called │ ├── given the user is not the owner │ │ └── it should revert diff --git a/src/test/unit/EigenPod-PodManagerUnit.t.sol b/src/test/unit/EigenPod-PodManagerUnit.t.sol index 7aae852b6d..b0507257b0 100644 --- a/src/test/unit/EigenPod-PodManagerUnit.t.sol +++ b/src/test/unit/EigenPod-PodManagerUnit.t.sol @@ -85,7 +85,6 @@ contract EigenPod_PodManager_UnitTests is EigenLayerUnitTestSetup { address(eigenPodManagerWrapper), abi.encodeWithSelector( EigenPodManager.initialize.selector, - type(uint256).max /*maxPods*/, beaconChainOracle, initialOwner, pauserRegistry, @@ -228,9 +227,7 @@ contract EigenPod_PodManager_UnitTests_EigenPod is EigenPod_PodManager_UnitTests function test_stake_podAlreadyDeployed(bytes memory signature, bytes32 depositDataRoot) public { uint256 stakeAmount = 32e18; - uint256 maxPods = eigenPodManager.maxPods(); uint256 numPods = eigenPodManager.numPods(); - emit log_named_uint("maxPods", maxPods); emit log_named_uint("numPods", numPods); cheats.startPrank(podOwner); diff --git a/src/test/unit/EigenPodManagerUnit.t.sol b/src/test/unit/EigenPodManagerUnit.t.sol index 9e0e3fc3f2..f88697273c 100644 --- a/src/test/unit/EigenPodManagerUnit.t.sol +++ b/src/test/unit/EigenPodManagerUnit.t.sol @@ -53,7 +53,6 @@ contract EigenPodManagerUnitTests is EigenLayerUnitTestSetup { address(eigenLayerProxyAdmin), abi.encodeWithSelector( EigenPodManager.initialize.selector, - type(uint256).max /*maxPods*/, IBeaconChainOracle(address(0)) /*beaconChainOracle*/, initialOwner, pauserRegistry, @@ -111,7 +110,6 @@ contract EigenPodManagerUnitTests_Initialization_Setters is EigenPodManagerUnitT function test_initialization() public { // Check max pods, beacon chain, owner, and pauser - assertEq(eigenPodManager.maxPods(), type(uint256).max, "Initialization: max pods incorrect"); assertEq(address(eigenPodManager.beaconChainOracle()), address(IBeaconChainOracle(address(0))), "Initialization: beacon chain oracle incorrect"); assertEq(eigenPodManager.owner(), initialOwner, "Initialization: owner incorrect"); assertEq(address(eigenPodManager.pauserRegistry()), address(pauserRegistry), "Initialization: pauser registry incorrect"); @@ -127,36 +125,17 @@ contract EigenPodManagerUnitTests_Initialization_Setters is EigenPodManagerUnitT function test_initialize_revert_alreadyInitialized() public { cheats.expectRevert("Initializable: contract is already initialized"); - eigenPodManager.initialize(type(uint256).max /*maxPods*/, + eigenPodManager.initialize( IBeaconChainOracle(address(0)) /*beaconChainOracle*/, initialOwner, pauserRegistry, 0 /*initialPausedStatus*/); } - function testFuzz_setMaxPods_revert_notUnpauser(address notUnpauser) public filterFuzzedAddressInputs(notUnpauser) { - cheats.assume(notUnpauser != unpauser); - cheats.prank(notUnpauser); - cheats.expectRevert("msg.sender is not permissioned as unpauser"); - eigenPodManager.setMaxPods(0); - } - /******************************************************************************* Setters *******************************************************************************/ - function test_setMaxPods() public { - // Set max pods - uint256 newMaxPods = 0; - cheats.expectEmit(true, true, true, true); - emit MaxPodsUpdated(eigenPodManager.maxPods(), newMaxPods); - cheats.prank(unpauser); - eigenPodManager.setMaxPods(newMaxPods); - - // Check storage update - assertEq(eigenPodManager.maxPods(), newMaxPods, "Max pods not updated"); - } - function testFuzz_updateBeaconChainOracle_revert_notOwner(address notOwner) public filterFuzzedAddressInputs(notOwner) { cheats.assume(notOwner != initialOwner); cheats.prank(notOwner); @@ -220,27 +199,6 @@ contract EigenPodManagerUnitTests_CreationTests is EigenPodManagerUnitTests, IEi cheats.expectRevert("EigenPodManager.createPod: Sender already has a pod"); eigenPodManager.createPod(); } - - function test_createPod_revert_maxPodsUint256() public { - // Write numPods into storage. Num pods is at slot 153 - bytes32 slot = bytes32(uint256(153)); - bytes32 value = bytes32(eigenPodManager.maxPods()); - cheats.store(address(eigenPodManager), slot, value); - - // Expect revert on pod creation - cheats.expectRevert(); // Arithmetic overflow/underflow - eigenPodManager.createPod(); - } - - function test_createPod_revert_maxPodsNontUint256() public { - // Set max pods to a small value - 0 - cheats.prank(unpauser); - eigenPodManager.setMaxPods(0); - - // Expect revert on pod creation - cheats.expectRevert("EigenPodManager._deployPod: pod limit reached"); - eigenPodManager.createPod(); - } } contract EigenPodManagerUnitTests_StakeTests is EigenPodManagerUnitTests { diff --git a/src/test/unit/EigenPodUnit.t.sol b/src/test/unit/EigenPodUnit.t.sol index 7b907dfc40..f9100c7fe3 100644 --- a/src/test/unit/EigenPodUnit.t.sol +++ b/src/test/unit/EigenPodUnit.t.sol @@ -453,6 +453,8 @@ contract EigenPodUnitTests_VerifyWithdrawalCredentialsTests is EigenPodHarnessSe uint64 effectiveBalanceGwei = validatorFields.getEffectiveBalanceGwei(); assertGt(effectiveBalanceGwei, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, "Proof file has an effective balance less than 32 ETH"); + uint activeValidatorCountBefore = eigenPodHarness.getActiveValidatorCount(); + // Verify withdrawal credentials vm.expectEmit(true, true, true, true); emit ValidatorRestaked(validatorIndex); @@ -467,6 +469,8 @@ contract EigenPodUnitTests_VerifyWithdrawalCredentialsTests is EigenPodHarnessSe ); // Checks + uint activeValidatorCountAfter = eigenPodHarness.getActiveValidatorCount(); + assertEq(activeValidatorCountAfter, activeValidatorCountBefore + 1, "active validator count should increase when proving withdrawal credentials"); assertEq(restakedBalanceWei, uint256(MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR) * uint256(1e9), "Returned restaked balance gwei should be max"); _assertWithdrawalCredentialsSet(MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR); } @@ -480,6 +484,8 @@ contract EigenPodUnitTests_VerifyWithdrawalCredentialsTests is EigenPodHarnessSe uint64 effectiveBalanceGwei = validatorFields.getEffectiveBalanceGwei(); assertLt(effectiveBalanceGwei, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, "Proof file has an effective balance greater than 32 ETH"); + uint activeValidatorCountBefore = eigenPodHarness.getActiveValidatorCount(); + // Verify withdrawal credentials vm.expectEmit(true, true, true, true); emit ValidatorRestaked(validatorIndex); @@ -494,6 +500,8 @@ contract EigenPodUnitTests_VerifyWithdrawalCredentialsTests is EigenPodHarnessSe ); // Checks + uint activeValidatorCountAfter = eigenPodHarness.getActiveValidatorCount(); + assertEq(activeValidatorCountAfter, activeValidatorCountBefore + 1, "active validator count should increase when proving withdrawal credentials"); assertEq(restakedBalanceWei, uint256(effectiveBalanceGwei) * uint256(1e9), "Returned restaked balance gwei incorrect"); _assertWithdrawalCredentialsSet(effectiveBalanceGwei); } @@ -920,10 +928,19 @@ contract EigenPodUnitTests_WithdrawalTests is EigenPodHarnessSetup, ProofParsing mostRecentBalanceUpdateTimestamp: 0, status: IEigenPod.VALIDATOR_STATUS.ACTIVE }); + + // Since we're withdrawing using an ACTIVE validator, ensure we have + // a validator count to decrement + uint activeValidatorCountBefore = 1 + eigenPodHarness.getActiveValidatorCount(); + eigenPodHarness.setActiveValidatorCount(activeValidatorCountBefore); - // Process full withdrawal + // Process full withdrawal. IEigenPod.VerifiedWithdrawal memory vw = eigenPodHarness.processFullWithdrawal(0, pubkeyHash, 0, podOwner, withdrawalAmount, validatorInfo); + // Validate that our activeValidatorCount decreased + uint activeValidatorCountAfter = eigenPodHarness.getActiveValidatorCount(); + assertEq(activeValidatorCountAfter, activeValidatorCountBefore - 1, "active validator count should decrease when withdrawing active validator"); + // Get expected amounts based on withdrawalAmount uint64 amountETHToQueue; uint64 amountETHToSend;