From b51a2a335c77e980c27f0f19a17ac9ba689e3b5f Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 29 Apr 2025 15:04:41 -0600 Subject: [PATCH 01/47] WIP starting updates for v3 prelude --- script/DeployEigenlayerSlashing.s.sol | 2 +- script/deploys/DeployEtherFISuite.s.sol | 7 +- script/deploys/DeployEtherFiViewer.s.sol | 2 - script/deploys/DeployPhaseOne.s.sol | 4 +- script/upgrades/EtherFiNodeScript.s.sol | 10 +- .../upgrades/MultipleValidatorsPerSafe.s.sol | 2 +- script/upgrades/UpgradeForEigenLayerM2.s.sol | 2 +- src/EtherFiAdmin.sol | 6 +- src/EtherFiNode.sol | 715 +---- src/EtherFiNodesManager.sol | 173 +- src/LiquidityPool.sol | 13 +- src/NFTExchange.sol | 3 +- src/StakingManager.sol | 511 +--- src/TNFT.sol | 10 - src/helpers/EtherFiViewer.sol | 20 +- src/interfaces/IEtherFiNode.sol | 9 + src/interfaces/IEtherFiNodesManager.sol | 73 + test/ContractCodeChecker.t.sol | 2 +- test/EigenLayerIntegration.t.sol | 94 +- test/EtherFiNode.t.sol | 2679 +---------------- test/EtherFiNodesManager.t.sol | 387 --- test/EtherFiTimelock.sol | 5 + test/EtherFiViewer.t.sol | 3 + test/LiquidityPool.t.sol | 8 +- test/TestSetup.sol | 56 +- 25 files changed, 495 insertions(+), 4301 deletions(-) diff --git a/script/DeployEigenlayerSlashing.s.sol b/script/DeployEigenlayerSlashing.s.sol index 3af10d085..74709b56a 100644 --- a/script/DeployEigenlayerSlashing.s.sol +++ b/script/DeployEigenlayerSlashing.s.sol @@ -31,7 +31,7 @@ contract DeployEigenlayerSlashingScript is Script { vm.startBroadcast(); - etherFiNodeImplementation = new EtherFiNode(); + etherFiNodeImplementation = new EtherFiNode(address(0),address(0),address(0),address(0)); etherFiNodesManagerImplementation = new EtherFiNodesManager(); etherFiRestakerImplementation = new EtherFiRestaker(rewardsCoordinator); diff --git a/script/deploys/DeployEtherFISuite.s.sol b/script/deploys/DeployEtherFISuite.s.sol index aa98e7af8..4dc139811 100644 --- a/script/deploys/DeployEtherFISuite.s.sol +++ b/script/deploys/DeployEtherFISuite.s.sol @@ -137,6 +137,7 @@ contract DeployEtherFiSuiteScript is Script { etherFiNodesManagerImplementation = new EtherFiNodesManager(); etherFiNodeManagerProxy = new UUPSProxy(address(etherFiNodesManagerImplementation),""); etherFiNodesManager = EtherFiNodesManager(payable(address(etherFiNodeManagerProxy))); + /* etherFiNodesManager.initialize( address(treasury), address(auctionManager), @@ -147,13 +148,15 @@ contract DeployEtherFiSuiteScript is Script { address(0), address(0) ); + */ regulationsManagerImplementation = new RegulationsManager(); regulationsManagerProxy = new UUPSProxy(address(regulationsManagerImplementation), ""); regulationsManagerInstance = RegulationsManager(address(regulationsManagerProxy)); regulationsManagerInstance.initialize(); - EtherFiNode etherFiNode = new EtherFiNode(); + // TODO + EtherFiNode etherFiNode = new EtherFiNode(address(0), address(0), address(0), address(0)); // Mainnet Addresses // address private immutable rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; @@ -187,11 +190,13 @@ contract DeployEtherFiSuiteScript is Script { protocolRevenueManager.setAuctionManagerAddress(address(auctionManager)); protocolRevenueManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); + /* stakingManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); stakingManager.setLiquidityPoolAddress(address(liquidityPool)); stakingManager.registerEtherFiNodeImplementationContract(address(etherFiNode)); stakingManager.registerTNFTContract(address(TNFTInstance)); stakingManager.registerBNFTContract(address(BNFTInstance)); + */ liquidityPool.initialize(address(eETHInstance), address(stakingManager), address(etherFiNodesManager), address(0), address(0), address(0), address(0)); diff --git a/script/deploys/DeployEtherFiViewer.s.sol b/script/deploys/DeployEtherFiViewer.s.sol index dc354ee71..9ed28953c 100644 --- a/script/deploys/DeployEtherFiViewer.s.sol +++ b/script/deploys/DeployEtherFiViewer.s.sol @@ -32,7 +32,5 @@ contract DeployEtherFiViewer is Script { assert(etherFiNodeAddresses[0] == 0x31db9021ec8E1065e1f55553c69e1B1ea9d20533); assert(etherFiNodeAddresses[1] == 0xC3D3662A44c0d80080D3AF0eea752369c504724e); - viewer.EigenPod_hasRestaked(validatorIds); - } } diff --git a/script/deploys/DeployPhaseOne.s.sol b/script/deploys/DeployPhaseOne.s.sol index 8730c91df..7c1d6ff7e 100644 --- a/script/deploys/DeployPhaseOne.s.sol +++ b/script/deploys/DeployPhaseOne.s.sol @@ -104,6 +104,7 @@ contract DeployPhaseOne is Script { etherFiNodesManagerImplementation = new EtherFiNodesManager(); etherFiNodeManagerProxy = new UUPSProxy(address(etherFiNodesManagerImplementation),""); etherFiNodesManager = EtherFiNodesManager(payable(address(etherFiNodeManagerProxy))); + /* etherFiNodesManager.initialize( address(treasury), address(auctionManager), @@ -114,8 +115,9 @@ contract DeployPhaseOne is Script { address(0), address(0) ); + */ - EtherFiNode etherFiNode = new EtherFiNode(); + EtherFiNode etherFiNode = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); // Setup dependencies nodeOperatorManager.setAuctionContractAddress(address(auctionManager)); diff --git a/script/upgrades/EtherFiNodeScript.s.sol b/script/upgrades/EtherFiNodeScript.s.sol index 75ec9f37a..5b8fb9315 100644 --- a/script/upgrades/EtherFiNodeScript.s.sol +++ b/script/upgrades/EtherFiNodeScript.s.sol @@ -18,13 +18,19 @@ contract EtherFiNodeUpgrade is Script { address stakingManagerProxyAddress = addressProvider.getContractAddress("StakingManager"); + revert("FILL IN ADDRESSES"); + address eigenPodManager; + address delegationManager; + address liquidityPool; + address etherFiNodesManager; + StakingManager stakingManager = StakingManager(stakingManagerProxyAddress); vm.startBroadcast(deployerPrivateKey); - EtherFiNode etherFiNode = new EtherFiNode(); + EtherFiNode etherFiNode = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager); stakingManager.upgradeEtherFiNode(address(etherFiNode)); vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/script/upgrades/MultipleValidatorsPerSafe.s.sol b/script/upgrades/MultipleValidatorsPerSafe.s.sol index 431920590..031f441f0 100644 --- a/script/upgrades/MultipleValidatorsPerSafe.s.sol +++ b/script/upgrades/MultipleValidatorsPerSafe.s.sol @@ -56,7 +56,7 @@ contract MultipleValidatorsPerSafe is Script { StakingManager StakingManagerNewImpl = new StakingManager(); TNFT TNFTNewImpl = new TNFT(); EtherFiNodesManager EtherFiNodesManagerNewImpl = new EtherFiNodesManager(); - EtherFiNode EtherFiNodeNewImpl = new EtherFiNode(); + EtherFiNode EtherFiNodeNewImpl = new EtherFiNode(address(0x0),address(0x0),address(0x0),address(0x0)); address el_delegationManager; if (block.chainid == 1) { diff --git a/script/upgrades/UpgradeForEigenLayerM2.s.sol b/script/upgrades/UpgradeForEigenLayerM2.s.sol index b172378d1..139aceaf5 100644 --- a/script/upgrades/UpgradeForEigenLayerM2.s.sol +++ b/script/upgrades/UpgradeForEigenLayerM2.s.sol @@ -52,7 +52,7 @@ contract UpgradeForEigenLayerM2 is Script { // Liquifier LiquifierNewImpl = new Liquifier(); EtherFiNodesManager EtherFiNodesManagerNewImpl = new EtherFiNodesManager(); - EtherFiNode EtherFiNodeNewImpl = new EtherFiNode(); + EtherFiNode EtherFiNodeNewImpl = new EtherFiNode(address(0x0),address(0x0),address(0x0),address(0x0)); address el_delegationManager; address pancakeRouter; diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index 27b66b540..5bf4ed81e 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -224,9 +224,11 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { } else if (taskType == TaskType.SendExitRequests) { liquidityPool.sendExitRequests(_validators); } else if (taskType == TaskType.ProcessNodeExit) { - etherFiNodesManager.processNodeExit(_validators, _timestamps); + // TODO(dave): re-implement for V3 + // etherFiNodesManager.processNodeExit(_validators, _timestamps); } else if (taskType == TaskType.MarkBeingSlashed) { - etherFiNodesManager.markBeingSlashed(_validators); + // TODO(dave): re-imprement for V3 + //etherFiNodesManager.markBeingSlashed(_validators); } emit ValidatorManagementTaskCompleted(taskHash, _reportHash, _validators, _timestamps, taskType); } diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 8379d7b21..8778eba67 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -1,691 +1,102 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; +pragma solidity ^0.8.27; -import "./interfaces/IEtherFiNode.sol"; -import "./interfaces/IEtherFiNodesManager.sol"; -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; -import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/interfaces/IERC1271.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "./eigenlayer-interfaces/IEigenPodManager.sol"; -import "./eigenlayer-interfaces/IDelayedWithdrawalRouter.sol"; -import "./eigenlayer-interfaces/IDelegationManager.sol"; +//import "../lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; +//import "../lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from "../src/eigenlayer-interfaces/IDelegationManager.sol"; +import {IEigenPodManager} from "../src/eigenlayer-interfaces/IEigenPodManager.sol"; +import {IEigenPod} from "../src/eigenlayer-interfaces/IEigenPod.sol"; +import {IERC20} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; -contract EtherFiNode is IEtherFiNode, IERC1271 { - address public etherFiNodesManager; +//import {IPodOwner} from "../src/iEtherFiNode/IPodOwner.sol"; +import {IEtherFiNode} from "../src/interfaces/IEtherFiNode.sol"; +import {IEtherFiNodesManager} from "../src/interfaces/IEtherFiNodesManager.sol"; +import {ILiquidityPool} from "../src/interfaces/ILiquidityPool.sol"; +//import {IRewardsManager} from "../src/RewardsManager.sol"; +import {LibCall} from "../lib/solady/src/utils/LibCall.sol"; - uint256 public DEPRECATED_localRevenueIndex; - uint256 public DEPRECATED_vestedAuctionRewards; - string public DEPRECATED_ipfsHashForEncryptedValidatorKey; - uint32 public DEPRECATED_exitRequestTimestamp; - uint32 public DEPRECATED_exitTimestamp; - uint32 public DEPRECATED_stakingStartTimestamp; - VALIDATOR_PHASE public DEPRECATED_phase; +contract EtherFiNode is IEtherFiNode { - uint32 public DEPRECATED_restakingObservedExitBlock; - address public eigenPod; + IEigenPodManager public immutable eigenPodManager; + IDelegationManager public immutable delegationManager; + ILiquidityPool public immutable liquidityPool; + IEtherFiNodesManager public immutable etherFiNodesManager; - /// @dev Is this withdrawal safe is configured for restaking within the etherfi protocol. - bool public isRestakingEnabled; - - uint16 public version; - uint16 private _numAssociatedValidators; // num validators in {LIVE, BEING_SLASHED, EXITED} phase - uint16 public numExitRequestsByTnft; - uint16 public numExitedValidators; // EXITED & but not FULLY_WITHDRAWN - - mapping(uint256 => uint256) public associatedValidatorIndices; - uint256[] public associatedValidatorIds; // validators in {STAKE_DEPOSITED, WAITING_FOR_APPROVAL, LIVE, BEING_SLASHED, EXITED} phase - - uint64 public DEPRECATED_pendingWithdrawalFromRestakingInGwei; - uint64 public DEPRECATED_completedWithdrawalFromRestakingInGwei; - mapping(uint256 => uint32) DEPRECATED_restakingObservedExitBlocks; - - event EigenPodCreated(address indexed nodeAddress, address indexed podAddress); - - //-------------------------------------------------------------------------------------- - //---------------------------------- CONSTRUCTOR ------------------------------------ - //-------------------------------------------------------------------------------------- - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - etherFiNodesManager = address(0x000000000000000000000000000000000000dEaD); // prevent initialization of the proxy implementation - } - - /// @notice Based on the sources where they come from, the staking rewards are split into - /// - those from the execution layer: transaction fees and MEV - /// - those from the consensus layer: staking rewards for attesting the state of the chain, - /// proposing a new block, or being selected in a validator sync committee - /// To receive the rewards from the execution layer, it should have 'receive()' function. - receive() external payable {} - - /// @dev called once immediately after creating a new instance of a EtheriNode beacon proxy - function initialize(address _etherFiNodesManager) external { - require(DEPRECATED_phase == VALIDATOR_PHASE.NOT_INITIALIZED, "ALREADY_INITIALIZED"); - require(etherFiNodesManager == address(0), "ALREADY_INITIALIZED"); - require(_etherFiNodesManager != address(0), "NO_ZERO_ADDRESS"); - etherFiNodesManager = _etherFiNodesManager; - version = 1; - } - - // Update the safe contract from verison 0 to version 1 - // if `_validatorId` != 0, the v0 safe contract currently is tied to the validator with its id = `_validatorId` - // this function updates it to v1 so that it can be used by multiple validators - // else `_validatorId` == 0, this safe is not tied to any validator yet - function migrateVersion(uint256 _validatorId, IEtherFiNodesManager.ValidatorInfo memory _info) external onlyEtherFiNodeManagerContract { - if (version != 0) return; - - DEPRECATED_exitRequestTimestamp = 0; - DEPRECATED_exitTimestamp = 0; - DEPRECATED_stakingStartTimestamp = 0; - DEPRECATED_phase = VALIDATOR_PHASE.NOT_INITIALIZED; - delete DEPRECATED_ipfsHashForEncryptedValidatorKey; - - version = 1; - - if (_validatorId != 0) { - require(_numAssociatedValidators == 0, "ALREADY_INITIALIZED"); - registerValidator(_validatorId, false); - - updateNumberOfAssociatedValidators(1, 0); - - // Meaning that the validator got `sendExitRequest` before the safe version 1 release - // EFM._updateExitRequestTimestamp (which updates 'numExitRequestsByTnft') was not called. So, process that here - if (_info.exitRequestTimestamp > 0) { - updateNumExitRequests(1, 0); - } - - // Meaning that the validator got `processNodeExit` before the safe version 1 release - // EFM._setValidatorPhase (which updates 'numExitedValidators') was not called. So, process that here - if (_info.exitTimestamp > 0) { - updateNumExitedValidators(1, 0); - } - } - } - - // At version 0, an EtherFiNode contract is associated with only one validator - // After version 1, it can be associated with multiple validators having the same (B-nft, T-nft, node operator) - // returns the number of the validators in {LIVE, BEING_SLASHED, EXITED} phase associated with this safe - function numAssociatedValidators() public view returns (uint256) { - if (version == 0) { - // For the safe at version 0, `phase` variable is still valid and can be used to check if the validator is still active - if (DEPRECATED_phase == VALIDATOR_PHASE.LIVE || DEPRECATED_phase == VALIDATOR_PHASE.BEING_SLASHED || DEPRECATED_phase == VALIDATOR_PHASE.EXITED) { - return 1; - } else { - return 0; - } - } else { - return _numAssociatedValidators; - } - } - - function registerValidator(uint256 _validatorId, bool _enableRestaking) public onlyEtherFiNodeManagerContract ensureLatestVersion { - require(numAssociatedValidators() == 0 || isRestakingEnabled == _enableRestaking, "restaking status mismatch"); - - { - uint256 index = associatedValidatorIds.length; - associatedValidatorIds.push(_validatorId); - associatedValidatorIndices[_validatorId] = index; - } - - if (_enableRestaking) { - isRestakingEnabled = true; - createEigenPod(); // NOOP if already exists - } - } - - /// @dev deRegister the validator from the safe - /// if there is no more validator associated with this safe, it is recycled to be used again in the withdrawal safe pool - function unRegisterValidator( - uint256 _validatorId, - IEtherFiNodesManager.ValidatorInfo memory _info - ) external onlyEtherFiNodeManagerContract ensureLatestVersion returns (bool) { - require(_info.phase == VALIDATOR_PHASE.FULLY_WITHDRAWN || _info.phase == VALIDATOR_PHASE.NOT_INITIALIZED, "invalid phase"); - - // If the phase changed from EXITED to FULLY_WITHDRAWN, decrement the counter - if (_info.phase == VALIDATOR_PHASE.FULLY_WITHDRAWN) { - numExitedValidators -= 1; - } - - // If there was an exit request, decrement the number of exit requests - if (_info.exitRequestTimestamp != 0) { - numExitRequestsByTnft -= 1; - } - - { - uint256 index = associatedValidatorIndices[_validatorId]; - uint256 endIndex = associatedValidatorIds.length - 1; - uint256 end = associatedValidatorIds[endIndex]; - - associatedValidatorIds[index] = associatedValidatorIds[endIndex]; - associatedValidatorIndices[end] = index; - - associatedValidatorIds.pop(); - delete associatedValidatorIndices[_validatorId]; - } - - if (associatedValidatorIds.length == 0) { - require(numAssociatedValidators() == 0, "INVALID_STATE"); - - DEPRECATED_restakingObservedExitBlocks[_validatorId] = 0; - isRestakingEnabled = false; - return true; - } - return false; - } - - //-------------------------------------------------------------------------------------- - //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ - //-------------------------------------------------------------------------------------- - - function updateNumberOfAssociatedValidators(uint16 _up, uint16 _down) public onlyEtherFiNodeManagerContract ensureLatestVersion { - if (_up > 0) _numAssociatedValidators += _up; - if (_down > 0) _numAssociatedValidators -= _down; - } - - function updateNumExitRequests(uint16 _up, uint16 _down) public onlyEtherFiNodeManagerContract ensureLatestVersion { - if (_up > 0) numExitRequestsByTnft += _up; - if (_down > 0) numExitRequestsByTnft -= _down; - } - - function updateNumExitedValidators(uint16 _up, uint16 _down) public onlyEtherFiNodeManagerContract ensureLatestVersion { - if (_up > 0) numExitedValidators += _up; - if (_down > 0) numExitedValidators -= _down; - } - - /// @notice process the exit - // TODO: make it permission-less call - function processNodeExit(uint256 _validatorId) external onlyEtherFiNodeManagerContract ensureLatestVersion returns (bytes32[] memory fullWithdrawalRoots) { - if (isRestakingEnabled) { - fullWithdrawalRoots = _queueEigenpodFullWithdrawal(); - require(fullWithdrawalRoots.length == 1, "NO_FULLWITHDRAWAL_QUEUED"); - } - } - - function processFullWithdraw(uint256 _validatorId) external onlyEtherFiNodeManagerContract ensureLatestVersion { - updateNumberOfAssociatedValidators(0, 1); - } - - function completeQueuedWithdrawal(IDelegationManager.Withdrawal memory withdrawals, bool _receiveAsTokens) external onlyEtherFiNodeManagerContract { - IDelegationManager.Withdrawal[] memory _withdrawals = new IDelegationManager.Withdrawal[](1); - _withdrawals[0] = withdrawals; - return _completeQueuedWithdrawals(_withdrawals, _receiveAsTokens); - } - - function completeQueuedWithdrawals(IDelegationManager.Withdrawal[] memory withdrawals, bool _receiveAsTokens) external onlyEtherFiNodeManagerContract { - return _completeQueuedWithdrawals(withdrawals, _receiveAsTokens); + constructor(address _eigenPodManager, address _delegationManager, address _liquidityPool, address _etherFiNodesManager) { + eigenPodManager = IEigenPodManager(_eigenPodManager); + delegationManager = IDelegationManager(_delegationManager); + liquidityPool = ILiquidityPool(_liquidityPool); + etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); } - // `DelegationManager.completeQueuedWithdrawals` is used to complete the withdrawals: - // if `_receiveAsTokens` is true, the ETH in EigenPod will be withdrawn to EtherFiNode via EigenPodManager.withdrawSharesAsTokens(...) call - // if `_receiveAsTokens` is false, the ETH in EigenPod will be delegated to the current operator - function _completeQueuedWithdrawals(IDelegationManager.Withdrawal[] memory withdrawals, bool _receiveAsTokens) internal { - bool[] memory receiveAsTokens = new bool[](withdrawals.length); - IERC20[][] memory tokens = new IERC20[][](withdrawals.length); // redundant for BeaconETH strategy - for (uint256 i = 0; i < withdrawals.length; i++) { - require(withdrawals[i].withdrawer == address(this) && withdrawals[i].staker == address(this), "INVALID"); - receiveAsTokens[i] = _receiveAsTokens; - tokens[i] = new IERC20[](withdrawals[i].strategies.length); - } - - IDelegationManager mgr = IEtherFiNodesManager(etherFiNodesManager).delegationManager(); - mgr.completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); - } - - /// @dev transfer funds from the withdrawal safe to the 4 associated parties (bNFT, tNFT, treasury, nodeOperator) - function withdrawFunds( - address _treasury, uint256 _treasuryAmount, - address _operator, uint256 _operatorAmount, - address _tnftHolder, uint256 _tnftAmount, - address _bnftHolder, uint256 _bnftAmount - ) external onlyEtherFiNodeManagerContract ensureLatestVersion { - // the recipients of the funds must be able to receive the fund - // if it is a smart contract, they should implement either receive() or fallback() properly - // It's designed to prevent malicious actors from pausing the withdrawals - bool sent; - if (_operatorAmount > 0) { - (sent, ) = payable(_operator).call{value: _operatorAmount, gas: 10000}(""); - _treasuryAmount += (!sent) ? _operatorAmount : 0; - } - if (_bnftAmount > 0) { - (sent, ) = payable(_bnftHolder).call{value: _bnftAmount, gas: 12000}(""); - _treasuryAmount += (!sent) ? _bnftAmount : 0; - } - if (_tnftAmount > 0) { - (sent, ) = payable(_tnftHolder).call{value: _tnftAmount, gas: 12000}(""); - _treasuryAmount += (!sent) ? _tnftAmount : 0; - } - if (_treasuryAmount > 0) { - (sent, ) = _treasury.call{value: _treasuryAmount, gas: 2300}(""); - require(sent, "ETH_SEND_FAILED"); - } - } //-------------------------------------------------------------------------------------- - //-------------------------------------- GETTER -------------------------------------- + //---------------------------- Eigenlayer Interactions -------------------------------- //-------------------------------------------------------------------------------------- - /// @notice Fetch the staking rewards accrued in the safe that can be paid out to (toNodeOperator, toTnft, toBnft, toTreasury) - /// @param _splits the splits for the staking rewards - /// - /// @return toNodeOperator the payout to the Node Operator - /// @return toTnft the payout to the T-NFT holder - /// @return toBnft the payout to the B-NFT holder - /// @return toTreasury the payout to the Treasury - function getRewardsPayouts( - uint32 _exitRequestTimestamp, - IEtherFiNodesManager.RewardsSplit memory _splits - ) public view returns (uint256, uint256, uint256, uint256) { - uint256 _balance = withdrawableBalanceInExecutionLayer(); - return _calculateSplits(_balance, _splits); - } - - /// @notice Compute the non exit penalty for the b-nft holder - /// @param _tNftExitRequestTimestamp the timestamp when the T-NFT holder asked the B-NFT holder to exit the node - /// @param _bNftExitRequestTimestamp the timestamp when the B-NFT holder submitted the exit request to the beacon network - function getNonExitPenalty( - uint32 _tNftExitRequestTimestamp, - uint32 _bNftExitRequestTimestamp - ) public view returns (uint256) { - if (_tNftExitRequestTimestamp == 0) return 0; - - uint128 _penaltyPrinciple = IEtherFiNodesManager(etherFiNodesManager).nonExitPenaltyPrincipal(); - uint64 _dailyPenalty = IEtherFiNodesManager(etherFiNodesManager).nonExitPenaltyDailyRate(); - uint256 daysElapsed = _getDaysPassedSince(_tNftExitRequestTimestamp, _bNftExitRequestTimestamp); - if (daysElapsed > 365) { - return _penaltyPrinciple; - } - - uint256 remaining = _penaltyPrinciple; - while (daysElapsed > 0) { - uint256 exponent = Math.min(7, daysElapsed); - remaining = (remaining * (10000 - uint256(_dailyPenalty)) ** exponent) / (10000 ** exponent); - daysElapsed -= Math.min(7, daysElapsed); - } - - return _penaltyPrinciple - remaining; + /// @dev returns the associated eigenpod or the zero address if it is not created yet. + /// `EigenPodManager.getPod()` is not used because it returns the deterministic address regardless of if it exists + function getEigenPod() public view returns (IEigenPod) { + return eigenPodManager.ownerToPod(address(this)); } - /// @notice total balance (in the execution layer) of this withdrawal safe split into its component parts. - /// 1. the withdrawal safe balance - /// 2. the EigenPod balance - /// 3. (DEPRECATED) the withdrawals pending in the DelayedWithdrawalRouter - function splitBalanceInExecutionLayer() public view returns (uint256 _withdrawalSafe, uint256 _eigenPod, uint256 _withdrawalQueue) { - _withdrawalSafe = address(this).balance; - - if (isRestakingEnabled) { - // because eigenlayer locks slashed eth in the pod instead of burning it - // this balance will have a slightly different meaning in the presence of slashing. - // We are not currently allocated to any slashing AVS so this is fine for now and - // we will have a more holistic solution in the next upgrade - _eigenPod = eigenPod.balance; - } - - // eth no longer actually moves to the withdrawal queue unlike the old delayedWithdrawalRouter - _withdrawalQueue = 0; - - return (_withdrawalSafe, _eigenPod, _withdrawalQueue); + function createEigenPod() external { + eigenPodManager.createPod(); } - /// @notice total balance (wei) of this safe currently in the execution layer. - function totalBalanceInExecutionLayer() public view returns (uint256) { - (uint256 _safe, uint256 _pod, uint256 _withdrawal_queue) = splitBalanceInExecutionLayer(); - return _safe + _pod + _withdrawal_queue; + // TODO(dave): permissions + function startCheckpoint() external { + bool revertIfNoBalance = true; // protect from wasting gas if checkpoint will not increase shares + getEigenPod().startCheckpoint(revertIfNoBalance); } - /// @notice balance (wei) of this safe that could be immediately withdrawn - // This withdrawable balance amount is updated after completion of the queued withdrawal with `receiveAsToken = True` - function withdrawableBalanceInExecutionLayer() public view returns (uint256) { - uint256 safeBalance = address(this).balance; - return safeBalance; + // TODO(dave): permissions + function setProofSubmitter(address _newProofSubmitter) external { + getEigenPod().setProofSubmitter(_newProofSubmitter); } - function moveFundsToManager(uint256 _amount) external onlyEtherFiNodeManagerContract { - (bool sent, ) = etherFiNodesManager.call{value: _amount, gas: 6000}(""); - require(sent, "ETH_SEND_FAILED"); + // TODO(dave): permissions + function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot) { + // Implemented this way because we almost never queue multiple withdrawals at the same time + // so I chose to improve our internal interface and simplify testing + IDelegationManager.QueuedWithdrawalParams[] memory paramsArray = new IDelegationManager.QueuedWithdrawalParams[](1); + paramsArray[0] = params; + return delegationManager.queueWithdrawals(paramsArray)[0]; } - function getFullWithdrawalPayouts( - IEtherFiNodesManager.ValidatorInfo memory _info, - IEtherFiNodesManager.RewardsSplit memory _SRsplits - ) public view onlyEtherFiNodeManagerContract returns (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) { - if (version == 0 || numAssociatedValidators() == 1) { - return calculateTVL(0, _info, _SRsplits, true); - } else if (version == 1) { - // If (version ==1 && numAssociatedValidators() > 1) - // the full withdrwal for a validator only considers its principal amount (= 16 ether ~ 32 ether) - // the staking rewards remain in the safe contract - // Therefore, if a validator is slashed, the accrued staking rewards are used to cover the slashing amount - // In the upcoming version, the proof system will be ported so that the penalty amount properly considered for withdrawals + // TODO(dave): permissions + /// @dev the latest slashing release adds eigenPodManager.getQueuedWithdrawals which allows us + /// to complete withdrawals without needing an external indexer to track the queued withdrawal params + function completeQueuedWithdrawals(bool receiveAsTokens) external { - uint256[] memory payouts = new uint256[](4); // (toNodeOperator, toTnft, toBnft, toTreasury) - uint256 principal = (withdrawableBalanceInExecutionLayer() >= 32 ether) ? 32 ether : withdrawableBalanceInExecutionLayer(); - (payouts[2], payouts[1]) = _calculatePrincipals(principal); - (payouts[0], payouts[1], payouts[2], payouts[3]) = _applyNonExitPenalty(_info, payouts[0], payouts[1], payouts[2], payouts[3]); + // because we are just dealing with beacon eth we don't need to populate the tokens[] array + IERC20[] memory tokens; - return (payouts[0], payouts[1], payouts[2], payouts[3]); - } else { - require(false, "WRONG_VERSION"); + //TODO: skip withdrawals that haven't had enough time pass yet + (IDelegationManager.Withdrawal[] memory queuedWithdrawals, ) = delegationManager.getQueuedWithdrawals(address(this)); + for (uint256 i = 0; i < queuedWithdrawals.length; i++) { + delegationManager.completeQueuedWithdrawal(queuedWithdrawals[i], tokens, receiveAsTokens); } - } - - /// @notice Given the current (phase, beacon balance) of a validator, compute the TVLs for {node operator, t-nft holder, b-nft holder, treasury} - function getTvlSplits( - VALIDATOR_PHASE _phase, - uint256 _beaconBalance, - bool _onlyWithdrawable - ) internal view returns (uint256 stakingRewards, uint256 principal) { - uint256 numValidators = numAssociatedValidators(); - if (numValidators == 0) return (0, 0); - - // Consider the total balance of the safe in the execution layer - uint256 balance = _onlyWithdrawable? withdrawableBalanceInExecutionLayer() : totalBalanceInExecutionLayer(); - - // Calculate the total principal for the exited validators. - // It must be in the range of [16 ether * numExitedValidators, 32 ether * numExitedValidators] - // since the maximum slashing amount is 16 ether per validator (without considering the slashing from restaking) - // - // Here, the accrued rewards in the safe are used to cover the loss from the slashing - // For example, say the safe had 1 ether accrued staking rewards, but the validator got slashed till 16 ether - // After exiting the validator, the safe balance becomes 17 ether (16 ether from the slashed validator, 1 ether was the accrued rewards), - // the accrued rewards are used to cover the slashing amount, thus, being considered as principal. - // While this is not the best way to handle it, we acknowledge it as a temporary solution until the more advanced & efficient method is implemented - require (balance >= 16 ether * numExitedValidators, "INSUFFICIENT_BALANCE"); - uint256 totalPrincipalForExitedValidators = 16 ether * numExitedValidators + Math.min(balance - 16 ether * numExitedValidators, 16 ether * numExitedValidators); - // The rewards in the safe are split equally among the associated validators - // The rewards in the beacon are considered as the staking rewards of the current validator being considered - uint256 stakingRewardsInEL = (balance - totalPrincipalForExitedValidators) / numValidators; - uint256 stakingRewardsInBeacon = (_beaconBalance > 32 ether ? _beaconBalance - 32 ether : 0); - stakingRewards = stakingRewardsInEL + stakingRewardsInBeacon; - - // The principal amount is computed - if (_phase == VALIDATOR_PHASE.EXITED) { - principal = totalPrincipalForExitedValidators / numExitedValidators; - require(_beaconBalance == 0, "Exited validator must have zero balanace in the beacon"); - } else if (_phase == VALIDATOR_PHASE.LIVE || _phase == VALIDATOR_PHASE.BEING_SLASHED) { - principal = _beaconBalance - stakingRewardsInBeacon; - } else { - require(false, "INVALID_PHASE"); + // if there are available rewards, forward them to the rewardsManager + if (address(this).balance > 0) { + //rewardsManager.depositETHRewards{value: address(this).balance}(); } - require(principal <= 32 ether && principal >= 16 ether, "INCORRECT_AMOUNT"); - } - - /// @notice Given - /// - the current balance of the validator in Consensus Layer (or Beacon) - /// - the current balance of the ether fi node contract, - /// Compute the TVLs for {node operator, t-nft holder, b-nft holder, treasury} - /// @param _beaconBalance the balance of the validator in Consensus Layer - /// @param _SRsplits the splits for the Staking Rewards - /// - /// @return toNodeOperator the payout to the Node Operator - /// @return toTnft the payout to the T-NFT holder - /// @return toBnft the payout to the B-NFT holder - /// @return toTreasury the payout to the Treasury - function calculateTVL( - uint256 _beaconBalance, - IEtherFiNodesManager.ValidatorInfo memory _info, - IEtherFiNodesManager.RewardsSplit memory _SRsplits, - bool _onlyWithdrawable - ) public view onlyEtherFiNodeManagerContract returns (uint256, uint256, uint256, uint256) { - (uint256 stakingRewards, uint256 principal) = getTvlSplits(_info.phase, _beaconBalance, _onlyWithdrawable); - if (stakingRewards + principal == 0) return (0, 0, 0, 0); - - // Compute the payouts for the staking rewards - uint256[] memory payouts = new uint256[](4); // (toNodeOperator, toTnft, toBnft, toTreasury) - (payouts[0], payouts[1], payouts[2], payouts[3]) = _calculateSplits(stakingRewards, _SRsplits); - - // Compute the payouts for the principals to {B, T}-NFTs - (uint256 toBnftPrincipal, uint256 toTnftPrincipal) = _calculatePrincipals(principal); - payouts[1] += toTnftPrincipal; - payouts[2] += toBnftPrincipal; - - // Apply the non-exit penalty to the B-NFT - (payouts[0], payouts[1], payouts[2], payouts[3]) = _applyNonExitPenalty(_info, payouts[0], payouts[1], payouts[2], payouts[3]); - - require(payouts[0] + payouts[1] + payouts[2] + payouts[3] == stakingRewards + principal, "INCORRECT_AMOUNT"); - return (payouts[0], payouts[1], payouts[2], payouts[3]); } //-------------------------------------------------------------------------------------- //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- - function callEigenPod(bytes calldata _data) external onlyEtherFiNodeManagerContract returns (bytes memory) { - return Address.functionCall(eigenPod, _data); - } - - function forwardCall(address _to, bytes calldata _data) external onlyEtherFiNodeManagerContract returns (bytes memory) { - return Address.functionCall(_to, _data); - } - - //-------------------------------------------------------------------------------------- - //------------------------------- INTERNAL FUNCTIONS --------------------------------- - //-------------------------------------------------------------------------------------- - - function _applyNonExitPenalty( - IEtherFiNodesManager.ValidatorInfo memory _info, - uint256 _toNodeOperator, - uint256 _toTnft, - uint256 _toBnft, - uint256 _toTreasury - ) internal view returns (uint256, uint256, uint256, uint256) { - // NonExitPenalty grows till 1 ether - uint256 bnftNonExitPenalty = getNonExitPenalty(_info.exitRequestTimestamp, _info.exitTimestamp); - uint256 appliedPenalty = Math.min(_toBnft, bnftNonExitPenalty); - uint256 incentiveToNoToExitValidator = Math.min(appliedPenalty, 0.2 ether); - - // Cap the incentive to the operator under 0.2 ether. - // the rest (= penalty - incentive to NO) goes to the treasury - _toNodeOperator += incentiveToNoToExitValidator; - _toTreasury += appliedPenalty - incentiveToNoToExitValidator; - _toBnft -= appliedPenalty; - - return (_toNodeOperator, _toTnft, _toBnft, _toTreasury); - } - - /// @notice Calculates values for payouts based on certain parameters - /// @param _totalAmount The total amount to split - /// @param _splits The splits for the staking rewards - /// - /// @return toNodeOperator the payout to the Node Operator - /// @return toTnft the payout to the T-NFT holder - /// @return toBnft the payout to the B-NFT holder - /// @return toTreasury the payout to the Treasury - function _calculateSplits( - uint256 _totalAmount, - IEtherFiNodesManager.RewardsSplit memory _splits - ) internal pure returns (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) { - uint256 scale = _splits.treasury + _splits.nodeOperator + _splits.tnft + _splits.bnft; - toNodeOperator = (_totalAmount * _splits.nodeOperator) / scale; - toTnft = (_totalAmount * _splits.tnft) / scale; - toBnft = (_totalAmount * _splits.bnft) / scale; - toTreasury = _totalAmount - (toBnft + toTnft + toNodeOperator); - return (toNodeOperator, toTnft, toBnft, toTreasury); - } - - /// @notice Calculate the principal for the T-NFT and B-NFT holders based on the balance - /// @param _balance The balance of the node - /// @return toBnftPrincipal the principal for the B-NFT holder - /// @return toTnftPrincipal the principal for the T-NFT holder - function _calculatePrincipals( - uint256 _balance - ) internal pure returns (uint256 , uint256) { - // Check if the ETH principal withdrawn (16 ETH ~ 32 ETH) from beacon is within this contract - // If not: - // - case 1: ETH is still in the EigenPod contract. Need to get that out - // - case 2: ETH is withdrawn from the EigenPod contract, but ETH got slashed and the amount is under 16 ETH - // Note that the case 2 won't happen until EigenLayer's AVS goes live on mainnet and the slashing mechanism is added - // We will need upgrades again once EigenLayer's AVS goes live - require(_balance >= 16 ether && _balance <= 32 ether, "INCORRECT_PRINCIPAL_AMOUNT"); - - uint256 toBnftPrincipal = (_balance >= 31 ether) ? _balance - 30 ether : 1 ether; - uint256 toTnftPrincipal = _balance - toBnftPrincipal; - return (toBnftPrincipal, toTnftPrincipal); - } - - function _getDaysPassedSince( - uint32 _startTimestamp, - uint32 _endTimestamp - ) public pure returns (uint256) { - uint256 timeElapsed = _endTimestamp - Math.min(_startTimestamp, _endTimestamp); - return uint256(timeElapsed / (24 * 3_600)); - } - - /// @dev implementation address for beacon proxy. - /// https://docs.openzeppelin.com/contracts/3.x/api/proxy#beacon - function implementation() external view returns (address) { - bytes32 slot = bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1); - address implementationVariable; - assembly { - implementationVariable := sload(slot) - } - - IBeacon beacon = IBeacon(implementationVariable); - return beacon.implementation(); - } - - - //-------------------------------------------------------------------------------------- - //----------------------------------- RESTAKING -------------------------------------- - //-------------------------------------------------------------------------------------- - - /// @notice Start a PEPE pod checkpoint balance proof. A new proof cannot be started until - /// the previous proof is completed - function startCheckpoint(bool _revertIfNoBalance) external onlyEtherFiNodeManagerContract { - IEigenPod(eigenPod).startCheckpoint(_revertIfNoBalance); - } - - // @notice you can delegate 1 additional wallet that is allowed to call startCheckpoint() and - // verifyWithdrawalCredentials() on behalf of this pod - function setProofSubmitter(address _newProofSubmitter) external onlyEtherFiNodeManagerContract { - IEigenPod(eigenPod).setProofSubmitter(_newProofSubmitter); - } - - /// @notice create a new eigenPod associated with this withdrawal safe - /// @dev to take advantage of restaking via eigenlayer the validator associated with this - /// withdrawal safe must set their withdrawalCredentials to point to this eigenPod - /// and not to the withdrawal safe itself - function createEigenPod() public { - if (eigenPod != address(0x0)) return; // already have pod - IEigenPodManager eigenPodManager = IEigenPodManager(IEtherFiNodesManager(etherFiNodesManager).eigenPodManager()); - eigenPod = eigenPodManager.createPod(); - emit EigenPodCreated(address(this), eigenPod); - } - - // per Validator, queue the withdrawal of native ETH from EigenPod to EtherFiNode - // Pre-Requisite: - // - the ETH was withdrawn from the beacon chain to the EigenPod - // - the PEPE proofs were submitted and verified - // returns the withdrawal roots for the queued full-withdrawals - function queueEigenpodFullWithdrawal() public onlyEtherFiNodeManagerContract returns (bytes32[] memory fullWithdrawalRoots) { - return _queueEigenpodFullWithdrawal(); - } - - function _queueEigenpodFullWithdrawal() private returns (bytes32[] memory fullWithdrawalRoots) { - if (!isRestakingEnabled) return fullWithdrawalRoots; - - // Withdrawals from Eigenlayer are queued through the DelegationManager - IDelegationManager delegationManager = IEtherFiNodesManager(etherFiNodesManager).delegationManager(); - - // get the withdrawable amount - IStrategy[] memory strategies = getStrategies(); - (uint256[] memory withdrawableShares, uint256[] memory depositShares) = delegationManager.getWithdrawableShares(address(this), strategies); - - // calculate the amount to withdraw: - // if the withdrawal is for the last validator: - // withdraw the full balance including staking rewards - // else - // as this is per validator withdrawal, we cap the withdrawal amount to 32 ether - // in the case of slashing, the withdrawals for the last few validators might have less than 32 ether - // - // TODO: revisit for Pectra where a validator can have more than 32 ether - uint256 depositSharesToWithdraw; - - if (numAssociatedValidators() == 1) { - require(IEigenPod(eigenPod).activeValidatorCount() == 0, "ACTIVE_VALIDATOR_EXISTS"); - depositSharesToWithdraw = depositShares[0]; - } else { - depositSharesToWithdraw = 32 ether; - } - - // Queue the withdrawal - // Note that the actual withdarwal amount can change if the slashing happens - IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManagerTypes.QueuedWithdrawalParams[](1); - uint256[] memory shares = new uint256[](1); + // TODO(dave): Permissions - strategies[0] = delegationManager.beaconChainETHStrategy(); - shares[0] = depositSharesToWithdraw; - params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ - strategies: strategies, - depositShares: shares, - __deprecated_withdrawer: address(this) - }); - - return delegationManager.queueWithdrawals(params); - } - - function getStrategies() public view returns (IStrategy[] memory) { - IDelegationManager delegationManager = IEtherFiNodesManager(etherFiNodesManager).delegationManager(); - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = delegationManager.beaconChainETHStrategy(); - return strategies; - } - - function validatePhaseTransition(VALIDATOR_PHASE _currentPhase, VALIDATOR_PHASE _newPhase) public pure returns (bool) { - bool pass; - - // Transition rules - if (_currentPhase == VALIDATOR_PHASE.NOT_INITIALIZED) { - pass = (_newPhase == VALIDATOR_PHASE.STAKE_DEPOSITED); - } else if (_currentPhase == VALIDATOR_PHASE.STAKE_DEPOSITED) { - pass = (_newPhase == VALIDATOR_PHASE.LIVE || _newPhase == VALIDATOR_PHASE.NOT_INITIALIZED || _newPhase == VALIDATOR_PHASE.WAITING_FOR_APPROVAL); - } else if (_currentPhase == VALIDATOR_PHASE.WAITING_FOR_APPROVAL) { - pass = (_newPhase == VALIDATOR_PHASE.LIVE || _newPhase == VALIDATOR_PHASE.NOT_INITIALIZED); - } else if (_currentPhase == VALIDATOR_PHASE.LIVE) { - pass = (_newPhase == VALIDATOR_PHASE.EXITED || _newPhase == VALIDATOR_PHASE.BEING_SLASHED); - } else if (_currentPhase == VALIDATOR_PHASE.BEING_SLASHED) { - pass = (_newPhase == VALIDATOR_PHASE.EXITED); - } else if (_currentPhase == VALIDATOR_PHASE.EXITED) { - pass = (_newPhase == VALIDATOR_PHASE.FULLY_WITHDRAWN); - } else { - pass = false; - } - - require(pass, "INVALID_PHASE_TRANSITION"); - return pass; - } - - function _onlyEtherFiNodeManagerContract() internal view { - require(msg.sender == etherFiNodesManager, "INCORRECT_CALLER"); - } - - //-------------------------------------------------------------------------------------- - //--------------------------------- Signatures- -------------------------------------- - //-------------------------------------------------------------------------------------- - - /** - * @dev Should return whether the signature provided is valid for the provided data - * @param _digestHash Hash of the data to be signed - * @param _signature Signature byte array associated with _data - */ - function isValidSignature(bytes32 _digestHash, bytes memory _signature) public view override returns (bytes4 magicValue) { - (address signer, ) = ECDSA.tryRecover(_digestHash, _signature); - bool isAdmin = IEtherFiNodesManager(etherFiNodesManager).operatingAdmin(signer); - return isAdmin ? this.isValidSignature.selector : bytes4(0xffffffff); + function callEigenPod(bytes calldata data) external returns (bytes memory) { + // callContract will revert if targeting an EOA so it is safe if getEigenPod() returns the zero address + return LibCall.callContract(address(getEigenPod()), 0, data); } - //-------------------------------------------------------------------------------------- - //----------------------------------- MODIFIERS -------------------------------------- - //-------------------------------------------------------------------------------------- - - modifier onlyEtherFiNodeManagerContract() { - _onlyEtherFiNodeManagerContract(); - _; + function forwardExternalCall(address to, bytes calldata data) external returns (bytes memory) { + return LibCall.callContract(to, 0, data); } - modifier ensureLatestVersion() { - require(version == 1, "NEED_TO_MIGRATE"); - _; - } } diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index bc9584b87..a8651cc8d 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -27,6 +27,8 @@ contract EtherFiNodesManager is //-------------------------------------------------------------------------------------- //--------------------------------- STATE-VARIABLES ---------------------------------- //-------------------------------------------------------------------------------------- + LegacyManagerState public legacyState; + /* uint64 public numberOfValidators; // # of validators in LIVE or WAITING_FOR_APPROVAL phases uint64 public nonExitPenaltyPrincipal; uint64 public nonExitPenaltyDailyRate; // in basis points @@ -70,6 +72,22 @@ contract EtherFiNodesManager is mapping(bytes4 => bool) public allowedForwardedEigenpodCalls; // function -> target_address -> allowed mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; + */ + + IEigenPodManager public immutable eigenPodManager; + IDelegationManager public immutable delegationManager; + IStakingManager public immutable stakingManager; + + //----------------------------------------------------------------- + //----------------------- Storage ------------------------------- + //----------------------------------------------------------------- + + mapping(bytes32 => IEtherFiNode) public etherFiNodeFromPubkeyHash; + + // Call Forwarding: functionSignature -> allowed + mapping(bytes4 => bool) public allowedForwardedEigenpodCalls; + // Call Forwarding: functionSignature -> targetAddress -> allowed + mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; //-------------------------------------------------------------------------------------- //------------------------------------- EVENTS --------------------------------------- @@ -82,24 +100,164 @@ contract EtherFiNodesManager is event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event QueuedRestakingWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, bytes32[] withdrawalRoots); - event AllowedForwardedExternalCallsUpdated(bytes4 indexed selector, address indexed _target, bool _allowed); - event AllowedForwardedEigenpodCallsUpdated(bytes4 indexed selector, bool _allowed); - - //-------------------------------------------------------------------------------------- - //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ - //-------------------------------------------------------------------------------------- - /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + receive() external payable {} error InvalidParams(); + + + function etherFiNodeFromId(uint256 id) public view returns (address) { + // if the ID is a legacy validatorID use the old storage array + // otherwise assume it is a pubkey hash. + // In a future upgrade we can fully remove the legacy path + + // heuristic that if a pubkey hash, at least 1 bit of higher order bits must be 1 + // all of the legacy id's were incrementing integers that will not have those bits set + uint256 mask = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000; + if (mask & id > 0) { + return address(etherFiNodeFromPubkeyHash[bytes32(id)]); + } else { + return legacyState.etherfiNodeAddress[id]; + } + } + + function getEigenPod(uint256 id) public view returns (address) { + IEtherFiNode node = IEtherFiNode(etherFiNodeFromId(id)); + return address(node.getEigenPod()); + } + + //------------------------------------------------------------------- + //--------------------- Key Management ---------------------------- + //------------------------------------------------------------------- + + ///@notice Calculates the pubkey hash of a validator's pubkey as per SSZ spec + function calculateValidatorPubkeyHash(bytes memory pubkey) public pure returns (bytes32) { + require(pubkey.length == 48, InvalidPubKeyLength()); + return sha256(abi.encodePacked(pubkey, bytes16(0))); + } + + /// @notice converts a target address to 0x01 withdrawal credential format + function addressToWithdrawalCredentials(address addr) public pure returns (bytes memory) { + return abi.encodePacked(bytes1(0x01), bytes11(0x0), addr); + } + + /* + function getDeterministicNodeAddress(uint256 nodeNonce) public view returns (address) { + return Create2.computeAddress( + bytes32(nodeNonce), //salt + keccak256(abi.encodePacked(beaconProxyBytecode, abi.encode(podOwnerBeacon, ""))) //bytecode + ); + } + + function getWithdrawalCredentials(uint256 nodeNonce) public view returns (bytes memory) { + // this is fine even if the node doesn't exist yet + address nodeAddress = getDeterministicNodeAddress(nodeNonce); + return addressToWithdrawalCredentials(getDeterministicEigpenpodAddress(nodeAddress)); + } + */ + error AlreadyLinked(); + error InvalidPubKeyLength(); + error InvalidCaller(); + error InvalidPubKeyLength(); + event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, bytes pubkey); + event NodeDeployed(address indexed nodeAddress, uint256 indexed nodeNonce); + + function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external { + if (msg.sender != address(stakingManager)) revert InvalidCaller(); + bytes32 pubkeyHash = calculateValidatorPubkeyHash(pubkey); + if (address(etherFiNodeFromPubkeyHash[pubkeyHash]) != address(0)) revert AlreadyLinked(); + if (legacyState.etherfiNodeAddress[legacyId] != address(0)) revert AlreadyLinked(); + + // link legacyId for now. We can remove this in a future upgrade + legacyState.etherfiNodeAddress[legacyId] = nodeAddress; + + etherFiNodeFromPubkeyHash[pubkeyHash] = IEtherFiNode(nodeAddress); + emit PubkeyLinked(pubkeyHash, nodeAddress, pubkey); + } + + + //-------------------------------------------------------------------------------------- + //-------------------------------- CALL FORWARDING ------------------------------------ + //-------------------------------------------------------------------------------------- + event AllowedForwardedExternalCallsUpdated(bytes4 indexed selector, address indexed _target, bool _allowed); + event AllowedForwardedEigenpodCallsUpdated(bytes4 indexed selector, bool _allowed); + error ForwardedCallNotAllowed(); error InvalidForwardedCall(); + /// @notice Update the whitelist for external calls that can be executed by an EtherfiNode + /// @param selector method selector + /// @param target call target for forwarded call + /// @param allowed enable or disable the call + function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external { + allowedForwardedExternalCalls[selector][target] = allowed; + emit AllowedForwardedExternalCallsUpdated(selector, target, allowed); + } + + /// @notice Update the whitelist for external calls that can be executed against the corresponding eigenpod + /// @param selector method selector + /// @param allowed enable or disable the call + function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external { + allowedForwardedEigenpodCalls[selector] = allowed; + emit AllowedForwardedEigenpodCallsUpdated(selector, allowed); + } + + function batchForwardEigenpodCall(bytes32[] calldata pubkeys, bytes[] calldata data) external returns (bytes[] memory returnData) { + returnData = new bytes[](pubkeys.length); + for (uint256 i = 0; i < pubkeys.length; i++) { + + // validate the call + if (data[i].length < 4) revert InvalidForwardedCall(); + bytes4 selector = bytes4(data[i][:4]); + if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); + + returnData[i] = etherFiNodeFromPubkeyHash[pubkeys[i]].callEigenPod(data[i]); + } + } + + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { + returnData = new bytes[](nodes.length); + for (uint256 i = 0; i < nodes.length; i++) { + + // validate the call + if (data[i].length < 4) revert InvalidForwardedCall(); + bytes4 selector = bytes4(data[i][:4]); + if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); + + returnData[i] = IEtherFiNode(nodes[i]).forwardExternalCall(target, data[i]); + } + } + + function forwardExternalCall(bytes32[] calldata pubkeys, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { + returnData = new bytes[](pubkeys.length); + for (uint256 i = 0; i < pubkeys.length; i++) { + + // validate the call + if (data[i].length < 4) revert InvalidForwardedCall(); + bytes4 selector = bytes4(data[i][:4]); + if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); + + returnData[i] = etherFiNodeFromPubkeyHash[pubkeys[i]].forwardExternalCall(target, data[i]); + } + } + + /* + function forwardExternalCall(bytes32 pubkey, bytes calldata data, address target) external returns (bytes memory returnData) { + bytes32[] memory pubkeys = new bytes32[](1); + bytes[] calldata datas = new bytes[](1); + return forwardExternalCall(pubkeys, datas, target)[0]; + } + */ + + + /* + /// @dev Sets the revenue splits on deployment /// @dev AuctionManager, treasury and deposit contracts must be deployed first /// @param _treasuryContract The address of the treasury contract for interaction @@ -781,4 +939,5 @@ contract EtherFiNodesManager is _operatingAdmin(); _; } + */ } diff --git a/src/LiquidityPool.sol b/src/LiquidityPool.sol index e0e937593..29510cbf9 100644 --- a/src/LiquidityPool.sol +++ b/src/LiquidityPool.sol @@ -374,6 +374,9 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL /// Until then the 1 ETH is considered as a loss /// Be careful not to cancel the registration after the approval phase function batchCancelDeposit(uint256[] calldata _validatorIds) external whenNotPaused { + + // TODO(dave): how should we change cancel with new lifecycle changes + /* address bnftHolder = address(this); for (uint256 i = 0; i < _validatorIds.length; i++) { @@ -384,6 +387,7 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL } else numPendingDeposits -= 1; } + */ stakingManager.batchCancelDepositAsBnftHolder(_validatorIds, msg.sender); } @@ -410,11 +414,16 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL emit ValidatorSpawnerUnregistered(_user); } + // TODO(dave): convert to pubkeyHash? + event ValidatorExitRequested(uint256 indexed validatorId); + /// @notice Send the exit requests as the T-NFT holder of the LiquidityPool validators function sendExitRequests(uint256[] calldata _validatorIds) external { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - - nodesManager.batchSendExitRequest(_validatorIds); + + for (uint256 i = 0; i < _validatorIds.length; i++) { + emit ValidatorExitRequested(_validatorIds[i]); + } } /// @notice Rebase by ether.fi diff --git a/src/NFTExchange.sol b/src/NFTExchange.sol index 2d3d98fba..ffecb370b 100644 --- a/src/NFTExchange.sol +++ b/src/NFTExchange.sol @@ -86,7 +86,8 @@ contract NFTExchange is Initializable, OwnableUpgradeable, ReentrancyGuardUpgrad require(msg.sender == reservedBuyers[mNftTokenId], "You are not the reserved buyer"); require(tnftTokenId == targetTNftTokenIds[mNftTokenId], "The T-NFT is not the target"); - require(nodesMgr.phase(tnftTokenId) == IEtherFiNode.VALIDATOR_PHASE.LIVE, "The validator is not LIVE"); + // TODO(dave): I think we can delete this entire contract? + // require(nodesMgr.phase(tnftTokenId) == IEtherFiNode.VALIDATOR_PHASE.LIVE, "The validator is not LIVE"); reservedBuyers[mNftTokenId] = address(0); targetTNftTokenIds[mNftTokenId] = 0; diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 9ba233f08..c0ae95554 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -35,6 +35,7 @@ contract StakingManager is UUPSUpgradeable { + /* uint128 public maxBatchDepositSize; uint128 public stakeAmount; @@ -56,16 +57,32 @@ contract StakingManager is address public DEPRECATED_admin; address public nodeOperatorManager; mapping(address => bool) public admins; + */ - //-------------------------------------------------------------------------------------- - //------------------------------------- EVENTS --------------------------------------- - //-------------------------------------------------------------------------------------- + // TODO(dave): fix storage shift + UpgradeableBeacon private upgradableBeacon; + address public etherFiNodeImplementation; + + IEtherFiNodesManager public immutable etherFiNodesManager; + IDepositContract public immutable depositContractEth2; + IAuctionManager public immutable auctionManager; + ITNFT public immutable tnft; + IBNFT public immutable bnft; + address public immutable etherfiOracle; + + error InvalidCaller(); + error UnlinkedPubkey(); + error IncorrectBeaconRoot(); + error InvalidPubKeyLength(); + error InvalidDepositData(); + error InactiveBid(); + error IncorrectValidatorFunds(); + error InvalidEtherFiNode(); + + event validatorCreated(bytes32 indexed pubkeyHash, address indexed etherFiNode, bytes pubkey); + event validatorConfirmed(bytes32 indexed pubkeyHash, address indexed bnftRecipient, address indexed tnftRecipient, bytes pubkey); + event linkLegacyValidatorId(bytes32 indexed pubkeyHash, uint256 indexed legacyId); - event StakeDeposit(address indexed staker, uint256 indexed bidId, address indexed withdrawSafe, bool restaked); - event DepositCancelled(uint256 id); - event ValidatorRegistered(address indexed operator, address indexed bNftOwner, address indexed tNftOwner, - uint256 validatorId, bytes validatorPubKey, string ipfsHashForEncryptedValidatorKey); - event StakeSource(uint256 bidId, ILiquidityPool.SourceOfFunds source); //-------------------------------------------------------------------------------------- //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ @@ -76,233 +93,88 @@ contract StakingManager is _disableInitializers(); } - /// @notice Initialize to set variables on deployment - /// @dev Deploys NFT contracts internally to ensure ownership is set to this contract - /// @dev AuctionManager Contract must be deployed first - /// @param _auctionAddress The address of the auction contract for interaction - function initialize(address _auctionAddress, address _depositContractAddress) external initializer { - stakeAmount = 32 ether; - maxBatchDepositSize = 25; - isFullStakeEnabled = true; - - __Pausable_init(); - __Ownable_init(); - __UUPSUpgradeable_init(); - __ReentrancyGuard_init(); - - auctionManager = IAuctionManager(_auctionAddress); - depositContractEth2 = IDepositContract(_depositContractAddress); - } + // TODO(dave): are we using a slightly different proxy for this contract? - function initializeOnUpgrade(address _nodeOperatorManager, address _etherFiAdmin) external onlyOwner { - DEPRECATED_admin = address(0); - nodeOperatorManager = _nodeOperatorManager; - admins[_etherFiAdmin] = true; + /// @notice Fetches the address of the implementation contract currently being used by the proxy + /// @return the address of the currently used implementation contract + function getImplementation() external view returns (address) { + return _getImplementation(); } - /// @notice Allows depositing multiple stakes at once - /// @param _candidateBidIds IDs of the bids to be matched with each stake - /// @return Array of the bid IDs that were processed and assigned - function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, bool _enableRestaking) - external payable whenNotPaused nonReentrant returns (uint256[] memory) - { - require(isFullStakeEnabled, "DEPRECATED"); - require(msg.value > 0 && msg.value % stakeAmount == 0 && msg.value / stakeAmount > 0, "WRONG_STAKING_AMOUNT"); - uint256 numberOfDeposits = msg.value / stakeAmount; - require(_candidateBidIds.length >= numberOfDeposits && numberOfDeposits <= maxBatchDepositSize, "WRONG_PARAMS"); - require(auctionManager.numberOfActiveBids() >= numberOfDeposits, "NOT_ENOUGH_BIDS"); + /// @notice send 1 eth to deposit contract to create the validator. + /// The rest of the eth will not be sent until the oracle confirms the withdrawal credentials + function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode, address nodeOperator) external payable { + if (msg.sender != etherfiOracle) revert InvalidCaller(); + if (depositData.length != bidIds.length) revert InvalidDepositData(); + if (msg.value != 1 ether * depositData.length) revert IncorrectValidatorFunds(); + if (address(IEtherFiNode(etherFiNode).getEigenPod()) == address(0)) revert InvalidEtherFiNode(); - uint256[] memory processedBidIds = _processDeposits(_candidateBidIds, numberOfDeposits, msg.sender, msg.sender, msg.sender, ILiquidityPool.SourceOfFunds.DELEGATED_STAKING, _enableRestaking, 0); + // process each 1 eth deposit to create validators for later verification from oracle + for (uint256 i = 0; i < depositData.length; i++) { - uint256 unMatchedBidCount = numberOfDeposits - processedBidIds.length; - if (unMatchedBidCount > 0) { - _refundDeposit(msg.sender, stakeAmount * unMatchedBidCount); - } - - return processedBidIds; - } + // claim the bid + if (!auctionManager.isBidActive(bidIds[i])) revert InactiveBid(); + auctionManager.updateSelectedBidInformation(bidIds[i]); - /// @notice Allows depositing multiple stakes at once - /// @dev Function gets called from the liquidity pool as part of the BNFT staker flow - /// @param _candidateBidIds IDs of the bids to be matched with each stake - /// @param _staker the address of the BNFT player who originated the call to the LP - /// @param _source the staking type that the funds are sourced from (EETH / ETHER_FAN), see natspec for allocateSourceOfFunds() - /// @param _enableRestaking Eigen layer integration check to identify if restaking is possible - /// @param _validatorIdToShareWithdrawalSafe the validator ID to use for the withdrawal safe - /// @return Array of the bid IDs that were processed and assigned - function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators, address _staker, address _tnftHolder, address _bnftHolder, ILiquidityPool.SourceOfFunds _source, bool _enableRestaking, uint256 _validatorIdToShareWithdrawalSafe) - public whenNotPaused nonReentrant returns (uint256[] memory) - { - require(msg.sender == liquidityPoolContract, "Incorrect Caller"); - require(_candidateBidIds.length >= _numberOfValidators && _candidateBidIds.length <= maxBatchDepositSize, "WRONG_PARAMS"); - require(auctionManager.numberOfActiveBids() >= _numberOfValidators, "NOT_ENOUGH_BIDS"); - - return _processDeposits(_candidateBidIds, _numberOfValidators, _staker, _tnftHolder, _bnftHolder, _source, _enableRestaking, _validatorIdToShareWithdrawalSafe); - } + // verify deposit root + bytes memory withdrawalCredentials = etherFiNodesManager.addressToWithdrawalCredentials(etherFiNode); + bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, 1 ether); + if (computedDataRoot != depositData[i].depositDataRoot) revert IncorrectBeaconRoot(); - /// @notice Batch creates validator object, mints NFTs, sets NB variables and deposits into beacon chain - /// @param _depositRoot The fetched root of the Beacon Chain - /// @param _validatorId Array of IDs of the validator to register - /// @param _depositData Array of data structures to hold all data needed for depositing to the beacon chain - function batchRegisterValidators( - bytes32 _depositRoot, - uint256[] calldata _validatorId, - DepositData[] calldata _depositData - ) public whenNotPaused nonReentrant verifyDepositState(_depositRoot) { - require(isFullStakeEnabled, "DEPRECATED"); - require(_validatorId.length == _depositData.length && _validatorId.length <= maxBatchDepositSize, "WRONG_PARAMS"); - - for (uint256 x; x < _validatorId.length; ++x) { - require(bidIdToStakerInfo[_validatorId[x]].sourceOfFund == ILiquidityPool.SourceOfFunds.DELEGATED_STAKING, "Wrong flow"); - _registerValidator(_validatorId[x], msg.sender, msg.sender, _depositData[x], msg.sender, 32 ether); - } - } + // Link the pubkey to a node. Will revert if this pubkey is already registered to a different target + etherFiNodesManager.linkPubkeyToNode(depositData[i].publicKey, etherFiNode, bidIds[i]); - /// @notice Creates validator object, mints NFTs, sets NB variables and deposits 1 ETH into beacon chain - /// @dev Function gets called from the LP and is used in the BNFT staking flow - /// @param _depositRoot The fetched root of the Beacon Chain - /// @param _validatorId Array of IDs of the validator to register - /// @param _bNftRecipient Array of BNFT recipients - /// @param _tNftRecipient Array of TNFT recipients - /// @param _depositData Array of data structures to hold all data needed for depositing to the beacon chain - /// @param _staker address of the BNFT holder who initiated the transaction - function batchRegisterValidators( - bytes32 _depositRoot, - uint256[] calldata _validatorId, - address _bNftRecipient, - address _tNftRecipient, - DepositData[] calldata _depositData, - address _staker - ) public payable whenNotPaused nonReentrant verifyDepositState(_depositRoot) { - require(msg.sender == liquidityPoolContract, "INCORRECT_CALLER"); - require(_validatorId.length <= maxBatchDepositSize && _validatorId.length == _depositData.length && msg.value == _validatorId.length * 1 ether, "WRONG_PARAMS"); - - for (uint256 x; x < _validatorId.length; ++x) { - require(bidIdToStakerInfo[_validatorId[x]].sourceOfFund == ILiquidityPool.SourceOfFunds.EETH, "Wrong flow"); - _registerValidator(_validatorId[x], _bNftRecipient, _tNftRecipient, _depositData[x], _staker, 1 ether); - } - } - - /// @notice Approves validators and deposits the remaining 31 ETH into the beacon chain - /// @dev This gets called by the LP and only will only happen when the oracle has confirmed that the withdraw credentials for the - /// validators are correct. This prevents a front-running attack. - /// @param _validatorId validator IDs to approve - /// @param _pubKey the pubkeys for each validator - /// @param _signature the signature for the 31 ETH transaction which was submitted in the register phase - /// @param _depositDataRootApproval the deposit data root for the 31 ETH transaction which was submitted in the register phase - function batchApproveRegistration( - uint256[] memory _validatorId, - bytes[] calldata _pubKey, - bytes[] calldata _signature, - bytes32[] calldata _depositDataRootApproval - ) external payable { - require(msg.sender == liquidityPoolContract, "INCORRECT_CALLER"); - - for (uint256 x; x < _validatorId.length; ++x) { - nodesManager.setValidatorPhase(_validatorId[x], IEtherFiNode.VALIDATOR_PHASE.LIVE); // Deposit to the Beacon Chain - bytes memory withdrawalCredentials = nodesManager.getWithdrawalCredentials(_validatorId[x]); - bytes32 beaconChainDepositRoot = depositRootGenerator.generateDepositRoot(_pubKey[x], _signature[x], withdrawalCredentials, 31 ether); - bytes32 registeredDataRoot = _depositDataRootApproval[x]; - require(beaconChainDepositRoot == registeredDataRoot, "WRONG_DEPOSIT_DATA_ROOT"); - depositContractEth2.deposit{value: 31 ether}(_pubKey[x], withdrawalCredentials, _signature[x], beaconChainDepositRoot); - } - } + depositContractEth2.deposit{value: 1 ether}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); - /// @notice Cancels a user's deposits - /// @param _validatorIds the IDs of the validators deposits to cancel - function batchCancelDeposit(uint256[] calldata _validatorIds) public whenNotPaused nonReentrant { - require(isFullStakeEnabled, "DEPRECATED"); - for (uint256 x; x < _validatorIds.length; ++x) { - require(bidIdToStakerInfo[_validatorIds[x]].sourceOfFund == ILiquidityPool.SourceOfFunds.DELEGATED_STAKING, "Wrong flow"); - _cancelDeposit(_validatorIds[x], msg.sender); + bytes32 pubkeyHash = calculateValidatorPubkeyHash(depositData[i].publicKey); + emit validatorCreated(pubkeyHash, etherFiNode, depositData[i].publicKey); + emit linkLegacyValidatorId(pubkeyHash, bidIds[i]); // can remove this once we fully transition to pubkeys } } - /// @notice Cancels deposits for validators registered in the BNFT flow - /// @dev Validators can be cancelled at any point before the full 32 ETH is deposited into the beacon chain. Validators which have - /// already gone through the 'registered' phase will lose 1 ETH which is stuck in the beacon chain and will serve as a penalty for - /// cancelling late. We need to update the number of validators each source has spun up to keep the target weight calculation correct. - /// @param _validatorIds validators to cancel - /// @param _caller address of the bNFT holder who initiated the transaction. Used for verification - function batchCancelDepositAsBnftHolder(uint256[] calldata _validatorIds, address _caller) public whenNotPaused nonReentrant { - require(msg.sender == liquidityPoolContract, "INCORRECT_CALLER"); - - for (uint256 x; x < _validatorIds.length; ++x) { - ILiquidityPool.SourceOfFunds source = bidIdToStakerInfo[_validatorIds[x]].sourceOfFund; - require(source != ILiquidityPool.SourceOfFunds.DELEGATED_STAKING, "Wrong flow"); - - if(nodesManager.phase(_validatorIds[x]) == IEtherFiNode.VALIDATOR_PHASE.WAITING_FOR_APPROVAL) { - uint256 nftTokenId = _validatorIds[x]; - TNFTInterfaceInstance.burnFromCancelBNftFlow(nftTokenId); - BNFTInterfaceInstance.burnFromCancelBNftFlow(nftTokenId); - } - - _cancelDeposit(_validatorIds[x], _caller); - } - } + /// @notice send 31 eth to activate validators created by "createBeaconValidators" + /// The oracle is expected to have confirmed the withdrawal credentials + function confirmAndFundBeaconValidators(DepositData[] calldata depositData, address BnftRecipient, address TnftRecipient) external payable { + if (msg.sender != etherfiOracle) revert InvalidCaller(); - /// @dev create a new proxy instance of the etherFiNode withdrawal safe contract. - /// @param _createEigenPod whether or not to create an associated eigenPod contract. - function instantiateEtherFiNode(bool _createEigenPod) external returns (address) { - require(msg.sender == address(nodesManager), "INCORRECT_CALLER"); + for (uint256 i = 0; i < depositData.length; i++) { - BeaconProxy proxy = new BeaconProxy(address(upgradableBeacon), ""); - address node = address(proxy); - IEtherFiNode(node).initialize(address(nodesManager)); - if (_createEigenPod) { - IEtherFiNode(node).createEigenPod(); - } - return node; - } + // check that withdrawal credentials for pubkey match what we originally intended + // It is expected that the oracle will not call the function for any key that was front-run + bytes32 pubkeyHash = calculateValidatorPubkeyHash(depositData[i].publicKey); + IEtherFiNode etherFiNode = etherFiNodesManager.etherFiNodeFromPubkeyHash(pubkeyHash); + if (address(etherFiNode) == address(0x0)) revert UnlinkedPubkey(); - error ALREADY_SET(); + // verify deposit root + bytes memory withdrawalCredentials = etherFiNodesManager.addressToWithdrawalCredentials(address(etherFiNode.getEigenPod())); + bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, 31 ether); + if (computedDataRoot != depositData[i].depositDataRoot) revert IncorrectBeaconRoot(); - /// @notice Sets the EtherFi node manager contract - /// @param _nodesManagerAddress address of the manager contract being set - function setEtherFiNodesManagerAddress(address _nodesManagerAddress) public onlyOwner { - if (address(nodesManager) != address(0)) revert ALREADY_SET(); - nodesManager = IEtherFiNodesManager(_nodesManagerAddress); - } + // Deposit the remaining 31 eth to activate validator + depositContractEth2.deposit{value: 31 ether}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); - /// @notice Sets the Liquidity pool contract address - /// @param _liquidityPoolAddress address of the liquidity pool contract being set - function setLiquidityPoolAddress(address _liquidityPoolAddress) public onlyOwner { - if (address(liquidityPoolContract) != address(0)) revert ALREADY_SET(); + // Use pubkey hash as the minted token ID + tnft.mint(BnftRecipient, uint256(pubkeyHash)); + bnft.mint(TnftRecipient, uint256(pubkeyHash)); - liquidityPoolContract = _liquidityPoolAddress; - } + emit validatorConfirmed(pubkeyHash, BnftRecipient, TnftRecipient, depositData[i].publicKey); + } - /// @notice Sets the max number of deposits allowed at a time - /// @param _newMaxBatchDepositSize the max number of deposits allowed - function setMaxBatchDepositSize(uint128 _newMaxBatchDepositSize) public onlyAdmin { - maxBatchDepositSize = _newMaxBatchDepositSize; + // TODO: emission of the ipfs key has been left out to await the decisions + // around validator key management changes, as they will effect this. } - function registerEtherFiNodeImplementationContract(address _etherFiNodeImplementationContract) public onlyOwner { - if (address(upgradableBeacon) != address(0) || address(implementationContract) != address(0)) revert ALREADY_SET(); - require(_etherFiNodeImplementationContract != address(0), "ZERO_ADDRESS"); - - implementationContract = _etherFiNodeImplementationContract; - upgradableBeacon = new UpgradeableBeacon(implementationContract); + ///@notice Calculates the pubkey hash of a validator's pubkey as per SSZ spec + function calculateValidatorPubkeyHash(bytes memory pubkey) public pure returns (bytes32) { + if (pubkey.length != 48) revert InvalidPubKeyLength(); + return sha256(abi.encodePacked(pubkey, bytes16(0))); } - /// @notice Instantiates the TNFT interface - /// @param _tnftAddress Address of the TNFT contract - function registerTNFTContract(address _tnftAddress) public onlyOwner { - if (address(TNFTInterfaceInstance) != address(0)) revert ALREADY_SET(); - TNFTInterfaceInstance = ITNFT(_tnftAddress); - } - /// @notice Instantiates the BNFT interface - /// @param _bnftAddress Address of the BNFT contract - function registerBNFTContract(address _bnftAddress) public onlyOwner { - if (address(BNFTInterfaceInstance) != address(0)) revert ALREADY_SET(); - - BNFTInterfaceInstance = IBNFT(_bnftAddress); - } + /////////////////////////////////////////////////////////////////////////////////////// /// @notice Upgrades the etherfi node /// @param _newImplementation The new address of the etherfi node @@ -310,201 +182,18 @@ contract StakingManager is require(_newImplementation != address(0), "ZERO_ADDRESS"); upgradableBeacon.upgradeTo(_newImplementation); - implementationContract = _newImplementation; - } - - function updateFullStakingStatus(bool _status) external onlyOwner { - isFullStakeEnabled = _status; + etherFiNodeImplementation = _newImplementation; } - function pauseContract() external onlyAdmin { _pause(); } - function unPauseContract() external onlyAdmin { _unpause(); } + // TODO(dave): reimplement pausing with role registry + function pauseContract() external { _pause(); } + function unPauseContract() external { _unpause(); } + /* + */ - /// @notice Updates the address of the admin - /// @param _address the new address to set as admin - function updateAdmin(address _address, bool _isAdmin) external onlyOwner { - require(_address != address(0), "ZERO_ADDRESS"); - admins[_address] = _isAdmin; - } - - function setNodeOperatorManager(address _nodeOperateManager) external onlyAdmin { - require(_nodeOperateManager != address(0), "ZERO_ADDRESS"); - nodeOperatorManager = _nodeOperateManager; - } //-------------------------------------------------------------------------------------- - //------------------------------- INTERNAL FUNCTIONS -------------------------------- - //-------------------------------------------------------------------------------------- - - function _processDeposits( - uint256[] calldata _candidateBidIds, - uint256 _numberOfDeposits, - address _staker, - address _tnftHolder, - address _bnftHolder, - ILiquidityPool.SourceOfFunds _source, - bool _enableRestaking, - uint256 _validatorIdToShareWithdrawalSafe - ) internal returns (uint256[] memory){ - uint256[] memory processedBidIds = new uint256[](_numberOfDeposits); - uint256 processedBidIdsCount = 0; - - for (uint256 i = 0; - i < _candidateBidIds.length && processedBidIdsCount < _numberOfDeposits; - ++i) { - uint256 bidId = _candidateBidIds[i]; - address bidStaker = bidIdToStakerInfo[bidId].staker; - address operator = auctionManager.getBidOwner(bidId); - if (bidStaker == address(0) && auctionManager.isBidActive(bidId)) { - // Verify the node operator who has been selected is approved to run validators using the specific source of funds. - // See more info in Node Operator manager around approving operators for different source types - require(_verifyNodeOperator(operator, _source), "INVALID_OPERATOR"); - auctionManager.updateSelectedBidInformation(bidId); - processedBidIds[processedBidIdsCount] = bidId; - processedBidIdsCount++; - _processDeposit(bidId, _staker, _tnftHolder, _bnftHolder, _enableRestaking, _source, _validatorIdToShareWithdrawalSafe); - } - } - - // resize the processedBidIds array to the actual number of processed bid IDs - assembly { - mstore(processedBidIds, processedBidIdsCount) - } - - return processedBidIds; - } - - /// @notice Creates validator object, mints NFTs, sets NB variables and deposits into beacon chain - /// @param _validatorId ID of the validator to register - /// @param _bNftRecipient The address to receive the minted B-NFT - /// @param _tNftRecipient The address to receive the minted T-NFT - /// @param _depositData Data structure to hold all data needed for depositing to the beacon chain - /// @param _staker User who has begun the registration chain of transactions - /// however, instead of the validator key, it will include the IPFS hash - /// containing the validator key encrypted by the corresponding node operator's public key - function _registerValidator( - uint256 _validatorId, - address _bNftRecipient, - address _tNftRecipient, - DepositData calldata _depositData, - address _staker, - uint256 _depositAmount - ) internal { - require(bidIdToStakerInfo[_validatorId].staker == _staker, "INCORRECT_CALLER"); - bytes memory withdrawalCredentials = nodesManager.getWithdrawalCredentials(_validatorId); - bytes32 depositDataRoot = depositRootGenerator.generateDepositRoot(_depositData.publicKey, _depositData.signature, withdrawalCredentials, _depositAmount); - require(depositDataRoot == _depositData.depositDataRoot, "WRONG_ROOT"); - - if(_tNftRecipient == liquidityPoolContract) { - // Deposits are split into two (1 ETH, 31 ETH). The latter is by the ether.fi Oracle - nodesManager.setValidatorPhase(_validatorId, IEtherFiNode.VALIDATOR_PHASE.WAITING_FOR_APPROVAL); - } else { - // Deposit 32 ETH at once - nodesManager.setValidatorPhase(_validatorId, IEtherFiNode.VALIDATOR_PHASE.LIVE); - } - - // Deposit to the Beacon Chain - depositContractEth2.deposit{value: _depositAmount}(_depositData.publicKey, withdrawalCredentials, _depositData.signature, depositDataRoot); - - nodesManager.incrementNumberOfValidators(1); - auctionManager.processAuctionFeeTransfer(_validatorId); - - // Let validatorId = nftTokenId - uint256 nftTokenId = _validatorId; - TNFTInterfaceInstance.mint(_tNftRecipient, nftTokenId); - BNFTInterfaceInstance.mint(_bNftRecipient, nftTokenId); - - emit ValidatorRegistered( - auctionManager.getBidOwner(_validatorId), - _bNftRecipient, - _tNftRecipient, - _validatorId, - _depositData.publicKey, - _depositData.ipfsHashForEncryptedValidatorKey - ); - } - - /// @notice Update the state of the contract now that a deposit has been made - /// @param _bidId The bid that won the right to the deposit - function _processDeposit(uint256 _bidId, address _staker, address _tnftHolder, address _bnftHolder, bool _enableRestaking, ILiquidityPool.SourceOfFunds _source, uint256 _validatorIdToShareWithdrawalSafe) internal { - bidIdToStakerInfo[_bidId] = StakerInfo(_staker, _source); - uint256 validatorId = _bidId; - - // register a withdrawalSafe for this bid/validator, creating a new one if necessary - address etherfiNode; - if (_validatorIdToShareWithdrawalSafe == 0) { - etherfiNode = nodesManager.allocateEtherFiNode(_enableRestaking); - } else { - require(TNFTInterfaceInstance.ownerOf(_validatorIdToShareWithdrawalSafe) == msg.sender, "WRONG_TNFT_OWNER"); // T-NFT owner must be the same - require(BNFTInterfaceInstance.ownerOf(_validatorIdToShareWithdrawalSafe) == _bnftHolder, "WRONG_BNFT_OWNER"); - require(auctionManager.getBidOwner(_validatorIdToShareWithdrawalSafe) == auctionManager.getBidOwner(_bidId), "WRONG_BID_OWNER"); - etherfiNode = nodesManager.etherfiNodeAddress(_validatorIdToShareWithdrawalSafe); - nodesManager.updateEtherFiNode(_validatorIdToShareWithdrawalSafe); - } - nodesManager.registerValidator(validatorId, _enableRestaking, etherfiNode); - - emit StakeDeposit(_staker, _bidId, etherfiNode, _enableRestaking); - emit StakeSource(_bidId, _source); - } - - /// @notice Cancels a users stake - /// @param _validatorId the ID of the validator deposit to cancel - function _cancelDeposit(uint256 _validatorId, address _caller) internal { - require(bidIdToStakerInfo[_validatorId].staker == _caller, "INCORRECT_CALLER"); - - bidIdToStakerInfo[_validatorId].staker = address(0); - nodesManager.unregisterValidator(_validatorId); - - // Call function in auction contract to re-initiate the bid that won - auctionManager.reEnterAuction(_validatorId); - - bool isFullStake = (msg.sender != liquidityPoolContract); - if (isFullStake) { - _refundDeposit(msg.sender, stakeAmount); - } - - emit DepositCancelled(_validatorId); - } - - /// @notice Refunds the depositor their staked ether for a specific stake - /// @dev called internally from cancelStakingManager or when the time runs out for calling registerValidator - /// @param _depositOwner address of the user being refunded - /// @param _amount the amount to refund the depositor - function _refundDeposit(address _depositOwner, uint256 _amount) internal { - uint256 balanace = address(this).balance; - (bool sent, ) = _depositOwner.call{value: _amount}(""); - require(sent && address(this).balance == balanace - _amount, "SendFail"); - } - - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} - - /// @notice Checks if an operator is approved for a specified source of funds - /// @dev Operators do not need to be approved for delegated_staking type - /// @param _operator address of the operator being checked - /// @param _source the source of funds the operator is being checked for - /// @return approved whether the operator is approved for the source type - function _verifyNodeOperator(address _operator, ILiquidityPool.SourceOfFunds _source) internal view returns (bool approved) { - if(uint256(ILiquidityPool.SourceOfFunds.DELEGATED_STAKING) == uint256(_source)) { - approved = true; - } else { - approved = INodeOperatorManager(nodeOperatorManager).isEligibleToRunValidatorsForSourceOfFund(_operator, _source); - } - } - - function _requireAdmin() internal view virtual { - require(admins[msg.sender], "NOT_ADMIN"); - } - - function _verifyDepositState(bytes32 _depositRoot) internal view virtual { - // disable deposit root check if none provided - if (_depositRoot != 0x0000000000000000000000000000000000000000000000000000000000000000) { - bytes32 onchainDepositRoot = depositContractEth2.get_deposit_root(); - require(_depositRoot == onchainDepositRoot, "DEPOSIT_ROOT_CHANGED"); - } - } - - //-------------------------------------------------------------------------------------- - //------------------------------------ GETTERS --------------------------------------- + //--------------------- EtherFiNode Beacon Proxy ---------------------------------- //-------------------------------------------------------------------------------------- /// @notice Fetches the address of the beacon contract for future EtherFiNodes (withdrawal safes) @@ -512,33 +201,9 @@ contract StakingManager is return address(upgradableBeacon); } - function bidIdToStaker(uint256 id) external view returns (address) { - return bidIdToStakerInfo[id].staker; - } - - /// @notice Fetches the address of the implementation contract currently being used by the proxy - /// @return the address of the currently used implementation contract - function getImplementation() external view returns (address) { - return _getImplementation(); - } - /// @notice Fetches the address of the implementation contract currently being used by the beacon proxy /// @return the address of the currently used implementation contract - function implementation() public view override returns (address) { + function getEtherFiNodeImplementation() public view override returns (address) { return upgradableBeacon.implementation(); } - - //-------------------------------------------------------------------------------------- - //----------------------------------- MODIFIERS -------------------------------------- - //-------------------------------------------------------------------------------------- - - modifier verifyDepositState(bytes32 _depositRoot) { - _verifyDepositState(_depositRoot); - _; - } - - modifier onlyAdmin() { - _requireAdmin(); - _; - } } diff --git a/src/TNFT.sol b/src/TNFT.sol index 19d7964e7..e5214e768 100644 --- a/src/TNFT.sol +++ b/src/TNFT.sol @@ -60,16 +60,6 @@ contract TNFT is ERC721Upgradeable, UUPSUpgradeable, OwnableUpgradeable { _burn(_validatorId); } - function _transfer( - address from, - address to, - uint256 tokenId - ) internal virtual override { - uint256 numAssociatedValidators = IEtherFiNodesManager(etherFiNodesManagerAddress).numAssociatedValidators(tokenId); - require(numAssociatedValidators == 1, "numAssociatedValidators != 1"); - - super._transfer(from, to, tokenId); - } //-------------------------------------------------------------------------------------- //------------------------------- INTERNAL FUNCTIONS -------------------------------- diff --git a/src/helpers/EtherFiViewer.sol b/src/helpers/EtherFiViewer.sol index b9a92942b..f945c2846 100644 --- a/src/helpers/EtherFiViewer.sol +++ b/src/helpers/EtherFiViewer.sol @@ -27,27 +27,19 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { } function _getDelegationManager() internal view returns (IDelegationManager) { - return nodesManager.delegationManager(); + return IDelegationManager(nodesManager.delegationManager()); } function _getEigenPodManager() internal view returns (IEigenPodManager) { - return nodesManager.eigenPodManager(); + return IEigenPodManager(nodesManager.eigenPodManager()); } function _getEtherFiNode(uint256 _validatorId) internal view returns (IEtherFiNode) { - return IEtherFiNode(nodesManager.etherfiNodeAddress(_validatorId)); + return IEtherFiNode(nodesManager.etherFiNodeFromId(_validatorId)); } function _getEigenPod(uint256 _validatorId) internal view returns (IEigenPod) { - return IEigenPod(_getEtherFiNode(_validatorId).eigenPod()); - } - - function EigenPod_hasRestaked(uint256[] memory _validatorIds) external view returns (bool[] memory _hasRestaked) { - _hasRestaked = new bool[](_validatorIds.length); - for (uint256 i = 0; i < _validatorIds.length; i++) { - // now every validator within eigenlayer is guaranteed to have this flag set - _hasRestaked[i] = _getEtherFiNode(_validatorIds[i]).isRestakingEnabled(); - } + return IEigenPod(_getEtherFiNode(_validatorId).getEigenPod()); } function EigenPod_withdrawableRestakedExecutionLayerGwei(uint256[] memory _validatorIds) external view returns (uint256[] memory _withdrawableRestakedExecutionLayerGwei) { @@ -112,6 +104,7 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { } } + /* function EtherFiNodesManager_splitBalanceInExecutionLayer(uint256[] memory _validatorIds) external view returns (uint256[] memory _withdrawalSafe, uint256[] memory _eigenPod, uint256[] memory _delayedWithdrawalRouter) { _withdrawalSafe = new uint256[](_validatorIds.length); _eigenPod = new uint256[](_validatorIds.length); @@ -129,13 +122,14 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { _withdrawableBalance[i] = _getEtherFiNode(_validatorIds[i]).withdrawableBalanceInExecutionLayer(); } } + */ function EtherFiNodesManager_aggregatedBalanceOfUnusedSafes() external view returns (uint256 total) { uint256 n = nodesManager.getUnusedWithdrawalSafesLength(); for (uint256 i = 0; i < n; i++) { address safe = nodesManager.unusedWithdrawalSafes(i); - address eigenpod = IEtherFiNode(safe).eigenPod(); + address eigenpod = address(IEtherFiNode(safe).getEigenPod()); total += safe.balance + eigenpod.balance; } } diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index 0b1945b30..38fd7b43b 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -46,6 +46,14 @@ interface IEtherFiNode { DEPRECATED_READY_FOR_DEPOSIT } + function startCheckpoint() external; + function setProofSubmitter(address _newProofSubmitter) external; + function createEigenPod() external; + function getEigenPod() external view returns (IEigenPod); + function callEigenPod(bytes memory data) external returns (bytes memory); + function forwardExternalCall(address to, bytes memory data) external returns (bytes memory); + + /* // VIEW functions function numAssociatedValidators() external view returns (uint256); function numExitRequestsByTnft() external view returns (uint16); @@ -100,4 +108,5 @@ interface IEtherFiNode { ) external; function moveFundsToManager(uint256 _amount) external; + */ } diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 6ffcbae15..a0716552f 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -7,10 +7,62 @@ import "./IEtherFiNode.sol"; import "../eigenlayer-interfaces/IEigenPodManager.sol"; import "../eigenlayer-interfaces/IDelegationManager.sol"; import "../eigenlayer-interfaces/IDelayedWithdrawalRouter.sol"; +import "../interfaces/IAuctionManager.sol"; +import "../interfaces/IEtherFiNode.sol"; +import "../interfaces/IEtherFiNodesManager.sol"; +import "../interfaces/IProtocolRevenueManager.sol"; +import "../interfaces/IStakingManager.sol"; + interface IEtherFiNodesManager { + struct LegacyManagerState { + uint64 numberOfValidators; // # of validators in LIVE or WAITING_FOR_APPROVAL phases + uint64 nonExitPenaltyPrincipal; + uint64 nonExitPenaltyDailyRate; // in basis points + uint64 SCALE; + + address treasuryContract; + address stakingManagerContract; + address DEPRECATED_protocolRevenueManagerContract; + + // validatorId == bidId -> withdrawalSafeAddress + mapping(uint256 => address) etherfiNodeAddress; + + address tnft; + address bnft; + IAuctionManager auctionManager; + IProtocolRevenueManager DEPRECATED_protocolRevenueManager; + + RewardsSplit stakingRewardsSplit; + RewardsSplit DEPRECATED_protocolRewardsSplit; + + address DEPRECATED_admin; + mapping(address => bool) admins; + + IEigenPodManager eigenPodManager; + IDelayedWithdrawalRouter delayedWithdrawalRouter; + // max number of queued eigenlayer withdrawals to attempt to claim in a single tx + uint8 maxEigenlayerWithdrawals; + + // stack of re-usable withdrawal safes to save gas + address[] unusedWithdrawalSafes; + + bool DEPRECATED_enableNodeRecycling; + + mapping(uint256 => ValidatorInfo) validatorInfos; + + IDelegationManager delegationManager; + + mapping(address => bool) operatingAdmin; + + // function -> allowed + mapping(bytes4 => bool) allowedForwardedEigenpodCalls; + // function -> target_address -> allowed + mapping(bytes4 => mapping(address => bool)) allowedForwardedExternalCalls; + } + struct ValidatorInfo { uint32 validatorIndex; uint32 exitRequestTimestamp; @@ -25,7 +77,26 @@ interface IEtherFiNodesManager { uint64 bnft; } + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); + function addressToWithdrawalCredentials(address addr) external pure returns (bytes memory); + function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; + function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); + + function getEigenPod(uint256 id) external view returns (address); + + function etherFiNodeFromId(uint256 id) public view returns (address); + + + function eigenPodManager() external view returns (address); + function delegationManager() external view returns (address); + + function pauseContract() external; + function unPauseContract() external; + + + // VIEW functions + /* function delayedWithdrawalRouter() external view returns (IDelayedWithdrawalRouter); function eigenPodManager() external view returns (IEigenPodManager); function delegationManager() external view returns (IDelegationManager); @@ -41,6 +112,7 @@ interface IEtherFiNodesManager { function getValidatorInfo(uint256 _validatorId) external view returns (ValidatorInfo memory); function numAssociatedValidators(uint256 _validatorId) external view returns (uint256); function phase(uint256 _validatorId) external view returns (IEtherFiNode.VALIDATOR_PHASE phase); + function getEigenPod(uint256 _validatorId) external view returns (IEigenPod); function generateWithdrawalCredentials(address _address) external view returns (bytes memory); function nonExitPenaltyDailyRate() external view returns (uint64); @@ -74,4 +146,5 @@ interface IEtherFiNodesManager { function updateAdmin(address _address, bool _isAdmin) external; function pauseContract() external; function unPauseContract() external; + */ } diff --git a/test/ContractCodeChecker.t.sol b/test/ContractCodeChecker.t.sol index db57effc7..aeb2a81f4 100644 --- a/test/ContractCodeChecker.t.sol +++ b/test/ContractCodeChecker.t.sol @@ -21,7 +21,7 @@ contract ContractCodeCheckerTest is TestSetup { EtherFiNodesManager etherFiNodesManagerImplementation = new EtherFiNodesManager(); address etherFiNodesManagerImplAddress = address(0xE9EE6923D41Cf5F964F11065436BD90D4577B5e4); - EtherFiNode etherFiNodeImplementation = new EtherFiNode(); + EtherFiNode etherFiNodeImplementation = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); address etherFiNodeImplAddress = address(0xc5F2764383f93259Fba1D820b894B1DE0d47937e); EtherFiRestaker etherFiRestakerImplementation = new EtherFiRestaker(address(0x7750d328b314EfFa365A0402CcfD489B80B0adda)); diff --git a/test/EigenLayerIntegration.t.sol b/test/EigenLayerIntegration.t.sol index a1c95808a..47500dd88 100644 --- a/test/EigenLayerIntegration.t.sol +++ b/test/EigenLayerIntegration.t.sol @@ -71,7 +71,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { vm.stopPrank(); EtherFiNodesManager newManagerImpl = new EtherFiNodesManager(); - EtherFiNode newNodeImpl = new EtherFiNode(); + EtherFiNode newNodeImpl = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); vm.startPrank(managerInstance.owner()); managerInstance.upgradeTo(address(newManagerImpl)); @@ -83,6 +83,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { } + /* function _setWithdrawalCredentialParams() public { validatorIndices = new uint40[](1); withdrawalCredentialProofs = new bytes[](1); @@ -140,37 +141,6 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { vm.stopPrank(); } - // https://holesky.beaconcha.in/validator/1644305#deposits - function test_verifyWithdrawalCredentials_32ETH() public { - - //vm.selectFork(vm.createFork(vm.envString("HISTORICAL_PROOF_RPC_URL"))); - - //int256 initialShares = eigenLayerEigenPodManager.podOwnerShares(podOwner); - //IEigenPod.ValidatorInfo memory validatorInfo = eigenPod.validatorPubkeyToInfo(pubkey); - //assertTrue(validatorInfo.status == IEigenPod.VALIDATOR_STATUS.INACTIVE, "Validator status should be INACTIVE"); - //assertEq(validatorInfo.validatorIndex, 0); - //assertEq(validatorInfo.restakedBalanceGwei, 0); - //assertEq(validatorInfo.mostRecentBalanceUpdateTimestamp, 0); - - //_beacon_process_32ETH_deposit(); - //console2.log("initialShares:", initialShares); - - //bytes4 selector = bytes4(keccak256("verifyWithdrawalCredentials(uint64,(bytes32,bytes),uint40[],bytes[],bytes32[][])")); - //bytes[] memory data = new bytes[](1); - //data[0] = abi.encodeWithSelector(selector, oracleTimestamp, stateRootProof, validatorIndices, withdrawalCredentialProofs, validatorFields); - //vm.prank(owner); - //managerInstance.forwardEigenpodCall(validatorIds, data); - - //int256 updatedShares = eigenLayerEigenPodManager.podOwnerShares(podOwner); - //console2.log("updatedShares:", updatedShares); - - //validatorInfo = eigenPod.validatorPubkeyToInfo(pubkey); - //assertEq(updatedShares, initialShares+32e18, "Shares should be 32 ETH in wei after verifying withdrawal credentials"); - //assertTrue(validatorInfo.status == IEigenPod.VALIDATOR_STATUS.ACTIVE, "Validator status should be ACTIVE"); - //assertEq(validatorInfo.validatorIndex, validatorIndices[0], "Validator index should be set"); - //assertEq(validatorInfo.restakedBalanceGwei, 32 ether / 1e9, "Restaked balance should be 32 eth"); - //assertEq(validatorInfo.mostRecentBalanceUpdateTimestamp, oracleTimestamp, "Most recent balance update timestamp should be set"); - } function test_verifyBalanceUpdates_FAIL_1() public { @@ -320,65 +290,6 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { } } - function test_completeQueuedWithdrawals_338_for_withdrawal_from_undelegate() public { - //uint256[] memory validatorIds = new uint256[](1); - //validatorIds[0] = 338; - //uint32[] memory timeStamps = new uint32[](1); - //timeStamps[0] = 0; - //address nodeAddress = managerInstance.etherfiNodeAddress(validatorIds[0]); - - //IDelegationManager mgr = managerInstance.delegationManager(); - - //// 1. completeQueuedWithdrawal - //// the withdrawal was queued by `undelegate` in https://etherscan.io/tx/0xd0e400ecd6711cf2f8e5ea97585c864db6d3ffb4d248d3e6d97a66b3683ec98b - //{ - //// - //// { - //// 'staker': '0x7aC9b51aB907715194F407C15191fce0F3771254', - //// 'delegatedTo': '0x5b9B3Cf0202a1a3Dc8f527257b7E6002D23D8c85', - //// 'withdrawer': '0x7aC9b51aB907715194F407C15191fce0F3771254', - //// 'nonce': 0, - //// 'startBlock': 19692808, - //// 'strategies': ['0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0'], - //// 'shares': [32000000000000000000] - //// } - //IDelegationManager.Withdrawal memory withdrawal; - //IERC20[] memory tokens = new IERC20[](1); - //IStrategy[] memory strategies = new IStrategy[](1); - //strategies[0] = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); - //uint256[] memory shares = new uint256[](1); - //shares[0] = 32000000000000000000; - //withdrawal = IDelegationManagerTypes.Withdrawal({ - //staker: 0x7aC9b51aB907715194F407C15191fce0F3771254, - //delegatedTo: 0x5b9B3Cf0202a1a3Dc8f527257b7E6002D23D8c85, - //withdrawer: 0x7aC9b51aB907715194F407C15191fce0F3771254, - //nonce: 0, - //startBlock: 19692808, - //strategies: strategies, - //scaledShares: shares - //}); - - //bytes32 withdrawalRoot = mgr.calculateWithdrawalRoot(withdrawal); - //assertTrue(mgr.pendingWithdrawals(withdrawalRoot)); - - //IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](1); - //uint256[] memory middlewareTimesIndexes = new uint256[](1); - //withdrawals[0] = withdrawal; - //middlewareTimesIndexes[0] = 0; - - //vm.prank(owner); - //vm.expectRevert(); - //EtherFiNode(payable(nodeAddress)).completeQueuedWithdrawals(withdrawals, middlewareTimesIndexes, false); - - //vm.prank(owner); - //vm.expectRevert(); - //managerInstance.completeQueuedWithdrawals(validatorIds, withdrawals, middlewareTimesIndexes, true); - - //vm.prank(owner); - //managerInstance.completeQueuedWithdrawals(validatorIds, withdrawals, middlewareTimesIndexes, false); - //} - } - function test_completeQueuedWithdrawals_338_e2e() public { uint256[] memory validatorIds = new uint256[](1); validatorIds[0] = 338; @@ -511,5 +422,6 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { verifyContractByteCodeMatch(etherFiNodeImplAddress, address(etherFiNodeImplementation)); verifyContractByteCodeMatch(etherFiRestakerImplAddress, address(etherFiRestakerImplementation)); } + */ } diff --git a/test/EtherFiNode.t.sol b/test/EtherFiNode.t.sol index bcc05672a..096556663 100644 --- a/test/EtherFiNode.t.sol +++ b/test/EtherFiNode.t.sol @@ -26,2683 +26,6 @@ interface IEigenlayerTimelock { contract EtherFiNodeTest is TestSetup { - // from EtherFiNodesManager.sol - uint256 TreasuryRewardSplit = 50_000; - uint256 NodeOperatorRewardSplit = 50_000; - uint256 TNFTRewardSplit = 815_625; - uint256 BNFTRewardSplit = 84_375; - uint256 RewardSplitDivisor = 1_000_000; - - uint256[] bidId; - EtherFiNode safeInstance; - EtherFiNode restakingSafe; - - event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); - event mockEvent_queuedWithdrawalShares(uint256 shares); // MockDelegationManager - - function setUp() public { - setUpTests(); - - vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - nodeOperatorManagerInstance.registerNodeOperator( - _ipfsHash, - 5 - ); - - hoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - bidId = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - - startHoax(0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf); - - uint256[] memory bidIdArray = new uint256[](1); - bidIdArray[0] = bidId[0]; - - stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}( - bidIdArray, - false - ); - - address etherFiNode = managerInstance.etherfiNodeAddress(bidId[0]); - - assertTrue( - managerInstance.phase(bidId[0]) == - IEtherFiNode.VALIDATOR_PHASE.STAKE_DEPOSITED - ); - - IStakingManager.DepositData[] - memory depositDataArray = new IStakingManager.DepositData[](1); - - bytes32 root = generateDepositRoot( - hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - //managerInstance.generateWithdrawalCredentials(etherFiNode), - managerInstance.getWithdrawalCredentials(bidId[0]), - 32 ether - ); - - IStakingManager.DepositData memory depositData = IStakingManager - .DepositData({ - publicKey: hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - signature: hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - depositDataRoot: root, - ipfsHashForEncryptedValidatorKey: "test_ipfs" - }); - - depositDataArray[0] = depositData; - - stakingManagerInstance.batchRegisterValidators(zeroRoot, bidId, depositDataArray); - vm.stopPrank(); - - assertTrue( - managerInstance.phase(bidId[0]) == - IEtherFiNode.VALIDATOR_PHASE.LIVE - ); - - safeInstance = EtherFiNode(payable(etherFiNode)); - - assertEq(address(etherFiNode).balance, 0 ether); - assertEq( - auctionInstance.accumulatedRevenue(), - 0.1 ether - ); - } - - - function test_batchClaimRestakedWithdrawal() public { - initializeTestingFork(MAINNET_FORK); - - uint256 validator1 = depositAndRegisterValidator(true); - uint256 validator2 = depositAndRegisterValidator(true); - EtherFiNode safe1 = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validator1))); - EtherFiNode safe2 = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validator2))); - - _transferTo(address(safe1.eigenPod()), 1 ether); - _transferTo(address(safe2.eigenPod()), 2 ether); - - (uint256 _withdrawalSafe, uint256 _eigenPod, uint256 _delayedWithdrawalRouter) = safe1.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 1 ether); - assertEq(_delayedWithdrawalRouter, 0 ether); - (_withdrawalSafe, _eigenPod, _delayedWithdrawalRouter) = safe2.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 2 ether); - assertEq(_delayedWithdrawalRouter, 0 ether); - - uint256[] memory validatorIds = new uint256[](2); - validatorIds[0] = validator1; - validatorIds[1] = validator2; - vm.prank(alice); // alice is admin - managerInstance.batchQueueRestakedWithdrawal(validatorIds); - - // as of PEPE queing withdrawal does not automatically process partial withdrawals - (_withdrawalSafe, _eigenPod, _delayedWithdrawalRouter) = safe1.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 1 ether); - assertEq(_delayedWithdrawalRouter, 0 ether); - (_withdrawalSafe, _eigenPod, _delayedWithdrawalRouter) = safe2.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 2 ether); - assertEq(_delayedWithdrawalRouter, 0 ether); - - } - - function test_splitBalanceInExecutionLayer() public { - - initializeTestingFork(MAINNET_FORK); - - uint256 validatorId = depositAndRegisterValidator(true); - safeInstance = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validatorId))); - - uint256 beaconBalance = 32 ether; - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = (0, 0, 0, 0); - - (uint256 _withdrawalSafe, uint256 _eigenPod, uint256 _delayedWithdrawalRouter) = safeInstance.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 0 ether); - assertEq(_delayedWithdrawalRouter, 0 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0 ether); - assertEq(toTreasury, 0 ether); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 2 ether); - - // simulate 1 eth of EL staking rewards (such as MEV fee) sent to the eigen pod - _transferTo(address(safeInstance.eigenPod()), 1 ether); - assertEq(address(safeInstance.eigenPod()).balance, 1 ether); - (_withdrawalSafe, _eigenPod, _delayedWithdrawalRouter) = safeInstance.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 1 ether); - assertEq(_delayedWithdrawalRouter, 0 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 1 ether * 5 / 100); - assertEq(toTreasury, 1 ether * 5 / 100); - assertEq(toTnft, 30 ether + (1 ether * 90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + (1 ether * 90 * 3) / (100 * 32)); - - // queue the withdrawal of the rewards. Funds have been sent to the DelayedWithdrawalRouter - _withdrawNonBeaconChainETHBalanceWei(validatorId); - (_withdrawalSafe, _eigenPod, _delayedWithdrawalRouter) = safeInstance.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 0 ether); - assertEq(_delayedWithdrawalRouter, 1 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 1 ether * 5 / 100); - assertEq(toTreasury, 1 ether * 5 / 100); - assertEq(toTnft, 30 ether + (1 ether * 90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + (1 ether * 90 * 3) / (100 * 32)); - - // more staking rewards - _transferTo(address(safeInstance.eigenPod()), 2 ether); - (_withdrawalSafe, _eigenPod, _delayedWithdrawalRouter) = safeInstance.splitBalanceInExecutionLayer(); - assertEq(_withdrawalSafe, 0 ether); - assertEq(_eigenPod, 2 ether); - assertEq(_delayedWithdrawalRouter, 1 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 3 ether * 5 / 100); - assertEq(toTreasury, 3 ether * 5 / 100); - assertEq(toTnft, 30 ether + (3 ether * 90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + (3 ether * 90 * 3) / (100 * 32)); - } - - function test_restakedFullWithdrawal() public { - initializeTestingFork(MAINNET_FORK); - - uint256 validatorId = depositAndRegisterValidator(true); - safeInstance = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validatorId))); - - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitRequestTimestamps = new uint32[](1); - validatorIds[0] = validatorId; - exitRequestTimestamps[0] = uint32(block.timestamp); - - vm.deal(safeInstance.eigenPod(), 32 ether); - - vm.startPrank(alice); // alice is the admin - vm.expectRevert("NOT_EXITED"); - managerInstance.fullWithdraw(validatorIds[0]); - - // Marked as EXITED - // should also have queued up the current balance to via DelayedWithdrawalRouter - vm.expectRevert("NO_FULLWITHDRAWAL_QUEUED"); - managerInstance.processNodeExit(validatorIds, exitRequestTimestamps); - - - // TODO - // IDelayedWithdrawalRouter.DelayedWithdrawal[] memory unclaimedWithdrawals = managerInstance.delayedWithdrawalRouter().getUserDelayedWithdrawals(address(safeInstance)); - // assertEq(unclaimedWithdrawals.length, 1); - // assertEq(unclaimedWithdrawals[0].amount, uint224(32 ether)); - - // // fail because we have not processed the queued withdrawal of the funds from the pod - // // because not enough time has passed to claim them - // vm.expectRevert("PENDING_WITHDRAWALS"); - // managerInstance.fullWithdraw(validatorIds[0]); - - // // wait some time - // vm.roll(block.number + (50400) + 1); - - // assertEq(managerInstance.getUnusedWithdrawalSafesLength(), 0); - - // // try again. FullWithdraw will automatically attempt to claim queuedWithdrawals - // managerInstance.fullWithdraw(validatorIds[0]); - // assertEq(address(safeInstance).balance, 0); - - // // safe should have been automatically recycled - // assertEq(managerInstance.getUnusedWithdrawalSafesLength(), 1); - // assertEq(uint256(managerInstance.phase(validatorIds[0])), uint256(IEtherFiNode.VALIDATOR_PHASE.FULLY_WITHDRAWN)); - // assertEq(safeInstance.isRestakingEnabled(), false); - // assertEq(safeInstance.restakingObservedExitBlock(), 0); - } - - - function _withdrawNonBeaconChainETHBalanceWei(uint256 validatorId) public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = validatorId; - - address safe = managerInstance.etherfiNodeAddress(validatorId); - address eigenPod = managerInstance.getEigenPod(validatorId); - - bytes4 selector = bytes4(keccak256("withdrawNonBeaconChainETHBalanceWei(address,uint256)")); - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeWithSelector(selector, safe, address(eigenPod).balance); - - vm.prank(owner); - managerInstance.forwardEigenpodCall(validatorIds, data); - } - - function testFullWithdrawBurnsTNFT() public { - initializeTestingFork(MAINNET_FORK); - - uint256 validatorId = depositAndRegisterValidator(false); - safeInstance = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validatorId))); - - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitRequestTimestamps = new uint32[](1); - validatorIds[0] = validatorId; - exitRequestTimestamps[0] = uint32(block.timestamp); - - vm.deal(address(safeInstance), 32 ether); - - vm.startPrank(alice); // alice is the admin - vm.expectRevert("NOT_EXITED"); - managerInstance.fullWithdraw(validatorId); - - // Marked as EXITED - // should also have queued up the current balance to via DelayedWithdrawalRouter - managerInstance.processNodeExit(validatorIds, exitRequestTimestamps); - - // alice should own the tNFT since she created the validator - assertEq(TNFTInstance.ownerOf(validatorId), alice); - - // withdraw the node - managerInstance.fullWithdraw(validatorIds[0]); - - // tNFT should be burned - vm.expectRevert("ERC721: invalid token ID"); - TNFTInstance.ownerOf(validatorId); - // bNFT should be burned - vm.expectRevert("ERC721: invalid token ID"); - BNFTInstance.ownerOf(validatorId); - } - - function test_EtherFiNodeMultipleSafesWorkCorrectly() public { - - vm.prank(alice); - nodeOperatorManagerInstance.registerNodeOperator( - aliceIPFSHash, - 5 - ); - - vm.prank(chad); - nodeOperatorManagerInstance.registerNodeOperator( - aliceIPFSHash, - 5 - ); - - hoax(alice); - uint256[] memory bidId1 = auctionInstance.createBid{value: 0.4 ether}( - 1, - 0.4 ether - ); - - hoax(chad); - uint256[] memory bidId2 = auctionInstance.createBid{value: 0.3 ether}( - 1, - 0.3 ether - ); - - hoax(bob); - uint256[] memory bidIdArray = new uint256[](1); - bidIdArray[0] = bidId1[0]; - - stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}( - bidIdArray, - false - ); - - hoax(dan); - bidIdArray = new uint256[](1); - bidIdArray[0] = bidId2[0]; - - stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}( - bidIdArray, - false - ); - - { - address staker_2 = stakingManagerInstance.bidIdToStaker(bidId1[0]); - address staker_3 = stakingManagerInstance.bidIdToStaker(bidId2[0]); - assertEq(staker_2, bob); - assertEq(staker_3, dan); - } - - address etherFiNode = managerInstance.etherfiNodeAddress(bidId1[0]); - - IStakingManager.DepositData[] - memory depositDataArray = new IStakingManager.DepositData[](1); - - root = generateDepositRoot( - hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), - 32 ether - ); - - IStakingManager.DepositData memory depositData = IStakingManager - .DepositData({ - publicKey: hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - signature: hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - depositDataRoot: root, - ipfsHashForEncryptedValidatorKey: "test_ipfs" - }); - - depositDataArray[0] = depositData; - - startHoax(bob); - stakingManagerInstance.batchRegisterValidators(zeroRoot, bidId1, depositDataArray); - vm.stopPrank(); - - assertEq(address(managerInstance.etherfiNodeAddress(bidId1[0])).balance, 0); - - etherFiNode = managerInstance.etherfiNodeAddress(bidId2[0]); - - IStakingManager.DepositData[] - memory depositDataArray2 = new IStakingManager.DepositData[](1); - - root = generateDepositRoot( - hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), - 32 ether - ); - - depositData = IStakingManager - .DepositData({ - publicKey: hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - signature: hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - depositDataRoot: root, - ipfsHashForEncryptedValidatorKey: "test_ipfs" - }); - - depositDataArray2[0] = depositData; - - startHoax(dan); - stakingManagerInstance.batchRegisterValidators(zeroRoot, bidId2, depositDataArray2); - vm.stopPrank(); - - assertEq(address(managerInstance.etherfiNodeAddress(bidId2[0])).balance, 0); - } - - function test_markExitedWorksCorrectly() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - assertTrue( - managerInstance.phase(validatorIds[0]) == - IEtherFiNode.VALIDATOR_PHASE.LIVE - ); - assertTrue(IEtherFiNode(etherFiNode).DEPRECATED_exitTimestamp() == 0); - - vm.expectRevert("INCORRECT_CALLER"); - IEtherFiNode(etherFiNode).processNodeExit(1); - - vm.expectRevert(EtherFiNodesManager.NotAdmin.selector); - vm.prank(bob); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - IEtherFiNodesManager.ValidatorInfo memory info = managerInstance.getValidatorInfo(validatorIds[0]); - assertTrue(info.phase == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertTrue(info.exitTimestamp == 0); - - hoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - info = managerInstance.getValidatorInfo(validatorIds[0]); - assertTrue(info.phase == IEtherFiNode.VALIDATOR_PHASE.EXITED); - assertTrue(info.exitTimestamp > 0); - - hoax(alice); - vm.expectRevert("INVALID_PHASE_TRANSITION"); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - } - - function test_markExitedWorksCorrectlyWhenBeingSlashed() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - IEtherFiNodesManager.ValidatorInfo memory info = managerInstance.getValidatorInfo(validatorIds[0]); - assertTrue(info.phase == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertTrue(info.exitTimestamp == 0); - - hoax(alice); - managerInstance.markBeingSlashed(validatorIds); - info = managerInstance.getValidatorInfo(validatorIds[0]); - assertTrue(info.phase == IEtherFiNode.VALIDATOR_PHASE.BEING_SLASHED); - - hoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - info = managerInstance.getValidatorInfo(validatorIds[0]); - assertTrue(info.phase == IEtherFiNode.VALIDATOR_PHASE.EXITED); - assertTrue(info.exitTimestamp > 0); - } - - function test_partialWithdrawRewardsDistribution() public { - address nodeOperator = 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931; - address staker = 0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf; - address etherfiNode = managerInstance.etherfiNodeAddress(bidId[0]); - - // Transfer the T-NFT to 'dan' - hoax(staker); - TNFTInstance.transferFrom(staker, dan, bidId[0]); - - uint256 nodeOperatorBalance = address(nodeOperator).balance; - uint256 treasuryBalance = address(treasuryInstance).balance; - uint256 danBalance = address(dan).balance; - uint256 bnftStakerBalance = address(staker).balance; - - // Simulate the rewards distribution from the beacon chain - vm.deal(etherfiNode, address(etherfiNode).balance + 1 ether); - - // Withdraw the {staking, protocol} rewards - // - bid amount = 0 ether (Auction revenue no longer distributed to nodes) - // - 50 % ether is vested for the stakers - // - 50 % ether is shared across all validators - // - 25 % to treasury, 25% to node operator, the rest to the stakers - // - staking rewards amount = 1 ether - hoax(owner); - managerInstance.partialWithdraw(bidId[0]); - assertEq( - address(nodeOperator).balance, - nodeOperatorBalance + (1 ether * 5) / 100 - ); - assertEq( - address(treasuryInstance).balance, - treasuryBalance + (1 ether * 5 ) / 100 - ); - assertEq(address(dan).balance, danBalance + 0.815625000000000000 ether); - assertEq(address(staker).balance, bnftStakerBalance + 0.084375000000000000 ether); - - vm.deal(etherfiNode, 16.0 ether); - vm.expectRevert("MUST_EXIT"); - vm.prank(owner); - managerInstance.partialWithdraw(bidId[0]); - } - - function test_partialWithdrawFails() public { - address etherfiNode = managerInstance.etherfiNodeAddress(bidId[0]); - - vm.deal(etherfiNode, 4 ether); - - vm.expectRevert(EtherFiNodesManager.NotAdmin.selector); - vm.prank(bob); - managerInstance.markBeingSlashed(bidId); - - hoax(alice); - managerInstance.markBeingSlashed(bidId); - - vm.prank(managerInstance.owner()); - vm.expectRevert("NOT_LIVE"); - managerInstance.partialWithdraw(bidId[0]); - } - - function test_markBeingSlashedFails() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - - hoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - - hoax(alice); - vm.expectRevert("INVALID_PHASE_TRANSITION"); - managerInstance.markBeingSlashed(bidId); - } - - function test_markBeingSlashedWorks() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - hoax(alice); - managerInstance.markBeingSlashed(bidId); - assertTrue(managerInstance.phase(bidId[0]) == IEtherFiNode.VALIDATOR_PHASE.BEING_SLASHED); - } - - function test_partialWithdrawAfterExitRequest() public { - address nodeOperator = 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931; - address staker = 0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf; - address etherfiNode = managerInstance.etherfiNodeAddress(bidId[0]); - - // Simulate the rewards distribution from the beacon chain - vm.deal(etherfiNode, address(etherfiNode).balance + 1 ether); - - // Send Exit Request - hoax(TNFTInstance.ownerOf(bidId[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(bidId[0])); - - vm.prank(managerInstance.owner()); - vm.expectRevert("PENDING_EXIT_REQUEST"); - managerInstance.partialWithdraw(bidId[0]); - } - - function test_getFullWithdrawalPayoutsFails() public { - - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - address etherfiNode = managerInstance.etherfiNodeAddress( - validatorIds[0] - ); - - vm.deal(etherfiNode, 16 ether); - vm.expectRevert("NOT_EXITED"); - managerInstance.fullWithdraw(validatorIds[0]); - } - - function test_processNodeDistributeProtocolRevenueCorrectly() public { - address nodeOperator = 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931; - address staker = 0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf; - - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - - uint256 nodeOperatorBalance = address(nodeOperator).balance; - uint256 treasuryBalance = address(treasuryInstance).balance; - uint256 stakerBalance = address(staker).balance; - - startHoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - vm.stopPrank(); - - // no auction rewards anymore - assertEq(address(nodeOperator).balance, nodeOperatorBalance); - assertEq(address(treasuryInstance).balance, treasuryBalance); - assertEq(address(staker).balance, stakerBalance); - } - - - function test_getFullWithdrawalPayoutsWorksCorrectly1() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - startHoax(alice); - assertEq(managerInstance.numberOfValidators(), 1); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - assertEq(managerInstance.numberOfValidators(), 0); - vm.stopPrank(); - - // 1. balance > 32 ether - vm.deal(etherfiNode, 33 ether); - assertEq(address(etherfiNode).balance, 33 ether); - - uint256 stakingRewards = 1 ether; - ( - uint256 toNodeOperator, - uint256 toTnft, - uint256 toBnft, - uint256 toTreasury - ) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, stakingRewards * NodeOperatorRewardSplit / RewardSplitDivisor); - assertEq(toTreasury, stakingRewards * TreasuryRewardSplit / RewardSplitDivisor); - assertEq(toTnft, 30 ether + (stakingRewards * TNFTRewardSplit / RewardSplitDivisor)); - assertEq(toBnft, 2 ether + (stakingRewards * BNFTRewardSplit / RewardSplitDivisor)); - - // 2. balance > 31.5 ether - vm.deal(etherfiNode, 31.75 ether); - assertEq(address(etherfiNode).balance, 31.75 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance - .getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0); - assertEq(toTreasury, 0); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 1.75 ether); - - // 3. balance > 26 ether - vm.deal(etherfiNode, 28.5 ether); - assertEq(address(etherfiNode).balance, 28.5 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance - .getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0); - assertEq(toTreasury, 0); - assertEq(toTnft, 27.5 ether); - assertEq(toBnft, 1 ether); - - // 4. balance > 25.5 ether - vm.deal(etherfiNode, 25.75 ether); - assertEq(address(etherfiNode).balance, 25.75 ether); - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance - .getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0); - assertEq(toTreasury, 0); - assertEq(toTnft, 24.75 ether); - assertEq(toBnft, 1 ether); - - // 5. balance > 16 ether - vm.deal(etherfiNode, 18.5 ether); - assertEq(address(etherfiNode).balance, 18.5 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance - .getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0); - assertEq(toTreasury, 0); - assertEq(toTnft, 17.5 ether); - assertEq(toBnft, 1 ether); - - // 6. balance = 16 ether - vm.deal(etherfiNode, 16 ether); - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance - .getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0); - assertEq(toTreasury, 0); - assertEq(toTnft, 15 ether); - assertEq(toBnft, 1 ether); - - // 7. balance < 16 ether - vm.deal(etherfiNode, 16 ether - 1); - - vm.expectRevert(); - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance - .getFullWithdrawalPayouts(validatorIds[0]); - } - - function test_partialWithdrawAfterExitFails() public { - address nodeOperator = 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931; - address staker = 0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf; - - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - // 8. balance < 4 ether - vm.deal(etherfiNode, 4 ether); - - startHoax(alice); - assertEq(managerInstance.numberOfValidators(), 1); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - assertEq(managerInstance.numberOfValidators(), 0); - vm.stopPrank(); - - // Transfer the T-NFT to 'dan' - hoax(staker); - TNFTInstance.transferFrom(staker, dan, validatorIds[0]); - - hoax(owner); - vm.expectRevert("NOT_LIVE"); - managerInstance.partialWithdraw(validatorIds[0]); - } - - function test_getFullWithdrawalPayoutsAuditFix3() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - startHoax(alice); - assertEq(managerInstance.numberOfValidators(), 1); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - assertEq(managerInstance.numberOfValidators(), 0); - vm.stopPrank(); - - uint256 stakingRewards = 0.04 ether; - vm.deal(etherfiNode, 32 ether + stakingRewards); - - assertEq(address(etherfiNode).balance, 32 ether + stakingRewards); - - { - (uint256 toNodeOperator, - uint256 toTnft, - uint256 toBnft, - uint256 toTreasury - ) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - - assertEq(toNodeOperator, stakingRewards * NodeOperatorRewardSplit / RewardSplitDivisor); - assertEq(toTnft, 30 ether + stakingRewards * TNFTRewardSplit / RewardSplitDivisor); - assertEq(toBnft, 2 ether + stakingRewards * BNFTRewardSplit / RewardSplitDivisor); - assertEq(toTreasury, stakingRewards * TreasuryRewardSplit / RewardSplitDivisor); - } - - skip(6 * 7 * 4 days); - - // auction rewards no longer vest so should be the same as above - { - (uint256 toNodeOperator, - uint256 toTnft, - uint256 toBnft, - uint256 toTreasury - ) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - - assertEq(toNodeOperator, stakingRewards * NodeOperatorRewardSplit / RewardSplitDivisor); - assertEq(toTnft, 30 ether + stakingRewards * TNFTRewardSplit / RewardSplitDivisor); - assertEq(toBnft, 2 ether + stakingRewards * BNFTRewardSplit / RewardSplitDivisor); - assertEq(toTreasury, stakingRewards * TreasuryRewardSplit / RewardSplitDivisor); - } - } - - function test_getFullWithdrawalPayoutsAuditFix2() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - startHoax(alice); - assertEq(managerInstance.numberOfValidators(), 1); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - assertEq(managerInstance.numberOfValidators(), 0); - vm.stopPrank(); - - //uint256 stakingRewards = 0.949 ether; - vm.deal(etherfiNode, 31.949 ether); - assertEq( - address(etherfiNode).balance, - 31.949000000000000000 ether - ); - - - skip(6 * 7 * 4 days); - - // auction rewards no longer vest so should be the same as above - { - (uint256 toNodeOperator, - uint256 toTnft, - uint256 toBnft, - uint256 toTreasury - ) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - - assertEq(toNodeOperator, 0); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 1.949000000000000000 ether); - assertEq(toTreasury, 0); - } - } - - - function test_getFullWithdrawalPayoutsWorksWithNonExitPenaltyCorrectly1() - public - { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp) + 86400; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 0); - - hoax(TNFTInstance.ownerOf(validatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 1); - - // 1 day passed - vm.warp(block.timestamp + 86400); - startHoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - uint256 nonExitPenalty = managerInstance.getNonExitPenalty(bidId[0]); - - // simulate staking rewards - uint256 stakingRewards = 1 ether; - vm.deal(etherfiNode, 32 ether + stakingRewards); - - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - assertEq(nonExitPenalty, 0.03 ether); - assertEq(toNodeOperator, nonExitPenalty + (stakingRewards * NodeOperatorRewardSplit / RewardSplitDivisor)); - assertEq(toTreasury, stakingRewards * TreasuryRewardSplit / RewardSplitDivisor); - assertEq(toTnft, 30 ether + (stakingRewards * TNFTRewardSplit / RewardSplitDivisor)); - assertEq(toBnft, 2 ether - nonExitPenalty + (stakingRewards * BNFTRewardSplit / RewardSplitDivisor)); - } - - function test_getFullWithdrawalPayoutsWorksWithNonExitPenaltyCorrectly2() - public - { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp) + (1 + 7 * 86400); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - hoax(TNFTInstance.ownerOf(validatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - - // 7 days passed - vm.warp(block.timestamp + (1 + 7 * 86400)); - startHoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - uint256 nonExitPenalty = managerInstance.getNonExitPenalty(bidId[0]); - - // simulate staking rewards - uint256 stakingRewards = 1 ether; - vm.deal(etherfiNode, 32 ether + stakingRewards); - - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, nonExitPenalty + (stakingRewards * NodeOperatorRewardSplit / RewardSplitDivisor)); - assertEq(toTreasury, stakingRewards * TreasuryRewardSplit / RewardSplitDivisor); - assertEq(toTnft, 30 ether + (stakingRewards * TNFTRewardSplit / RewardSplitDivisor)); - assertEq(toBnft, 2 ether - nonExitPenalty + (stakingRewards * BNFTRewardSplit / RewardSplitDivisor)); - } - - function test_getFullWithdrawalPayoutsWorksWithNonExitPenaltyCorrectly4() - public - { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp) + 28 * 86400; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - hoax(TNFTInstance.ownerOf(validatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - - // 28 days passed - // When (appliedPenalty <= 0.2 ether) - vm.warp(block.timestamp + 28 * 86400); - startHoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - uint256 nonExitPenalty = managerInstance.getNonExitPenalty(bidId[0]); - - // see EtherFiNode.sol:calculateTVL() - // the node got slashed seriously - vm.deal(etherfiNode, 16 ether); - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - assertEq(nonExitPenalty, 0.573804794831376551 ether); - - assertEq(toNodeOperator, 0.2 ether); // incentive for nodeOperator from NonExitPenalty caps at 0.2 ether - assertEq(toTreasury, nonExitPenalty - 0.2 ether); // treasury gets excess penalty if node operator delays too long - assertEq(toTnft, 15 ether); - assertEq(toBnft, 1 ether - 0.573804794831376551 ether); // BNFT has been fully penalized for not exiting - } - - function test_markExitedFails() public { - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](2); - startHoax(alice); - vm.expectRevert(EtherFiNodesManager.InvalidParams.selector); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - } - - function test_getFullWithdrawalPayoutsWorksWithNonExitPenaltyCorrectly3() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp) + (1 + 28 * 86400); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - hoax(TNFTInstance.ownerOf(validatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - - // 28 days passed - // When (appliedPenalty > 0.2 ether) - vm.warp(block.timestamp + (1 + 28 * 86400)); - startHoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - uint256 nonExitPenalty = managerInstance.getNonExitPenalty(bidId[0]); - assertGe(nonExitPenalty, 0.5 ether); - - // Treasury gets the excess penalty reward after the node operator hits the 0.2 eth cap - // Treasury also gets the base reward of the node operator since its over 14 days - uint256 baseTreasuryPayout = (1 ether * TreasuryRewardSplit / RewardSplitDivisor); - uint256 baseNodeOperatorPayout = (1 ether * NodeOperatorRewardSplit / RewardSplitDivisor); - - uint256 stakingRewards = 1 ether; - vm.deal(etherfiNode, 32 ether + stakingRewards); - ( - uint256 toNodeOperator, - uint256 toTnft, - uint256 toBnft, - uint256 toTreasury - ) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0.2 ether + baseNodeOperatorPayout); - assertEq(toTreasury, baseTreasuryPayout + (nonExitPenalty - 0.2 ether)); - assertEq(toTnft, 30 ether + (stakingRewards * TNFTRewardSplit / RewardSplitDivisor)); - assertEq(toBnft, 2 ether - nonExitPenalty + (stakingRewards * BNFTRewardSplit / RewardSplitDivisor)); - } - - function test_getFullWithdrawalPayoutsWorksWithNonExitPenaltyCorrectly5() public { - - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp) + (1 + 28 * 86400); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - hoax(TNFTInstance.ownerOf(validatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - - // 2 * 28 days passed - // When (appliedPenalty > 0.2 ether) - vm.warp(block.timestamp + (1 + 2 * 28 * 86400)); - hoax(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - uint256 nonExitPenalty = managerInstance.getNonExitPenalty(bidId[0]); - - - uint256 stakingRewards = 1 ether; - vm.deal(etherfiNode, 32 ether + stakingRewards); - - // Treasury gets the excess penalty reward after the node operator hits the 0.2 eth cap - // Treasury also gets the base reward of the node operator since its over 14 days - uint256 baseTreasuryPayout = (1 ether * TreasuryRewardSplit / RewardSplitDivisor); - uint256 baseNodeOperatorPayout = (1 ether * NodeOperatorRewardSplit / RewardSplitDivisor); - - ( - uint256 toNodeOperator, - uint256 toTnft, - uint256 toBnft, - uint256 toTreasury - ) = managerInstance.getFullWithdrawalPayouts(validatorIds[0]); - assertEq(toNodeOperator, 0.2 ether + baseNodeOperatorPayout); - assertEq(toTreasury, baseTreasuryPayout + (nonExitPenalty - 0.2 ether)); - assertEq(toTnft, 30 ether + (stakingRewards *TNFTRewardSplit / RewardSplitDivisor)); - assertEq(toBnft, 2 ether - nonExitPenalty + (stakingRewards * BNFTRewardSplit / RewardSplitDivisor)); - } - - function test_sendEthToEtherFiNodeContractSucceeds() public { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - vm.deal(owner, 10 ether); - vm.prank(owner); - uint256 nodeBalance = address(etherfiNode).balance; - (bool sent, ) = address(etherfiNode).call{value: 5 ether}(""); - require(sent, "Failed to send eth"); - assertEq(address(etherfiNode).balance, nodeBalance + 5 ether); - } - - function test_ExitRequestAfterExitFails() public { - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](1); - - validatorIds[0] = bidId[0]; - - vm.prank(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - - vm.prank(TNFTInstance.ownerOf(validatorIds[0])); - exitTimestamps[0] = uint32(block.timestamp); - - // T-NFT holder sends the exit request after the node is marked EXITED - vm.expectRevert("INVALID"); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - } - - function test_ExitTimestampBeforeExitRequestLeadsToZeroNonExitPenalty() public { - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](1); - - validatorIds[0] = bidId[0]; - - vm.prank(TNFTInstance.ownerOf(validatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(validatorIds[0])); - - // the node actually exited a second before the exit request from the T-NFT holder - vm.prank(alice); - exitTimestamps[0] = uint32(block.timestamp) - 1; - managerInstance.processNodeExit(validatorIds, exitTimestamps); - - uint256 nonExitPenalty = managerInstance.getNonExitPenalty(bidId[0]); - assertEq(nonExitPenalty, 0 ether); - } - - function test_ImplementationContract() public { - assertEq(safeInstance.implementation(), address(node)); - } - - function test_trackingTVL() public { - uint256 validatorId = bidId[0]; - - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = (0, 0, 0, 0); - - // (Validator 'active_not_slashed', Accrued rewards in CL = 1 ether) - { - uint256 beaconBalance = 32 ether + 1 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0.05 ether); - assertEq(toTreasury, 0.05 ether); - assertEq(toTnft, 30.815625000000000000 ether); - assertEq(toBnft, 2.084375000000000000 ether); - } - - // (Validator 'active_not_slashed', Accrued rewards in CL = 0) - { - uint256 beaconBalance = 32 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0 ether); - assertEq(toTreasury, 0 ether); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 2 ether); - } - - // (Validator 'active_slashed', slashing penalty in CL = 0.5 ether) - // - slashing penalty [0, 1 ether] is paid by the B-NFT holder - { - uint256 beaconBalance = 31.5 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0 ether); - assertEq(toTreasury, 0 ether); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 1.5 ether); - } - - // (Validator 'active_slashed', slashing penalty in CL = 1 ether) - { - uint256 beaconBalance = 31 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0 ether); - assertEq(toTreasury, 0 ether); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 1 ether); - } - - { - uint256 beaconBalance = 30 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0 ether); - assertEq(toTreasury, 0 ether); - assertEq(toTnft, 29 ether); - assertEq(toBnft, 1 ether); - } - - // The worst-case, 16 ether is all slashed! - { - uint256 beaconBalance = 16 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0 ether); - assertEq(toTreasury, 0 ether); - assertEq(toTnft, 15 ether); - assertEq(toBnft, 1 ether); - } - } - - function test_trackingTVL2() public { - uint256 validatorId = bidId[0]; - uint256[] memory tvls = new uint256[](4); // (operator, tnft, bnft, treasury) - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = (0, 0, 0, 0); - - // Check the staking rewards when we have 1 ether accrued - { - uint256 beaconBalance = 32 ether + 1 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, 0.05 ether); - assertEq(toTreasury, 0.05 ether); - assertEq(toTnft, 30.815625000000000000 ether); - assertEq(toBnft, 2.084375000000000000 ether); - tvls[0] += toNodeOperator; - tvls[1] += toTnft; - tvls[2] += toBnft; - tvls[3] += toTreasury; - - assertEq(beaconBalance, toNodeOperator + toTnft + toBnft + toTreasury); - } - - // Confirm the total TVL - { - uint256 beaconBalance = 32 ether + 1 ether; - - (toNodeOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, beaconBalance); - assertEq(toNodeOperator, tvls[0]); - assertEq(toTreasury, tvls[3]); - assertEq(toTnft, tvls[1]); - assertEq(toBnft, tvls[2]); - } - - // Confirm that after exiting the validator node from the beacon network, - // if we trigger the full withdrawal, the same amount is transferred to {stakers, operator, treasury} - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitRequestTimestamps = new uint32[](1); - validatorIds[0] = validatorId; - exitRequestTimestamps[0] = uint32(block.timestamp); - - address nodeOperator = 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931; - address staker = 0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf; - - // Transfer the T-NFT to 'dan' (Just for testing scenario) - vm.prank(staker); - TNFTInstance.transferFrom(staker, dan, bidId[0]); - - uint256 nodeOperatorBalance = address(nodeOperator).balance; - uint256 treasuryBalance = address(treasuryInstance).balance; - uint256 bnftStakerBalance = address(staker).balance; - uint256 tNftStakerBalance = address(dan).balance; - - // Simulate the withdrawal from Beacon Network to Execution Layer - _transferTo(etherfiNode, 32 ether + 1 ether); - - // After a long period of time (after the auction fee vesting period completes) - skip(6 * 7 * 4 days); - - vm.prank(alice); - managerInstance.processNodeExit(validatorIds, exitRequestTimestamps); // Marked as EXITED - managerInstance.batchFullWithdraw(validatorIds); // Full Withdrawal! - - assertEq(address(nodeOperator).balance, nodeOperatorBalance + tvls[0]); - assertEq(address(dan).balance, tNftStakerBalance + tvls[1]); - assertEq(address(staker).balance, bnftStakerBalance + tvls[2]); - assertEq(address(treasuryInstance).balance, treasuryBalance + tvls[3]); - } - - function test_trackingTVL3() public { - uint256 tvl = 0; - uint256 numBnftsHeldByLP = 0; // of validators in [LIVE, EXITED, BEING_SLASHED] - uint256 numTnftsHeldByLP = 0; // of validators in [LIVE, EXITED, BEING_SLASHED] - uint256 numValidators_STAKE_DEPOSITED = 0; - uint256 numValidators_WATING_FOR_APPROVAL = 0; - assertEq(address(liquidityPoolInstance).balance, 0); - assertEq(address(stakingManagerInstance).balance, 0); - - uint256[] memory validatorIds = launch_validator(1, 0, true); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - // tvl = (BNFT) + (TNFT) + (LP Balance) - .... - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 1; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance - numValidators_STAKE_DEPOSITED * 2 ether - numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(address(liquidityPoolInstance).balance, 0); - assertEq(address(stakingManagerInstance).balance, 0); - - vm.startPrank(alice); - - // ---------------------------------------------------------- // - // -------------- BNFT to BNFT-Staker, TNFT to LP ----------- // - // ---------------------------------------------------------- // - // liquidityPoolInstance.updateBnftMode(false); - - liquidityPoolInstance.deposit{value: 30 ether}(); - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 1; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance - numValidators_STAKE_DEPOSITED * 2 ether - numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether); - assertEq(address(liquidityPoolInstance).balance, 30 ether); - assertEq(address(stakingManagerInstance).balance, 0); - - // - // New Validator Deposit - // - uint256[] memory newValidatorIds = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - liquidityPoolInstance.batchDeposit(newValidatorIds, 1, 0); - - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 1; - numValidators_STAKE_DEPOSITED = 1; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance - numValidators_STAKE_DEPOSITED * 2 ether - numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(address(stakingManagerInstance).balance, 0); - - // - // New Validator Register - // - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(newValidatorIds); - liquidityPoolInstance.batchRegister(zeroRoot, newValidatorIds, depositDataArray, depositDataRootsForApproval, sig); - - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 1; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 1; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance - numValidators_STAKE_DEPOSITED * 2 ether - numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether); - assertEq(address(liquidityPoolInstance).balance, 31 ether); - assertEq(address(stakingManagerInstance).balance, 0); - - // - // APPROVE - // - liquidityPoolInstance.batchApproveRegistration(newValidatorIds, pubKey, sig); - - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 2; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance - numValidators_STAKE_DEPOSITED * 2 ether - numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether); - assertEq(address(liquidityPoolInstance).balance, 0); - assertEq(address(stakingManagerInstance).balance, 0); - - - // ---------------------------------------------------------- // - // ---------------- BNFT to LP, TNFT to LP ------------------ // - // ---------------------------------------------------------- // - // liquidityPoolInstance.updateBnftMode(true); - - liquidityPoolInstance.deposit{value: 32 ether}(); - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 2; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance + numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether + 32 ether); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(address(stakingManagerInstance).balance, 0); - - // - // New Validator Deposit - // - newValidatorIds = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - liquidityPoolInstance.batchDeposit(newValidatorIds, 1, 0); - - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 2; - numValidators_STAKE_DEPOSITED = 1; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance + numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether + 32 ether); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(address(stakingManagerInstance).balance, 0); - - // - // New Validator Register - // - (depositDataArray, depositDataRootsForApproval, sig, pubKey) = _prepareForValidatorRegistration(newValidatorIds); - liquidityPoolInstance.batchRegister(zeroRoot, newValidatorIds, depositDataArray, depositDataRootsForApproval, sig); - - numBnftsHeldByLP = 1; - numTnftsHeldByLP = 2; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 1; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance + numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether + 32 ether); - assertEq(address(liquidityPoolInstance).balance, 31 ether); - assertEq(address(stakingManagerInstance).balance, 0); - - // - // APPROVE - // - liquidityPoolInstance.batchApproveRegistration(newValidatorIds, pubKey, sig); - - numBnftsHeldByLP = 2; - numTnftsHeldByLP = 3; - numValidators_STAKE_DEPOSITED = 0; - numValidators_WATING_FOR_APPROVAL = 0; - tvl = numBnftsHeldByLP * 2 ether + numTnftsHeldByLP * 30 ether + address(liquidityPoolInstance).balance + numValidators_WATING_FOR_APPROVAL * 1 ether; - assertEq(liquidityPoolInstance.getTotalPooledEther(), tvl); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether + 30 ether + 32 ether); - assertEq(address(liquidityPoolInstance).balance, 0); - assertEq(address(stakingManagerInstance).balance, 0); - - vm.stopPrank(); - } - - // Zelic audit - reward can be go wrong because it's using wrong numAssociatedValidators. - function test_partialWithdrawWithMultipleValidators_WithIntermdiateValidators() public { - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 30 ether); - - // One validator of id `validatorId` with accrued rewards amount in the safe = 1 ether - vm.deal(etherfiNode, 1 ether); - - // the accrued rewards (1 ether) are split as follows: - (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.getRewardsPayouts(validatorId); - assertEq(toOperator, 1 ether * 5 / (100)); - assertEq(toTnft, 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (100)); - - // TVL = accrued rewards amounts + beacon balance as principal - // assuming the beacon balance is 32 ether - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - assertEq(toOperator, 1 ether * 5 / 100); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / 100); - - liquidityPoolInstance.deposit{value: 30 ether * 1}(); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 30 ether + 30 ether); - - vm.startPrank(alice); - registerAsBnftHolder(alice); - - // - // New Validator Deposit into the same safe - // - uint256[] memory newValidatorIds = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - liquidityPoolInstance.batchDeposit(newValidatorIds, 1, validatorId); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 30 ether + 30 ether); - - // Confirm that the num of associated validators still 1 - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - // Confirm that the {getRewardsPayouts, calculateTVL} remain the smae - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorId); - assertEq(toOperator, 1 ether * 5 / (100)); - assertEq(toTnft, 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - assertEq(toOperator, 1 ether * 5 / 100); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / 100); - - vm.expectRevert("NOT_LIVE"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - - vm.expectRevert("INVALID_PHASE"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(newValidatorIds[0], 32 ether); - - // - // New Validator Register into the same safe - // - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(newValidatorIds); - liquidityPoolInstance.batchRegister(zeroRoot, newValidatorIds, depositDataArray, depositDataRootsForApproval, sig); - - // Confirm that the num of associated validators still 1 - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 30 ether + 30 ether); - - // Confirm that the {getRewardsPayouts, calculateTVL} remain the smae - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorId); - assertEq(toOperator, 1 ether * 5 / (100)); - assertEq(toTnft, 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - assertEq(toOperator, 1 ether * 5 / 100); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / 100); - - vm.expectRevert("NOT_LIVE"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - - vm.expectRevert("INVALID_PHASE"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(newValidatorIds[0], 32 ether); - - // - // APPROVE - // - liquidityPoolInstance.batchApproveRegistration(newValidatorIds, pubKey, sig); - - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 2); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 30 ether + 30 ether); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorId); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(newValidatorIds[0], 32 ether); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - vm.stopPrank(); - } - - function test_partialWithdrawWithMultipleValidators() public { - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - // Case 1: one validator of id `validatorId` with accrued rewards amount in the safe = 1 ether - vm.deal(etherfiNode, 1 ether); - - // the accrued rewards (1 ether) are split as follows: - (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.getRewardsPayouts(validatorId); - assertEq(toOperator, 1 ether * 5 / (100)); - assertEq(toTnft, 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (100)); - - // TVL = accrued rewards amounts + beacon balance as principal - // assuming the beacon balance is 32 ether - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - assertEq(toOperator, 1 ether * 5 / 100); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (100 * 32)); - assertEq(toTreasury, 1 ether * 5 / 100); - - // Case 2: launch a new validator sharing the same safe, EtherFiNode, contract - // so the safe is shared by the two validators - // Note that the safe has the same total accrued rewards amount (= 1 ether) - uint256[] memory newValidatorIds = launch_validator(1, validatorId, false); - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertTrue(managerInstance.phase(newValidatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 2); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 0); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorId); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(newValidatorIds[0], 32 ether); - assertEq(toOperator, 1 ether * 5 / (2 * 100)); - assertEq(toTnft, 30 ether + 1 ether * (90 * 29) / (2 * 100 * 32)); - assertEq(toBnft, 2 ether + 1 ether * (90 * 3) / (2 * 100 * 32)); - assertEq(toTreasury, 1 ether * 5 / (2 * 100)); - - // What if one of the validators exits after getting slashed till 16 ether? - // It exited & its principle is withdrawn - _transferTo(etherfiNode, 16 ether); - - vm.expectRevert("MUST_EXIT"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorIds[0]); - vm.expectRevert("MUST_EXIT"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - - // Mark validatorIds[0] as EXITED - vm.prank(alice); - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 2); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 1); - - // 16 ether was withdrawn from Beacon after the full slashing - // 1 ether which were accrued rewards for both validators are used to cover the loss in `validatorId` - // -> in total, the safe has 17 ether - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 0); - assertEq(toOperator, 0 ether); - assertEq(toTnft, 16 ether); - assertEq(toBnft, 1 ether); - assertEq(toTreasury, 0 ether); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(newValidatorIds[0], 32 ether); - assertEq(toOperator, 0 ether); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 2 ether); - assertEq(toTreasury, 0 ether); - - vm.expectRevert("NOT_LIVE"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorIds[0]); - vm.expectRevert("NEED_FULL_WITHDRAWAL"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - - vm.expectRevert("NOT_EXITED"); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getFullWithdrawalPayouts(newValidatorIds[0]); - vm.expectRevert("NOT_EXITED"); - managerInstance.batchFullWithdraw(newValidatorIds); - - managerInstance.batchFullWithdraw(validatorIds); - - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - // validatorIds[0] is gone, thus, calling its {calculateTVL, getRewardsPayouts} should fail - vm.expectRevert(); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorIds[0], 32 ether); - vm.expectRevert(); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(validatorIds[0]); - - // newValidatorIds[0] is still live - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(newValidatorIds[0], 32 ether); - assertEq(toOperator, 0); - assertEq(toTnft, 30 ether); - assertEq(toBnft, 2 ether); - assertEq(toTreasury, 0); - - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.getRewardsPayouts(newValidatorIds[0]); - assertEq(toOperator, 0); - assertEq(toTnft, 0); - assertEq(toBnft, 0); - assertEq(toTreasury, 0); - } - - - function _add_validator_to_safe(uint256 validatorIdToShareSafeWith) internal returns (uint256) { - address operator = auctionInstance.getBidOwner(validatorIdToShareSafeWith); - vm.deal(operator, 100 ether); - vm.startPrank(operator); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * 1}(1, 0.1 ether); - vm.stopPrank(); - - address bnftStaker = BNFTInstance.ownerOf(validatorIdToShareSafeWith); - uint256 lp_balance = address(liquidityPoolInstance).balance; - vm.startPrank(bnftStaker); - vm.deal(bnftStaker, 2 ether); - uint256[] memory newValidatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1, validatorIdToShareSafeWith); - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(newValidatorIds); - liquidityPoolInstance.batchRegister(zeroRoot, newValidatorIds, depositDataArray, depositDataRootsForApproval, sig); - vm.stopPrank(); - - assertEq(uint8(managerInstance.phase(newValidatorIds[0])), uint8(IEtherFiNode.VALIDATOR_PHASE.WAITING_FOR_APPROVAL)); - } - - function test_mainnet_partialWithdraw_after_upgrade() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - uint256 validatorId = 2285; - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = validatorId; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - - _transferTo(nodeAddress, 1 ether); - - vm.prank(owner); - managerInstance.batchQueueRestakedWithdrawal(validatorIds); - _moveClock(7 * 7200); - - // Success - vm.prank(managerInstance.owner()); - managerInstance.partialWithdraw(validatorId); - - _transferTo(nodeAddress, 1 ether); - vm.prank(owner); // alice is admin - managerInstance.batchQueueRestakedWithdrawal(validatorIds); - - hoax(TNFTInstance.ownerOf(validatorId)); - managerInstance.batchSendExitRequest(validatorIds); - - // `partialWithdraw` Fail, if there is any pending exit request - vm.prank(managerInstance.owner()); - vm.expectRevert("PENDING_EXIT_REQUEST"); - managerInstance.partialWithdraw(validatorId); - } - - function test_mainnet_launch_validator_with_reserved_version1_safe() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - address etherFiNode = managerInstance.unusedWithdrawalSafes(managerInstance.getUnusedWithdrawalSafesLength() - 1); - - assertEq(IEtherFiNode(etherFiNode).version(), 1); - assertEq(IEtherFiNode(etherFiNode).numAssociatedValidators(), 0); - - uint256[] memory newValidatorIds = launch_validator(1, 0, false); - address newEtherFiNode = managerInstance.etherfiNodeAddress(newValidatorIds[0]); - - assertEq(etherFiNode, newEtherFiNode); - assertEq(IEtherFiNode(newEtherFiNode).version(), 1); - assertEq(IEtherFiNode(newEtherFiNode).numAssociatedValidators(), 1); - } - - function test_mainnet_launch_validator_sharing_version0_safe() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - uint256 validatorId = 2285; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertEq(IEtherFiNode(etherFiNode).version(), 1); - assertEq(IEtherFiNode(etherFiNode).numAssociatedValidators(), 1); - - uint256[] memory newValidatorIds = launch_validator(1, validatorId, false, BNFTInstance.ownerOf(validatorId), auctionInstance.getBidOwner(validatorId)); - address newEtherFiNode = managerInstance.etherfiNodeAddress(newValidatorIds[0]); - - assertEq(etherFiNode, newEtherFiNode); - assertEq(IEtherFiNode(etherFiNode).version(), 1); - assertEq(IEtherFiNode(etherFiNode).numAssociatedValidators(), 2); - } - - function test_mainnet_launch_validator_cancel_afeter_deposit_while_sharing_version0_safe() public { - initializeRealisticFork(MAINNET_FORK); - - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - uint256 validatorId = 23835; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertEq(IEtherFiNode(etherFiNode).version(), 1); - assertEq(IEtherFiNode(etherFiNode).numAssociatedValidators(), 1); - - address operator = auctionInstance.getBidOwner(validatorId); - vm.deal(operator, 100 ether); - vm.startPrank(operator); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * 1}(1, 0.1 ether); - vm.stopPrank(); - - address bnftStaker = 0x5836152812568244760ba356B5f3838Aa5B672e0; - uint256 lp_balance = address(liquidityPoolInstance).balance; - vm.startPrank(bnftStaker); - uint256[] memory newValidatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1, validatorId); - vm.stopPrank(); - address newEtherFiNode = managerInstance.etherfiNodeAddress(newValidatorIds[0]); - - assertEq(etherFiNode, newEtherFiNode); - assertEq(IEtherFiNode(etherFiNode).version(), 1); - assertEq(IEtherFiNode(etherFiNode).numAssociatedValidators(), 1); - - vm.startPrank(bnftStaker); - liquidityPoolInstance.batchCancelDeposit(newValidatorIds); - vm.stopPrank(); - - assertEq(lp_balance, address(liquidityPoolInstance).balance); - assertEq(managerInstance.etherfiNodeAddress(newValidatorIds[0]), address(0)); - assertEq(IEtherFiNode(etherFiNode).numAssociatedValidators(), 1); - } - - - // Zellic audit - Cancel validator deposit with version 0 safe fails - function test_mainnet_cancel_intermediate_validator() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - address operator = 0x1876ECcb4eDd3ed95051c64824430fc7f1C8763c; - vm.deal(operator, 100 ether); - vm.startPrank(operator); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * 1}(1, 0.1 ether); - vm.stopPrank(); - - address bnftStaker = 0x5836152812568244760ba356B5f3838Aa5B672e0; - vm.startPrank(bnftStaker); - uint256[] memory validatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1); - vm.stopPrank(); - - _upgrade_etherfi_nodes_manager_contract(); - _upgrade_etherfi_node_contract(); - _upgrade_staking_manager_contract(); - _upgrade_liquidity_pool_contract(); - - vm.startPrank(bnftStaker); - liquidityPoolInstance.batchCancelDeposit(validatorIds); - vm.stopPrank(); - } - - function test_ExitOneAmongMultipleValidators() public { - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 0); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 0); - - // launch 3 more validators - uint256[] memory newValidatorIds = launch_validator(3, validatorId, false); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 4); - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 0); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 0); - - // Send exit request to the 2nd one - hoax(TNFTInstance.ownerOf(newValidatorIds[0])); - managerInstance.batchSendExitRequest(_to_uint256_array(newValidatorIds[0])); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 4); - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 1); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 0); - - // Exit the 2nd one - uint256 validatorToExit = IEtherFiNode(etherfiNode).associatedValidatorIds(1); - _transferTo(managerInstance.etherfiNodeAddress(validatorToExit), 16 ether); - - uint256[] memory validatorIdsToExit = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](1); - validatorIdsToExit[0] = validatorToExit; - exitTimestamps[0] = uint32(block.timestamp); - hoax(managerInstance.owner()); - managerInstance.processNodeExit(validatorIdsToExit, exitTimestamps); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 4); - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 1); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 1); - - managerInstance.fullWithdraw(validatorToExit); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 3); - assertEq(IEtherFiNode(etherfiNode).numExitRequestsByTnft(), 0); - assertEq(IEtherFiNode(etherfiNode).numExitedValidators(), 0); - - assertEq(managerInstance.etherfiNodeAddress(validatorToExit), address(0)); - for (uint256 i = 0; i < IEtherFiNode(etherfiNode).numAssociatedValidators(); i++) { - uint256 valId = IEtherFiNode(etherfiNode).associatedValidatorIds(i); - address safe = managerInstance.etherfiNodeAddress(valId); - - assertEq(safe, etherfiNode); - } - } - - function test_lp_as_bnft_holders_cant_mix_up_1() public { - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - vm.deal(alice, 10000 ether); - vm.startPrank(alice); - // liquidityPoolInstance.updateBnftMode(true); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * 1}(1, 0.1 ether); - liquidityPoolInstance.deposit{value: 32 ether * 1}(); - - // launch 1 more validators - vm.expectRevert("WRONG_BNFT_OWNER"); - uint256[] memory newValidatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1, validatorId); - } - - function test_lp_as_bnft_holders_cant_mix_up_2() public { - uint256[] memory validatorIds = launch_validator(1, 0, true); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - vm.deal(alice, 10000 ether); - vm.startPrank(alice); - // liquidityPoolInstance.updateBnftMode(false); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * 1}(1, 0.1 ether); - liquidityPoolInstance.deposit{value: 30 ether * 1}(); - - // launch 1 more validators - vm.expectRevert("WRONG_BNFT_OWNER"); - uint256[] memory newValidatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1, validatorId); - } - - function test_PartialWithdrawalOfPrincipalFails() public { - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - // launch 3 more validators - uint256[] memory newValidatorIds = launch_validator(3, validatorId, false); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 4); - - uint256[] memory validatorIdsToExit = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp); - - // Exit 1 among 4 - validatorIdsToExit[0] = newValidatorIds[0]; - _transferTo(etherfiNode, 16 ether); - - // Someone triggers paritalWithrdaw - // Before the Oracle marks it as EXITED - vm.prank(managerInstance.owner()); - vm.expectRevert("MUST_EXIT"); - managerInstance.partialWithdraw(validatorId); - - hoax(managerInstance.owner()); - managerInstance.processNodeExit(validatorIdsToExit, exitTimestamps); - - managerInstance.fullWithdraw(validatorIdsToExit[0]); - } - - function test_TnftTransferFailsWithMultipleValidators_Fails() public { - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint256 validatorId = validatorIds[0]; - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); - - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - // launch 3 more validators - uint256[] memory newValidatorIds = launch_validator(3, validatorId, false); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 4); - - address tnftOwner = TNFTInstance.ownerOf(validatorId); - vm.prank(tnftOwner); - vm.expectRevert("numAssociatedValidators != 1"); - TNFTInstance.transferFrom(tnftOwner, bob, validatorId); - - uint256[] memory validatorIdsToExit = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = uint32(block.timestamp); - - // Exit 1 among 4 - validatorIdsToExit[0] = newValidatorIds[0]; - _transferTo(etherfiNode, 16 ether); - hoax(managerInstance.owner()); - managerInstance.processNodeExit(validatorIdsToExit, exitTimestamps); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 4); - - managerInstance.fullWithdraw(validatorIdsToExit[0]); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 3); - - // Still fails - vm.prank(tnftOwner); - vm.expectRevert("numAssociatedValidators != 1"); - TNFTInstance.transferFrom(tnftOwner, bob, validatorId); - - // Exit 1 among 3 - validatorIdsToExit[0] = newValidatorIds[1]; - _transferTo(etherfiNode, 16 ether); - hoax(managerInstance.owner()); - managerInstance.processNodeExit(validatorIdsToExit, exitTimestamps); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 3); - - managerInstance.fullWithdraw(validatorIdsToExit[0]); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 2); - - // Still fails - vm.prank(tnftOwner); - vm.expectRevert("numAssociatedValidators != 1"); - TNFTInstance.transferFrom(tnftOwner, bob, validatorId); - - // Exit 1 among 2 - validatorIdsToExit[0] = newValidatorIds[2]; - _transferTo(etherfiNode, 16 ether); - hoax(managerInstance.owner()); - managerInstance.processNodeExit(validatorIdsToExit, exitTimestamps); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 2); - - // Still fails - vm.prank(tnftOwner); - vm.expectRevert("numAssociatedValidators != 1"); - TNFTInstance.transferFrom(tnftOwner, bob, validatorId); - - - managerInstance.fullWithdraw(validatorIdsToExit[0]); - assertEq(IEtherFiNode(etherfiNode).numAssociatedValidators(), 1); - - // Now succeeds - vm.prank(tnftOwner); - TNFTInstance.transferFrom(tnftOwner, bob, validatorId); - } - - // Zellic-Audit-Issue 1 - function test_CacnelAfterBeingMarkedExited_fails() public { - vm.deal(alice, 10000 ether); - - uint256[] memory validatorIds = launch_validator(1, 0, false); - uint32[] memory exitTimestamps = new uint32[](1); - exitTimestamps[0] = 1; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); - - assertTrue(managerInstance.phase(validatorIds[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE); - - vm.startPrank(alice); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - IEtherFiNodesManager.ValidatorInfo memory info = managerInstance.getValidatorInfo(validatorIds[0]); - assertTrue(info.phase == IEtherFiNode.VALIDATOR_PHASE.EXITED); - - uint256[] memory bidIds = auctionInstance.createBid{value: 0.2 ether}(2, 0.1 ether); - liquidityPoolInstance.deposit{value: 60 ether}(); - uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, 2); - - vm.expectRevert("INVALID_PHASE_TRANSITION"); - liquidityPoolInstance.batchCancelDeposit(validatorIds); - } - - // Zellic-Audit-Issue 2 - function test_SendingMultipleExitRequests_fails() public { - vm.startPrank(TNFTInstance.ownerOf(bidId[0])); - - managerInstance.batchSendExitRequest(_to_uint256_array(bidId[0])); - - vm.expectRevert("INVALID"); - managerInstance.batchSendExitRequest(_to_uint256_array(bidId[0])); - } - - // Zellic-Audit-Issue 4 - function test_wrong_staker_on_fails_1() public { - vm.deal(alice, 100000 ether); - - vm.startPrank(alice); - liquidityPoolInstance.deposit{value: 32 ether * 1}(); - - registerAsBnftHolder(alice); - nodeOperatorManagerInstance.registerNodeOperator(aliceIPFSHash, 5); - - { - uint256[] memory bidId1 = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidId1, 1); - } - - uint256[] memory bidId1 = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId1, false); - - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(bidId1); - - vm.expectRevert("Wrong flow"); - liquidityPoolInstance.batchRegister(zeroRoot, bidId1, depositDataArray, depositDataRootsForApproval, sig); - vm.stopPrank(); - } - - // Zellic-Audit-Issue 4 - function test_wrong_staker_on_fails_2() public { - vm.deal(alice, 100000 ether); - - vm.startPrank(alice); - liquidityPoolInstance.deposit{value: 32 ether * 1}(); - - registerAsBnftHolder(alice); - nodeOperatorManagerInstance.registerNodeOperator(aliceIPFSHash, 5); - - uint256[] memory bidId1 = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - liquidityPoolInstance.batchDeposit(bidId1, 1); - - uint256[] memory bidId2 = auctionInstance.createBid{value: 0.4 ether}(1, 0.4 ether); - stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId2, false); - - // Confirm that the LP flow can't affect the 32 ETH staker flow - { - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(bidId2); - vm.expectRevert("Wrong flow"); - liquidityPoolInstance.batchRegister(zeroRoot, bidId2, depositDataArray, depositDataRootsForApproval, sig); - } - - // Confirm that the 32ETH flow can't affect the LP flow - { - IStakingManager.DepositData[] memory depositDataArray = _prepareForDepositData(bidId1, 32 ether); - vm.expectRevert("Wrong flow"); - stakingManagerInstance.batchRegisterValidators(zeroRoot, bidId1, depositDataArray); - } - - vm.stopPrank(); - } - - function test_mainnet_pepe_calculateTVL() public { - initializeRealisticFork(MAINNET_FORK); - - uint256 validatorId = 66992; - address etherFiNode = managerInstance.etherfiNodeAddress(validatorId); - address eigenPod = managerInstance.getEigenPod(validatorId); - - (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - managerInstance.numAssociatedValidators(validatorId); - (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - - // TODO: this is broken and the Oracle handles these calculations separately - // This needs to be fixed - // _transferTo(eigenPod, 32 ether); - // (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 0 ether); - } - - //************************************************************ - //** Eigenlayer Slashing Upgrade ** - //************************************************************ - - - - function test_execute_eigenlayer_slashing_upgrade() public { - - // why is this a persistent global? - bool originalSetting = shouldSetupRoleRegistry; - updateShouldSetRoleRegistry(false); - - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - // set back to original value - updateShouldSetRoleRegistry(originalSetting); - - // simulate executing eigenlayers slashing upgrade from their timelock - vm.warp(block.timestamp + 12 days); - IEigenlayerTimelock eigenLayerTimelock = IEigenlayerTimelock(0xC06Fd4F821eaC1fF1ae8067b36342899b57BAa2d); - vm.prank(address(0x461854d84Ee845F905e0eCf6C288DDEEb4A9533F)); - eigenLayerTimelock.execute( - 0x369e6F597e22EaB55fFb173C6d9cD234BD699111, - 0, - hex"6a76120200000000000000000000000040a2accbd92bca938b02010e17a5b8929b49130d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea00000000000000000000000000000000000000000000000000000000000000d248d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000cc6008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec4000000000000000000000000135dda560e946695d6f155dacafc6f1f25c1f5af000000000000000000000000a396d855d70e1a1ec1a0199adb9845096683b6a2008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec400000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a000000000000000000000000a75112d1df37fa53a431525cd47a7d7facea7e73008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000007750d328b314effa365a0402ccfd489b80b0adda000000000000000000000000a505c0116ad65071f0130061f94745b7853220ab008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec4000000000000000000000000858646372cc42e1a627fce94aa7a7033e7cf075a000000000000000000000000ba4b2b8a076851a3044882493c2e36503d50b925005a2a4f2f3c18f09179b6703e63d9edd165909073000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000243659cfe6000000000000000000000000b132a8dad03a507f1b9d2f467a4936df2161c63e008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec400000000000000000000000091e677b07f7af907ec9a428aafa9fc14a0d3a3380000000000000000000000009801266cbbbe1e94bb9daf7de8d61528f49cec77008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec4000000000000000000000000acb55c530acdb2849e6d4f36992cd8c9d50ed8f700000000000000000000000090b074ddd680bd06c72e28b09231a0f848205729000ed6703c298d28ae0878d1b28e88ca87f9662fe9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000243659cfe60000000000000000000000000ec17ef9c00f360db28ca8008684a4796b11e456008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000005e4c39ad7a3e881585e383db9827eb4811f6f6470000000000000000000000001b97d8f963179c0e17e5f3d85cdfd9a31a49bc66008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec400000000000000000000000093c4b944d05dfe6df7645a86cd2206016c51564d000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000001bee69b7dfffa4e2d53c2a2df135c388ad25dcd2000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec400000000000000000000000054945180db7943c0ed0fee7edab2bd24620256bc000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000009d7ed45ee2e8fc5482fa2428f15c971e6369011d000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec400000000000000000000000013760f50a9d7377e4f20cb8cf9e4c26586c658ff000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec4000000000000000000000000a4c637e0f704745d182e4d38cab7e7485321d059000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec400000000000000000000000057ba429517c3473b6d34ca9acd56c0e735b94c02000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000000fe4f44bee93503346a3ac9ee5a26b130a5796d6000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000007ca911e83dabf90c90dd3de5411a10f1a6112184000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec40000000000000000000000008ca7a5d6f3acd3a7a8bc468a8cd0fb14b6bd28b6000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec4000000000000000000000000ae60d8180437b5c34bb956822ac2710972584473000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf178008b9566ada63b64d1e1dcf1418b43fd1433b724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499a88ec4000000000000000000000000298afb19a105d59e74658c4c334ff360bade6dd2000000000000000000000000afda870d4a94b9444f9f22a0e61806178b6bf1780091e677b07f7af907ec9a428aafa9fc14a0d3a33800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024fabc1cbc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041000000000000000000000000c06fd4f821eac1ff1ae8067b36342899b57baa2d00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000", - 0, - 0 - ); - - IEtherFiNode etherfiNode = IEtherFiNode(address(0xe67d839eE1236bB3a82bB54d8056B736bB695B84)); - IEigenPod eigenPod = IEigenPod(address(0xeFa4d4664757019A33C1Db94789A4EddeeB7c365)); - IDelegationManager delegationManager = IDelegationManager(managerInstance.delegationManager()); - - // can't easily generate proofs until actual release - // so poke some memory in eigenpod to give us some withdrawableETH - { - bytes32 slot = bytes32(uint256(52)); - bytes32 value = bytes32(uint256(40e9)); - - vm.store(address(eigenPod), slot, value); - console2.log(eigenPod.withdrawableRestakedExecutionLayerGwei()); - } - - // queue up withdrawal via etherfiNodesManager.processNodeExit() - uint256 queueBlock = block.number; - uint256 validatorID = 83199; - uint32 exitTimestamp = 5; // arbitrary - uint256[] memory validators = toArray_u256(validatorID); - uint32[] memory timestamps = toArray_u32(exitTimestamp); - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = delegationManager.beaconChainETHStrategy(); - vm.prank(managerInstance.owner()); - managerInstance.processNodeExit(validators, timestamps); - - - // fast forward so withdrawal can be completed - vm.roll(block.number + (7200 * 15)); - - // claim the withdrawal - vm.deal(address(eigenPod), 33 ether); - IDelegationManagerTypes.Withdrawal memory withdrawal; - withdrawal.staker = address(etherfiNode); - withdrawal.delegatedTo = address(0x17C5F0CC30Bd57B308b7f62600B415fD1335E1FE); - withdrawal.withdrawer = address(etherfiNode); - withdrawal.nonce = 4; - withdrawal.startBlock = uint32(queueBlock); - withdrawal.strategies = strategies; - withdrawal.scaledShares = toArray_u256(32 ether); // determines amount of eth withdrawn by mock - vm.prank(managerInstance.owner()); - managerInstance.completeQueuedWithdrawals(validators, toArray(withdrawal), true); - - // finalize the full withdrawal in our protocol - vm.prank(owner); - managerInstance.fullWithdraw(validatorID); - } - - - function test_slashingUpdateFullWithdrawal() public { - - // This test simulates the normal withdrawal of a 32 ether validator - - address admin = managerInstance.owner(); - uint256 validatorID = depositAndRegisterValidator(true); - uint32 exitTimestamp = 5; // arbitrary - - uint256[] memory validators = toArray_u256(validatorID); - uint32[] memory timestamps = toArray_u32(exitTimestamp); - - address eigenPod = managerInstance.getEigenPod(validatorID); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorID); - address tnftOwner = TNFTInstance.ownerOf(validatorID); - address bnftOwner = BNFTInstance.ownerOf(validatorID); - - // simulate 32 ether of withdrawable shares - MockDelegationManager delegationManager = MockDelegationManager(payable(address(managerInstance.delegationManager()))); - delegationManager.mockSet_withdrawableShares(etherfiNode, delegationManager.beaconChainETHStrategy(), 32 ether, 32 ether); - - // node should queue a withdrawal for 32 ether - vm.expectEmit(false, false, false, true); - emit mockEvent_queuedWithdrawalShares(32 ether); - - // exit the validator - vm.prank(owner); - managerInstance.processNodeExit(validators, timestamps); - - // because there are no staking rewards we expect 100% to be paid to TNFT and BNFT when completed - uint256 preTNFTBalance = tnftOwner.balance; - uint256 preBNFTBalance = bnftOwner.balance; - - // complete the withdrawal from eigenlayer - bool receiveAsTokens = true; - IDelegationManagerTypes.Withdrawal memory withdrawal; - withdrawal.scaledShares = toArray_u256(32 ether); // determines amount of eth withdrawn by mock - withdrawal.withdrawer = etherfiNode; // checked by EtherfiNode.processNodeExit() - withdrawal.staker = etherfiNode; // checked by EtherfiNode.processNodeExit() - vm.prank(owner); - managerInstance.completeQueuedWithdrawals(validators, toArray(withdrawal), receiveAsTokens); - - // finalize the full withdrawal in our protocol - vm.prank(owner); - managerInstance.fullWithdraw(validatorID); - - // we expect the same owner of TNFT and BNFT in this example - require(tnftOwner == bnftOwner, "different owners"); - require(preTNFTBalance + 32 ether == tnftOwner.balance, "TNFT owner did not receive expected funds"); - require(preBNFTBalance + 32 ether == bnftOwner.balance, "BNFT owner did not receive expected funds"); - } - - function test_slashingUpdateSlashedWithdrawalFinalValidator() public { - - // This test simulates the withdrawal of a validator that has been slashed by eigenlayer by 1 ether - // It is expected to succeed because when processing the final validator - // from both the eigenpod and the etherfiNode, all remaining funds are withdrawn - // regardless of if slashing has occurred - - address admin = managerInstance.owner(); - uint256 validatorID = depositAndRegisterValidator(true); - uint32 exitTimestamp = 5; // arbitrary - - uint256[] memory validators = toArray_u256(validatorID); - uint32[] memory timestamps = toArray_u32(exitTimestamp); - - address eigenPod = managerInstance.getEigenPod(validatorID); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorID); - address tnftOwner = TNFTInstance.ownerOf(validatorID); - address bnftOwner = BNFTInstance.ownerOf(validatorID); - - // simulate 31 ether of withdrawable shares, this validator has been slashed - MockDelegationManager delegationManager = MockDelegationManager(payable(address(managerInstance.delegationManager()))); - delegationManager.mockSet_withdrawableShares(etherfiNode, delegationManager.beaconChainETHStrategy(), 31 ether, 32 ether); - - // node should queue a withdrawal for 32 ether - // This is 32 instead of 31 because we queue the target deposit shares but - // eigenlayer will later apply the slashing - vm.expectEmit(false, false, false, true); - emit mockEvent_queuedWithdrawalShares(32 ether); - - // exit the validator - vm.prank(owner); - managerInstance.processNodeExit(validators, timestamps); - - // because there are no staking rewards we expect 100% to be paid to TNFT and BNFT when completed - uint256 preTNFTBalance = tnftOwner.balance; - uint256 preBNFTBalance = bnftOwner.balance; - - // complete the withdrawal from eigenlayer - bool receiveAsTokens = true; - IDelegationManagerTypes.Withdrawal memory withdrawal; - withdrawal.scaledShares = toArray_u256(31 ether); // determines amount of eth withdrawn by mock - withdrawal.withdrawer = etherfiNode; // checked by EtherfiNode.processNodeExit() - withdrawal.staker = etherfiNode; // checked by EtherfiNode.processNodeExit() - vm.prank(owner); - managerInstance.completeQueuedWithdrawals(validators, toArray(withdrawal), receiveAsTokens); - - // finalize the full withdrawal in our protocol - vm.prank(owner); - managerInstance.fullWithdraw(validatorID); - - // we expect the same owner of TNFT and BNFT in this example - require(tnftOwner == bnftOwner, "different owners"); - require(preTNFTBalance + 31 ether == tnftOwner.balance, "TNFT owner did not receive expected funds"); - require(preBNFTBalance + 31 ether == bnftOwner.balance, "BNFT owner did not receive expected funds"); - } - - function test_slashingUpdateSlashedWithdrawalIntermediateValidator() public { - - // This test simulates the withdrawal of a validator that has been slashed by eigenlayer by 1 ether - // It is expected to succeed because our contracts no longer revert in that case that there - // is less than 32 eth claimable even if not the final validator - - address admin = managerInstance.owner(); - uint256 validatorID = depositAndRegisterValidator(true); - uint32 exitTimestamp = 5; // arbitrary - - uint256[] memory validators = toArray_u256(validatorID); - uint32[] memory timestamps = toArray_u32(exitTimestamp); - - address eigenPod = managerInstance.getEigenPod(validatorID); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorID); - address tnftOwner = TNFTInstance.ownerOf(validatorID); - address bnftOwner = BNFTInstance.ownerOf(validatorID); - - // simulate 31 ether of withdrawable shares, this validator has been slashed - MockDelegationManager delegationManager = MockDelegationManager(address(managerInstance.delegationManager())); - delegationManager.mockSet_withdrawableShares(etherfiNode, delegationManager.beaconChainETHStrategy(), 31 ether, 32 ether); - - // simulate multiple validators tied to this etherfiNode - MockEigenPod(eigenPod).mockSet_activeValidatorCount(2); - vm.prank(address(managerInstance)); - IEtherFiNode(etherfiNode).updateNumberOfAssociatedValidators(1, 0); // increase by 1 - vm.prank(address(managerInstance)); - IEtherFiNode(etherfiNode).registerValidator(1337, true); // appends to `associatedValidatorIds` array - - // node should queue a withdrawal for 32 ether - // This is 32 instead of 31 because we queue the target deposit shares but - // eigenlayer will later apply the slashing - vm.expectEmit(false, false, false, true); - emit mockEvent_queuedWithdrawalShares(32 ether); - - // exit the validator - vm.prank(owner); - managerInstance.processNodeExit(validators, timestamps); - - // because there are no staking rewards we expect 100% to be paid to TNFT and BNFT when completed - uint256 preTNFTBalance = tnftOwner.balance; - uint256 preBNFTBalance = bnftOwner.balance; - - // complete the withdrawal from eigenlayer - bool receiveAsTokens = true; - IDelegationManagerTypes.Withdrawal memory withdrawal; - withdrawal.scaledShares = toArray_u256(31 ether); // determines amount of eth withdrawn by mock - withdrawal.withdrawer = etherfiNode; // checked by EtherfiNode.processNodeExit() - withdrawal.staker = etherfiNode; // checked by EtherfiNode.processNodeExit() - vm.prank(owner); - managerInstance.completeQueuedWithdrawals(validators, toArray(withdrawal), receiveAsTokens); - - // finalize the full withdrawal in our protocol - vm.prank(owner); - managerInstance.fullWithdraw(validatorID); - - // we expect the same owner of TNFT and BNFT in this example - require(tnftOwner == bnftOwner, "different owners"); - require(preTNFTBalance + 31 ether == tnftOwner.balance, "TNFT owner did not receive expected funds"); - require(preBNFTBalance + 31 ether == bnftOwner.balance, "BNFT owner did not receive expected funds"); - } - - function test_slashingUpdateWithdrawalFinalValidatorWithRewards() public { - - // This test simulates the withdrawal of the final validator in the eigenpod/etherfi Node - // where the validator has earned an extra 1 eth of staking rewards. - // It is expected that all 33 eth is claimed because it is the final validator - address admin = managerInstance.owner(); - uint256 validatorID = depositAndRegisterValidator(true); - uint32 exitTimestamp = 5; // arbitrary - - uint256[] memory validators = toArray_u256(validatorID); - uint32[] memory timestamps = toArray_u32(exitTimestamp); - - address eigenPod = managerInstance.getEigenPod(validatorID); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorID); - address tnftOwner = TNFTInstance.ownerOf(validatorID); - address bnftOwner = BNFTInstance.ownerOf(validatorID); - - // simulate 33 ether of withdrawable shares, this validator has earned rewards - MockDelegationManager delegationManager = MockDelegationManager(payable(address(managerInstance.delegationManager()))); - delegationManager.mockSet_withdrawableShares(etherfiNode, delegationManager.beaconChainETHStrategy(), 33 ether, 33 ether); - - // node should queue a withdrawal for 33 ether - vm.expectEmit(false, false, false, true); - emit mockEvent_queuedWithdrawalShares(33 ether); - - // exit the validator - vm.prank(owner); - managerInstance.processNodeExit(validators, timestamps); - - // complete the withdrawal from eigenlayer - bool receiveAsTokens = true; - IDelegationManagerTypes.Withdrawal memory withdrawal; - withdrawal.scaledShares = toArray_u256(33 ether); // determines amount of eth withdrawn by mock - withdrawal.withdrawer = etherfiNode; // checked by EtherfiNode.processNodeExit() - withdrawal.staker = etherfiNode; // checked by EtherfiNode.processNodeExit() - vm.prank(owner); - managerInstance.completeQueuedWithdrawals(validators, toArray(withdrawal), receiveAsTokens); - - // since there are additional staking rewards, expect payouts to tnft + bnft - // event(validator, node, toOperator, toTnft, toBnft, toTreasury) - vm.expectEmit(true, true, false, true); - emit FullWithdrawal(validatorID, etherfiNode, 0, 31 ether , 2 ether, 0); - - // finalize the full withdrawal in our protocol - vm.prank(owner); - managerInstance.fullWithdraw(validatorID); - - } - - function test_slashingUpdateWithdrawalIntermediateValidatorWithRewards() public { - - // This test simulates the withdrawal of a validator with multiple other validators remaining - address admin = managerInstance.owner(); - uint256 validatorID = depositAndRegisterValidator(true); - uint32 exitTimestamp = 5; // arbitrary - - uint256[] memory validators = toArray_u256(validatorID); - uint32[] memory timestamps = toArray_u32(exitTimestamp); - - address eigenPod = managerInstance.getEigenPod(validatorID); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorID); - address tnftOwner = TNFTInstance.ownerOf(validatorID); - address bnftOwner = BNFTInstance.ownerOf(validatorID); - - // simulate multiple validators tied to this etherfiNode - MockEigenPod(eigenPod).mockSet_activeValidatorCount(2); - vm.prank(address(managerInstance)); - IEtherFiNode(etherfiNode).updateNumberOfAssociatedValidators(1, 0); // increase by 1 - vm.prank(address(managerInstance)); - IEtherFiNode(etherfiNode).registerValidator(1337, true); // appends to `associatedValidatorIds` array - - // simulate multiple exited validators worth of withdrawable shares in the pod - MockDelegationManager delegationManager = MockDelegationManager(payable(address(managerInstance.delegationManager()))); - delegationManager.mockSet_withdrawableShares(etherfiNode, delegationManager.beaconChainETHStrategy(), 64 ether, 64 ether); - - // even though there is a lot of withdrawable shares, expect the exit - // to only queue up 32 eth of withdrawals because it is not the last validator - vm.expectEmit(false, false, false, true); - emit mockEvent_queuedWithdrawalShares(32 ether); - - // exit the validator - vm.prank(owner); - managerInstance.processNodeExit(validators, timestamps); - - // complete the withdrawal from eigenlayer - bool receiveAsTokens = true; - IDelegationManagerTypes.Withdrawal memory withdrawal; - withdrawal.scaledShares = toArray_u256(32 ether); // determines amount of eth withdrawn by mock - withdrawal.withdrawer = etherfiNode; // checked by EtherfiNode.processNodeExit() - withdrawal.staker = etherfiNode; // checked by EtherfiNode.processNodeExit() - vm.prank(owner); - managerInstance.completeQueuedWithdrawals(validators, toArray(withdrawal), receiveAsTokens); - - // finalize the full withdrawal in our protocol - vm.prank(owner); - managerInstance.fullWithdraw(validatorID); - } - - //------------------------------------------------------------------------------------------------------------------- - - //************************************************************ - // TODO: re-implement forked withdrawal testing with a new validator - // once mainnet changes are live - //************************************************************ - - /* - function _mainnet_369_verifyAndProcessWithdrawals(bool partialWithdrawal, bool fullWithdrawal) internal { - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - - assertEq(eigenPod.withdrawableRestakedExecutionLayerGwei(), 0); - - // verifyAndProcessWithdrawals - if (partialWithdrawal) { - address(eigenPod).call{value: 0, gas:1_000_000}(hex"e251ef5200000000000000000000000000000000000000000000000000000000663a4e1f00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000012a000000000000000000000000000000000000000000000000000000000000014008e405ed18605dbf438a1c0115d1a93b580ee4c942e2fc858ad34d3ba388d8b8600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060a2ad91774bccf4423b727a10983a04378d48f280e4217c7070b9523993fe7dca9ba15af405ee306ca32a4c14a1273df163e949a2a1f08a84d7c1566299987a9bbc5cf9c59bbd157bd7a24bc50c0d768b03c13135ac1a66536f959049155272ce00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002e00000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000005200000000000000000000000000000000000000000000000000000000000001ee8000000000000000000000000000000000000000000000000000000000000014a0000000000000000000000000000000000000000000000000000000000000003cc003aa3e58648d449af984b12c4b6de38bb4ee81226e8723838568d5c836e54e81e88000000000000000000000000000000000000000000000000000000000037a32766000000000000000000000000000000000000000000000000000000005b56176575dc15667e194cf6f1599bbad88920cc3d3b1705a332097fee2d6a7300000000000000000000000000000000000000000000000000000000000001406b1636db8408b53792ffcbec6a939d676a251c4460b40e7207abaaaafce49d109f9988088a5c04fb2f7246028a279031931b6d146eb83f3bf10258a4ede814ef269ef811551ebda0bafc785f05ff348c6659c2a3ada2be14195d4a11bf1f65d8bed0e770de8946c2182316aeaea88e5beb5f37fb8dcb73c2d6edbd014aa78ae410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fca1884a0e636a68b90cb425cc0ae5493378d8440714a375b238c9b6829c8034f5c265b1a8d6ddbfdf8194cb5016221f58d1508745501a87b38a7f26f18c2425302be53468d391fe41647064e0c42feaa9da337644b4f453b5181d88ec2e524e2a36e25ced18cdb69e1560a10f42aec4acd87e7661ff35501380e212b10e0e62000000000000000000000000000000000000000000000000000000000000006057740f000000000000000000000000000000000000000000000000000000000098247f9611876beb1c50172fe04b929f630929a3a2505c300b5308270bd2633eb088fed94ced6811e0b3005510e5716a3cb5f47ead00c6e1e7188b9f215c5ae100000000000000000000000000000000000000000000000000000000000000e07c672abcd627326ab27469aeeedf7f3e8555d2a441d26f4b47e5073bdf942ee7b46f0c01805fe212e15907981b757e6c496b0cb06664224655613dcec82505bbdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d7180d856275a577499986e02fe5c5ec408467b8cfe8c516c16690d09442438d1110000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b66e21951ffc563a13110d81c15d8de35c902069dc3f4c88c5dee9bc7a9344f5600000000000000000000000000000000000000000000000000000000000000a091da120100000000000000000000000000000000000000000000000000000000f765ee409227596ed00401db4253dd1d5743bab6897179f8674fc122166e87cb654e9b10b7bc1d94bfd5798b93c474342b9db20c6ac89f6ff2fcf61a97551fbf302be53468d391fe41647064e0c42feaa9da337644b4f453b5181d88ec2e524e2a36e25ced18cdb69e1560a10f42aec4acd87e7661ff35501380e212b10e0e620000000000000000000000000000000000000000000000000000000000000580cb2407d209fa474c16c43f1d270ddc493940f16a38c4e22d6aff4acf7fe1f5a6da3e83874cc698b20de0f69dbf968f7d311428c1f1aed27dd4da540f85a887ffbb2e6716d2b74c04a235e44404a65a7b874410c3c96c48f76331163e8c8a958be3f0f6d8c5f4d2fada206aa31ec732a528a973b084e27237ecc2a295572d3165db099c65a569940fcd85fa84263bd420161465c46eda435f7664c277799841f36e0810c823ef48aa94644831460140ebfcd3ca0808f2c84ae3dda7081b8e4d28be82c11bf888cdc1802d63f57600a2de146f01f27c06330981f68e62484274ca0a399fdbaf61f44c9930a63fc4860101de6c5d3b8d33720ad99b8b6661d47461e8d20440589af169db3ea8a7db8d8be6424bc5542c3b294fa7b1ad5cec25db48f510773bdba10f9625f9ed698cd947206934de80aa7a7347c342ecd8ffe566bd77a7dc6e3f299f679e082e95358c3446c156524aaeda5e5678b1b094cdc36205d4c23d65c99dec0d5a1b3fb632fa2b18f679912566087d79797a6095f666692d641021afc4676d3660ad3dc3e968de1b560beb4b1459f98caaf22f5347bc82c05045a5680a3a32a0b011780e4141e1b79f419c06785f9072b7e4294cd41603c4c4a3317ce1f96ef8d94fc8117137fc2e60bdf390d0e20706fff9a2528f444521e3f4993b7515e9948c579d2dc07fe18d46360ed484aa3fa132a892308d73c1108157f009fac8eb7825d1a7015beaf310feccc1a9bb5aa51615f2e5f70c815021ba90c1709dc89a3b4965a8e02bdb16cef4573bd2cdd5a7180ce5c5485083bea329f2d67a3c7ff0bdb031d7659e39167d723edd43aebb36e77116242dc0a341d89efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d5fb5effea691d833f3c53c00b390c08aaffa8294e48c7790f4d4ab18c4d943087eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c460485af574848585860d57ea2bd50835e0b5a2a296e0dcb014483c82debb54f506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d75701000000000000000000000000000000000000000000000000000000000000acd708000000000000000000000000000000000000000000000000000000000084ceaa4dde66e23c4e4c32636f7c2b0298bb449c4db731b90e5a39b4b264936bdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d719c5ebbb28e654845862cc16e46b200feecac45488e12f0d39cbac3b14b6608d2db82f769a33f407aa641459e217f4e1ec7ad7412d75092e1ca9243e1f0d976e10000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000005c045f8cf05beedeb84fcae091fcb2ee47004764d17f457990ae3896426107c7a3732b1992669e48276043927cf519ff38fc16607f58ad93459c4495f32899fea09472a67b3cad7814ea1c0c18d43bb75140be02840bfd89cd46aa112fecd74dd904eff388ad7b3fa8679413441b329d86682937c3769b9af03307d150912dd4cf967ebb5be4d7d11360483dbea5eb821ef42d625ef387fcf8eeef520a4941520bcd88969552805e6b880a95b8e51ebd43b5a0326a59d6234f884d6301e48007c23ab68ce7484c4eaa534b4a7e4ca941981911623be2e822bd4263b78f57fb13e8256fc4b4f33ce0b8a1d67d911d3b288a3f7bd83dba6a94732bbe91743e996ebe845609139d009d32bfacee0d976cb7764418115a056b1d6c960559e3ae81fb2ca124adeceb195bb9ddcace33bf9389857a49434ad906678722485a99b9e0d32a5d878c5edcf3de0d11c8801ec3922b84dd12f67ae0593a38c8301d033a5607ed890abff4d542f85ef28766754556da1ee16ee0a62975669ab998be62bda776ab8db898c4521a6b44b9a64b496324ae7891f4c093abea717debc156234668ae447aeac7009b41efa940362cbbecc5e5f96b7b54a1b5ad4f7c05474c193ba71d29439a821fb189ff9073fab3fadf25340b4e0d0b2ecca2119872ab94390f9697c84d73e0f8e59f5a87b9938ba93ab27ad6b054fb2fd353065ccc6322033f08a267a085ed31c844b806f4fcf2e2e5525ca19d95f9fe06fba90b08f20ce7cca8875268e15d87243d2608a90d2f409097f373ddfe878c15a593a9f2391162c38c39f99fce4a47409cf806957e75da4a22a6b45bacafa85dfab4af7b43c758ca12509c2f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fd3019e322e1007c697bc17c90409600efa985e2cb7746180afbcb40e100505718a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f4029150000000000000000000000000000000000000000000000000000000000d45b16000000000000000000000000000000000000000000000000000000000022510254e4f4544672d0eb19657a0be79a48c7cf5c472e13e3e277cfd44045137deb13b2d15e74fda4e56936c38b7cbf6eb1c20603e4ae799e763e41417095723eacadb74fb7a6f608f74e3650bcc308a325e6618e2ee58ec8463f404764d23a9feb2b5083e4002918f7db0955d1aa1f506f75f5fd24430fe4984a56f2b8d1e40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000089c37f28ca0901d81d9af4258607e2c0959e5c0e908470dac8c5027cd967f0ad2010000000000000000000000afd81a1f8062a383f9d5e067af3a6eb5f517102400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2c3030000000000000000000000000000000000000000000000000000000000f3c30300000000000000000000000000000000000000000000000000000000007e460400000000000000000000000000000000000000000000000000000000007e47040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004ae078f0200000000000000000000000000000000000000000000000000000000930f100000000000000000000000000000000000000000000000000000000000afd81a1f8062a383f9d5e067af3a6eb5f51710240000000000000000000000001543180100000000000000000000000000000000000000000000000000000000"); - } - if (fullWithdrawal) { - address(eigenPod).call{value: 0, gas:1_000_000}(hex"e251ef52000000000000000000000000000000000000000000000000000000006638fa3b00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000001400198834354f1ac0ae8a3ec4011b706e7a92e948d256a856a9a3e5e2e93b402a6700000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060ebbfe960bd141e77f95b0dd1951955996425cf85ce29076159ad3f47f92ca916c96893dc0d8b73a12310ced273450fbedc2e6c6cf2d620bd46685e3869143042a78115593b93f98909f98c3068e1cf7639232052413970cf1c8017bafc00e30e00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000460000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000010d500000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000008172978eb77844db83a9ef01dcbfbe0b7c7ad11056759193e8d3958d70f7785e4d51089000000000000000000000000000000000000000000000000000000000053fa3266000000000000000000000000000000000000000000000000000000000d9c14261ee928c31797186c89a831477580f0d3bc6098e4f9350bfecf7faa150000000000000000000000000000000000000000000000000000000000000140cf0b5956fe1d61b770a8de6fffd31b9c3f8fc775bc3901beb3039480f3786e57cfb96e0f7cde640f7ba9d33824f3be1975608519d2d2c90334ec06a0ff94c78420613ded12606d94c0db03794b3faea365c887903fbd0bb00aeba3e61799d83b5a4dea179c54ee96af9fccf0eafe1dc916a784e5c99c79c82f6e0ace928e694510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d06a859dcd26f8e77721786d904cac9467441b60014adebfd1c53a36475e08d486ef1de76000fd36f915a0349cd8a763eab2e30b118355f7d29f9a9153cfb3ac599e350eee2a8c703de1e86b09d872c48d797662ce7d7985cb027c14775b9cde536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c00000000000000000000000000000000000000000000000000000000000000607974100000000000000000000000000000000000000000000000000000000000c1351b147c463120ebcb0ad88eeb7a419e4bc3d7c028470bd5100cfff242761feacdf609c77f95b539ac5460b188d1c6d95149ea9b6ea74453f672e0dca1f8ca00000000000000000000000000000000000000000000000000000000000000e04770848f71241cb0132ae23a72dcea11cb58a2ab6506d12dc697701beb5ad53ab46f0c01805fe212e15907981b757e6c496b0cb06664224655613dcec82505bbdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d717e466931f54f3a09f6ccc9560d49542e6d4ed92c252fb90e9198abed364857830000000000000000000000000000000000000000000000000000000000000000f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b1c163e09fade42b03be003c55e37415cb3d10d5c1da601b3c62ccf149cb610a400000000000000000000000000000000000000000000000000000000000000a0f1c4da000000000000000000000000000000000000000000000000000000000015ba0de266825f5463fab0eca873bf9973a8eec557faa7a1bff9d4a7c68ca5baaf85706b6aae3e00b9737d6cf05fd942f1a1e3ac4469c8bede52be92b4259a41599e350eee2a8c703de1e86b09d872c48d797662ce7d7985cb027c14775b9cde536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c0000000000000000000000000000000000000000000000000000000000000580ccc56b18a55d998d7ab9b382ae063c915cd760d4e15b3adf43a6c409b70f2fd00abdb98c72a995f612149a87870c3ecf36750de9cd77d6c64b56e646846beb992fadd6eeaa6da03aeb4d9e51d293dcf4e18c4c964ae8d94ae66bcd7de70d33440ec142a21e471ccdec4880d59364999c157d803051863f9d4f66383898aa5a98c6f7c620b5ca9119a62f386d033fefa5b71e133146305c84bc1598a473d5da45e4f0286f5eaae92452191fd475ce6436bff1a8be5e48080d2fe01bc732929694f2d7d99760ac1b4c7b66c78bdcf8694e67f2473809057b02db03f6843c4c3eb9381fdc6d61d8039ee2a58c54da54d7194a5945fe8b4784d83a3d911deebf8d43b348e74b019611b93602acfc7950a47e73eded3e7917d5aebfd4a500fbe93b3aa2c093aa38d591b490461426b9e38c840957fb285149b789e0b8cbcc34bd9fe9217b0db835e8767d87b41c99fc4453d7f927b3a77193ba6c5b908ac7a38c7d6fb023133925aec522a3b82385be2e96b57a2bd82a7d4a02f94b420e5b0160ce1f122950d23a65b16be7db5e79cca1788918979d724db7b1cd9c4098a52d532db3a35e638be25190d875880a5738069b223c8bc6820cf3a380bd35c9a6371ca0de52038cdd03b6f3252a5765baefe6a1420824d0e4e3e386706fb029a7bf1308711c9760f6e42caabff33a492674530611e489b57791ffc687e5cda81d239226feed47d70c7cae3b1358a0ebfd75f87172e9e6167b2ce9ed2ae6d050d9bd9e0169c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c4798d0e92891c6bff8e0828a487fb7668a6ad8649993fa1e6f9f09d496f6d2329efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30d5fb5effea691d833f3c53c00b390c08aaffa8294e48c7790f4d4ab18c4d943087eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c460485af574848585860d57ea2bd50835e0b5a2a296e0dcb014483c82debb54f506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5fdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85eb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4f893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17fcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d756010000000000000000000000000000000000000000000000000000000000003c3a060000000000000000000000000000000000000000000000000000000000e97c869919e7a3846613f4bf3f72a86df0af9f9faabddb35008a266c54f74022db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71436c8971f0963e274db592074eef4608740fc2360665e961c7839a847d24ddd9771133e804d0a5ceaf4ced110260eb4941823d137d7d90d2ecc5e0bc48828f020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000005c045f8cf05beedeb84fcae091fcb2ee47004764d17f457990ae3896426107c7a3732b1992669e48276043927cf519ff38fc16607f58ad93459c4495f32899fea09472a67b3cad7814ea1c0c18d43bb75140be02840bfd89cd46aa112fecd74dd904eff388ad7b3fa8679413441b329d86682937c3769b9af03307d150912dd4cf967ebb5be4d7d11360483dbea5eb821ef42d625ef387fcf8eeef520a4941520bcd88969552805e6b880a95b8e51ebd43b5a0326a59d6234f884d6301e48007c23ab68ce7484c4eaa534b4a7e4ca941981911623be2e822bd4263b78f57fb13e8256fc4b4f33ce0b8a1d67d911d3b288a3f7bd83dba6a94732bbe91743e996ebe845609139d009d32bfacee0d976cb7764418115a056b1d6c960559e3ae81fb2ca124adeceb195bb9ddcace33bf9389857a49434ad906678722485a99b9e0d32a5d878c5edcf3de0d11c8801ec3922b84dd12f67ae0593a38c8301d033a5607ed890abff4d542f85ef28766754556da1ee16ee0a62975669ab998be62bda776ab8a537662433c44d2740e3c87e02304cd4a0554ad89ed03267b8b0106f04474dc13e22deead7ee01a015cf87c3a1c64e03d4d30e8e3ea2f0e41440402e3f12b0e539a821fb189ff9073fab3fadf25340b4e0d0b2ecca2119872ab94390f9697c846c02f5bcbc84ea5361fa90162f418ba33b1de0e010bfc33e1cf68eb3ab493399dae9312d93b698d08a504776d3e30937419f14d7fc50b0cf25b6076c6f29d838783e078f41855d97fa5f8ddafd16c71291a654d79b03d5b87e20cc6f17f522377a8b66f7436c8e7d8644a2bc37cd0445239bd4996de321d4b81b335286380fdef893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f4704e8367b63754276b4d91c5bcc943f1388380f743c7554c1ed3083d73c7bd68a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9cfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167e71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d731206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc021352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a467657cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe18869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636b5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7c6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc52f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362cbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c32755d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74f7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76ad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206fab261500000000000000000000000000000000000000000000000000000000003459160000000000000000000000000000000000000000000000000000000000cf134aae33d75aa66ba5a51906b0adfbaeeae302d3c82a20ce1dee250616d9b6e3b20a20d924029ce9cc5677310c9965596487bda5f842ab4625a0fff41aefa0e8381811d500984752da2fc221daa869d370eda79e41f5b15e1bf5a9d58e0a8aef897b9fbedb7d8d522483a67ee115d67cba2363f207742491dfbbebdbad36740000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000089c37f28ca0901d81d9af4258607e2c0959e5c0e908470dac8c5027cd967f0ad2010000000000000000000000afd81a1f8062a383f9d5e067af3a6eb5f517102400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2c3030000000000000000000000000000000000000000000000000000000000f3c30300000000000000000000000000000000000000000000000000000000007e460400000000000000000000000000000000000000000000000000000000007e47040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004230b9e0200000000000000000000000000000000000000000000000000000000930f100000000000000000000000000000000000000000000000000000000000afd81a1f8062a383f9d5e067af3a6eb5f51710240000000000000000000000004091267407000000000000000000000000000000000000000000000000000000"); - assertEq(eigenPod.withdrawableRestakedExecutionLayerGwei(), 32 ether / 1 gwei); - } - } - - function test_mainnet_369_verifyAndProcessWithdrawals() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - _mainnet_369_add_validator(); - - _mainnet_369_verifyAndProcessWithdrawals(false, true); - } - - function test_mainnet_369_processNodeExit_without_withdrawal_proved() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - IDelegationManager mgr = managerInstance.delegationManager(); - IEigenPodManager eigenPodManager = managerInstance.eigenPodManager(); - - // call `ProcessNodeExit` to initiate the queued withdrawal - uint256[] memory validatorIds = new uint256[](1); - uint32[] memory exitTimestamps = new uint32[](1); - validatorIds[0] = validatorId; - exitTimestamps[0] = uint32(block.timestamp); - - hoax(managerInstance.owner()); - vm.expectRevert("NO_FULLWITHDRAWAL_QUEUED"); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - } - - function test_mainnet_369_queueWithdrawals_by_rando_fails() public { - initializeRealisticFork(MAINNET_FORK); - _upgrade_etherfi_node_contract(); - _upgrade_etherfi_nodes_manager_contract(); - - _mainnet_369_verifyAndProcessWithdrawals(true, true); - - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - IDelegationManager mgr = managerInstance.delegationManager(); - IEigenPodManager eigenPodManager = managerInstance.eigenPodManager(); - - IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - IStrategy[] memory strategies = new IStrategy[](1); - uint256[] memory shares = new uint256[](1); - - strategies[0] = mgr.beaconChainETHStrategy(); - shares[0] = uint256(eigenPod.withdrawableRestakedExecutionLayerGwei()) * uint256(1 gwei); - params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ - strategies: strategies, - depositShares: shares, - __deprecated_withdrawer: nodeAddress - }); - - // Caller != withdrawer - vm.expectRevert("DelegationManager.queueWithdrawal: withdrawer must be staker"); - vm.prank(alice); - mgr.queueWithdrawals(params); - } - - function test_mainnet_369_processNodeExit_success() public returns (IDelegationManager.Withdrawal memory) { - // test_mainnet_369_verifyAndProcessWithdrawals(); - initializeRealisticFork(MAINNET_FORK); - - vm.warp(block.timestamp + 7 * 24 * 3600); - - uint256 validatorId = 338; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - IDelegationManager mgr = managerInstance.delegationManager(); - IEigenPodManager eigenPodManager = managerInstance.eigenPodManager(); - - // Calculate TVL does not work once the eigenPod's balance goes above 16 ether since we cannot tell if it is the reward or exited fund - // ether.fi will perform `verifyAndProcessWithdrawals` and `processNodeExit` to mark the validator as exited - // Then, it will call `calculateTVL` to get the correct TVL - vm.expectRevert(); - managerInstance.calculateTVL(validatorId, 0 ether); - - IDelegationManagerTypes.Withdrawal memory withdrawal; - IERC20[] memory tokens = new IERC20[](1); - { - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = mgr.beaconChainETHStrategy(); - uint256[] memory shares = new uint256[](1); - shares[0] = uint256(eigenPod.withdrawableRestakedExecutionLayerGwei()) * 1 gwei; - withdrawal = IDelegationManagerTypes.Withdrawal({ - - function test_mainnet_70300_queueWithdrawals() public { - initializeRealisticFork(MAINNET_FORK); - - _whitelist_completeQueuedWithdrawals(); - _upgrade_etherfi_nodes_manager_contract(); - - uint256 validatorId = 70300; - _perform_withdrawals(validatorId); - } - - function _perform_withdrawals(uint256 validatorId) internal { - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = validatorId; - - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - IDelegationManager mgr = managerInstance.delegationManager(); - - uint256 etherFiNodeBalance = address(nodeAddress).balance; - uint256 liquidityPoolBalance = address(liquidityPoolInstance).balance; - - // 1. Prepare for Params for `queueWithdrawals` - IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - IStrategy[] memory strategies = new IStrategy[](1); - uint256[] memory shares = new uint256[](1); - strategies[0] = mgr.beaconChainETHStrategy(); - shares[0] = uint256(eigenPod.withdrawableRestakedExecutionLayerGwei()) * uint256(1 gwei); - params[0] = IDelegationManager.QueuedWithdrawalParams({ - strategies: strategies, - shares: shares, - withdrawer: nodeAddress - }); - - // 2. Prepare for `completeQueuedWithdrawals` - IDelegationManager.Withdrawal memory withdrawal = - IDelegationManager.Withdrawal({ - staker: nodeAddress, - delegatedTo: mgr.delegatedTo(nodeAddress), - withdrawer: nodeAddress, - nonce: mgr.cumulativeWithdrawalsQueued(nodeAddress), - startBlock: uint32(block.number), - strategies: strategies, - scaledShares: shares - }); - - bytes32 withdrawalRoot = mgr.calculateWithdrawalRoot(withdrawal); - } - - - // 2. call `ProcessNodeExit` to initiate the queued withdrawal - uint256[] memory validatorIds = new uint256[](1); - { - uint32[] memory exitTimestamps = new uint32[](1); - validatorIds[0] = validatorId; - exitTimestamps[0] = uint32(block.timestamp); - - hoax(managerInstance.owner()); - managerInstance.processNodeExit(validatorIds, exitTimestamps); - // It calls `DelegationManager::undelegate` which emits the event `WithdrawalQueued` - } - - // 'calculateTVL' now works - managerInstance.calculateTVL(validatorId, 0 ether); - - // it reamins the same even after queueing the withdrawal until it is claimed - assertEq(eigenPod.withdrawableRestakedExecutionLayerGwei(), 32 ether / 1 gwei); - - return withdrawal; - } - - function test_mainnet_369_completeQueuedWithdrawal() public { - IDelegationManager.Withdrawal memory withdrawal = test_mainnet_369_processNodeExit_success(); - - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - IDelegationManager mgr = managerInstance.delegationManager(); - IEigenPodManager eigenPodManager = managerInstance.eigenPodManager(); - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = validatorId; - - // mgr.completeQueuedWithdrawal(withdrawal, tokens, 0, true); - IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](1); - withdrawals[0] = withdrawal; - - IERC20[] memory tokens = new IERC20[](1); - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeWithSelector(IDelegationManager.completeQueuedWithdrawal.selector, withdrawal, tokens, 0, true); - - // FAIL, the forward call is not allowed for `completeQueuedWithdrawal` - vm.expectRevert("NOT_ALLOWED"); - vm.prank(owner); - managerInstance.forwardExternalCall(validatorIds, data, address(managerInstance.delegationManager())); - - // FAIL, if the `minWithdrawalDelayBlocks` is not passed - vm.prank(owner); - vm.expectRevert("DelegationManager._completeQueuedWithdrawal: minWithdrawalDelayBlocks period has not yet passed"); - managerInstance.completeQueuedWithdrawals(validatorIds, withdrawals, true); - - // 1. Wait - // Wait 'minDelayBlock' after the `verifyAndProcessWithdrawals` - { - uint256 minDelayBlock = mgr.minWithdrawalDelayBlocks(); - vm.roll(block.number + minDelayBlock); - } - - // 2. DelegationManager.completeQueuedWithdrawal - uint256 prevEtherFiNodeAddress = address(nodeAddress).balance; - managerInstance.completeQueuedWithdrawals(validatorIds, withdrawals, true); - - assertEq(address(nodeAddress).balance, prevEtherFiNodeAddress + 32 ether); - assertEq(eigenPodManager.podOwnerDepositShares(nodeAddress), 0); - assertEq(eigenPod.withdrawableRestakedExecutionLayerGwei(), 0); - } - - function test_mainnet_369_fullWithdraw_success() public { - test_mainnet_369_completeQueuedWithdrawal(); - - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - - assertEq(IEtherFiNode(nodeAddress).associatedValidatorIds(IEtherFiNode(nodeAddress).associatedValidatorIndices(validatorId)), validatorId); - - managerInstance.fullWithdraw(validatorId); - - assertNotEq(IEtherFiNode(nodeAddress).associatedValidatorIds(IEtherFiNode(nodeAddress).associatedValidatorIndices(validatorId)), validatorId); - } - - function test_mainnet_369_fullWithdraw_without_completeQueuedWithdrawal() public { - IDelegationManager.Withdrawal memory withdrawal = test_mainnet_369_processNodeExit_success(); - - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - - vm.deal(nodeAddress, 32 ether); - - // Say the withdrawal safe (etherfi node contract) got >32 ether - // but that is not from the withdrawal, then it is not counted as the withdrawan principal - vm.expectRevert("INSUFFICIENT_BALANCE"); - managerInstance.fullWithdraw(validatorId); - } - - function _mainnet_369_add_validator() public { - uint256 validatorId = 369; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorId); - IEigenPod eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - IDelegationManager mgr = managerInstance.delegationManager(); - IEigenPodManager eigenPodManager = managerInstance.eigenPodManager(); - - assertEq(IEtherFiNode(nodeAddress).numAssociatedValidators(), 1); - - uint256 newValidatorId = _add_validator_to_safe(validatorId); - - assertEq(IEtherFiNode(nodeAddress).numAssociatedValidators(), 1); // the new validator is registered but not approved yet - } - */ - - - function _whitelist_completeQueuedWithdrawals() internal { - address target = address(managerInstance); - bytes4[] memory selectors = new bytes4[](1); - - // https://etherscan.io/address/0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A#writeProxyContract - selectors[0] = 0x33404396; // completeQueuedWithdrawals - - bytes memory data = abi.encodeWithSelector(EtherFiNodesManager.updateAllowedForwardedExternalCalls.selector, selectors[0], 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A, true); - _execute_timelock(target, data, true, false, true, false); - } - - function _completeQueuedWithdrawals(uint256[] memory validatorIds, IDelegationManager.Withdrawal[] memory withdrawals) internal { - IDelegationManager delegationMgr = managerInstance.delegationManager(); - IERC20[][] memory tokens = new IERC20[][](1); - tokens[0] = new IERC20[](1); - uint256[] memory middlewareTimesIndexes = new uint256[](1); - bool[] memory receiveAsTokens = new bool[](1); - middlewareTimesIndexes[0] = 0; - receiveAsTokens[0] = true; - tokens[0][0] = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeWithSelector(IDelegationManager.completeQueuedWithdrawals.selector, withdrawals, tokens, middlewareTimesIndexes, receiveAsTokens); +} - vm.prank(0x7835fB36A8143a014A2c381363cD1A4DeE586d2A); - managerInstance.forwardExternalCall(validatorIds, data, address(delegationMgr)); - } -} diff --git a/test/EtherFiNodesManager.t.sol b/test/EtherFiNodesManager.t.sol index b66a3da84..108c0403c 100644 --- a/test/EtherFiNodesManager.t.sol +++ b/test/EtherFiNodesManager.t.sol @@ -7,396 +7,9 @@ import "../src/EtherFiNode.sol"; import "forge-std/console2.sol"; contract EtherFiNodesManagerTest is TestSetup { - address etherFiNode; - uint256[] bidId; - EtherFiNode safeInstance; function setUp() public { setUpTests(); - - vm.expectRevert("Initializable: contract is already initialized"); - vm.prank(owner); - managerImplementation.initialize( - address(treasuryInstance), - address(auctionInstance), - address(stakingManagerInstance), - address(TNFTInstance), - address(BNFTInstance), - address(0), - address(0), - address(0) - ); - - vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - nodeOperatorManagerInstance.registerNodeOperator(_ipfsHash, 5); - - hoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - bidId = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - - startHoax(0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf); - - uint256[] memory bidIdArray = new uint256[](1); - bidIdArray[0] = bidId[0]; - - stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}( - bidIdArray, - false - ); - - etherFiNode = managerInstance.etherfiNodeAddress(bidId[0]); - - assertTrue( - managerInstance.phase(bidId[0]) == - IEtherFiNode.VALIDATOR_PHASE.STAKE_DEPOSITED - ); - - IStakingManager.DepositData[] - memory depositDataArray = new IStakingManager.DepositData[](1); - - bytes32 root = generateDepositRoot( - hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), - 32 ether - ); - IStakingManager.DepositData memory depositData = IStakingManager - .DepositData({ - publicKey: hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - signature: hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - depositDataRoot: root, - ipfsHashForEncryptedValidatorKey: "test_ipfs" - }); - - depositDataArray[0] = depositData; - - stakingManagerInstance.batchRegisterValidators(zeroRoot, bidId, depositDataArray); - vm.stopPrank(); - - assertTrue( - managerInstance.phase(bidId[0]) == IEtherFiNode.VALIDATOR_PHASE.LIVE - ); - - safeInstance = EtherFiNode(payable(etherFiNode)); - } - - function test_SetStakingRewardsSplit() public { - vm.expectRevert(EtherFiNodesManager.NotAdmin.selector); - vm.prank(bob); - managerInstance.setStakingRewardsSplit(100000, 100000, 400000, 400000); - - (uint64 treasury, uint64 nodeOperator, uint64 tnft, uint64 bnft) = managerInstance.stakingRewardsSplit(); - assertEq(treasury, 50000); - assertEq(nodeOperator, 50000); - assertEq(tnft, 815625); - assertEq(bnft, 84375); - - vm.prank(alice); - managerInstance.setStakingRewardsSplit(100000, 100000, 400000, 400000); - - (treasury, nodeOperator, tnft, bnft) = managerInstance.stakingRewardsSplit(); - assertEq(treasury, 100000); - assertEq(nodeOperator, 100000); - assertEq(tnft, 400000); - assertEq(bnft, 400000); - } - - function test_SetNonExitPenaltyPrincipal() public { - vm.expectRevert(EtherFiNodesManager.NotAdmin.selector); - vm.prank(bob); - managerInstance.setNonExitPenalty(300, 2 ether); - - assertEq(managerInstance.nonExitPenaltyPrincipal(), 1 ether); - - vm.prank(alice); - managerInstance.setNonExitPenalty(300, 2 ether); - - assertEq(managerInstance.nonExitPenaltyPrincipal(), 2 ether); - } - - function test_SetNonExitPenaltyDailyRate() public { - vm.expectRevert(EtherFiNodesManager.NotAdmin.selector); - vm.prank(bob); - managerInstance.setNonExitPenalty(300, 2 ether); - - vm.prank(alice); - managerInstance.setNonExitPenalty(5, 2 ether); - assertEq(managerInstance.nonExitPenaltyDailyRate(), 5); - } - - function test_SetEtherFiNodePhaseRevertsOnIncorrectCaller() public { - vm.expectRevert(EtherFiNodesManager.NotStakingManager.selector); - vm.prank(owner); - managerInstance.setValidatorPhase(bidId[0], IEtherFiNode.VALIDATOR_PHASE.LIVE); - } - - function test_RegisterEtherFiNodeRevertsOnIncorrectCaller() public { - vm.prank(address(stakingManagerInstance)); - address ws = managerInstance.allocateEtherFiNode(false); - - vm.expectRevert(EtherFiNodesManager.NotStakingManager.selector); - vm.prank(owner); - managerInstance.registerValidator(bidId[0], false, ws); - } - - function test_RegisterEtherFiNodeRevertsIfAlreadyRegistered() public { - vm.prank(address(stakingManagerInstance)); - address ws = managerInstance.allocateEtherFiNode(false); - - // Node is registered in setup - vm.expectRevert(EtherFiNodesManager.AlreadyInstalled.selector); - vm.prank(address(stakingManagerInstance)); - managerInstance.registerValidator(bidId[0], false, ws); - } - - function test_UnregisterValidatorRevertsOnIncorrectCaller() public { - vm.expectRevert(EtherFiNodesManager.NotStakingManager.selector); - vm.prank(owner); - managerInstance.unregisterValidator(bidId[0]); - } - - function test_getEigenPod() public { - initializeTestingFork(MAINNET_FORK); - - uint256 nonRestakedValidatorId = depositAndRegisterValidator(false); - assertEq(managerInstance.getEigenPod(nonRestakedValidatorId), address(0x0)); - - uint256 restakedValidatorId = depositAndRegisterValidator(true); - assert(managerInstance.getEigenPod(restakedValidatorId) != address(0x0)); - } - - function test_CreateEtherFiNode() public { - vm.prank(alice); - nodeOperatorManagerInstance.registerNodeOperator( - _ipfsHash, - 5 - ); - - hoax(alice); - bidId = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - - assertEq(managerInstance.etherfiNodeAddress(bidId[0]), address(0)); - - hoax(alice); - uint256[] memory processedBids = stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId, false); - - address node = managerInstance.etherfiNodeAddress(processedBids[0]); - assert(node != address(0)); - } - - function test_RegisterEtherFiNode() public { - vm.prank(alice); - nodeOperatorManagerInstance.registerNodeOperator( - _ipfsHash, - 5 - ); - - hoax(alice); - bidId = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - - assertEq(managerInstance.etherfiNodeAddress(bidId[0]), address(0)); - - hoax(alice); - uint256[] memory processedBids = stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId, false); - - address node = managerInstance.etherfiNodeAddress(processedBids[0]); - assert(node != address(0)); - - } - - function test_RegisterEtherFiNodeReusesAvailableSafes() public { - vm.prank(alice); - nodeOperatorManagerInstance.registerNodeOperator(_ipfsHash, 5); - - assertEq(managerInstance.getUnusedWithdrawalSafesLength(), 0); - - // create bid with no matching deposit yet - hoax(alice); - bidId = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - assertEq(managerInstance.etherfiNodeAddress(bidId[0]), address(0)); - assertEq(managerInstance.getUnusedWithdrawalSafesLength(), 0); - - // deposit - hoax(alice); - uint256[] memory processedBids = stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId, false); - address node = managerInstance.etherfiNodeAddress(processedBids[0]); - - // recycle the safe - vm.prank(alice); - stakingManagerInstance.batchCancelDeposit(processedBids); - assertEq(managerInstance.getUnusedWithdrawalSafesLength(), 1); - - // original premade safe should be on top of the stack after being recycled - assertEq(managerInstance.unusedWithdrawalSafes(0), node); - } - - // TODO(Dave): Remaining withdrawal-safe-pool Tests - // 1. add restaking to previously non-restaking node - // 2. restaking with previously restaked node - // 3. normal mode in previously restaked - - function test_UnregisterValidatorAfterFullWithdraw_fails() public { - address node = managerInstance.etherfiNodeAddress(bidId[0]); - assert(node != address(0)); - - uint256[] memory validatorsToReset = new uint256[](1); - uint32[] memory timeStamps = new uint32[](1); - validatorsToReset[0] = bidId[0]; - timeStamps[0] = uint32(block.timestamp); - uint256 validatorId = validatorsToReset[0]; - - // need to put the node in a terminal state before it can be unregistered - _transferTo(managerInstance.etherfiNodeAddress(validatorsToReset[0]), 32 ether); - vm.prank(alice); - managerInstance.processNodeExit(validatorsToReset, timeStamps); - - assertTrue(managerInstance.phase(validatorId) == IEtherFiNode.VALIDATOR_PHASE.EXITED); - assertEq(IEtherFiNode(node).version(), 1); - assertEq(IEtherFiNode(node).numAssociatedValidators(), 1); - assertEq(managerInstance.numAssociatedValidators(validatorId), 1); - assertEq(managerInstance.getNonExitPenalty(validatorId), 0); - assertEq(IEtherFiNode(node).numExitRequestsByTnft(), 0); - assertEq(IEtherFiNode(node).numExitedValidators(), 1); - assertEq(IEtherFiNode(node).isRestakingEnabled(), false); - - _moveClock(100000); - managerInstance.batchFullWithdraw(validatorsToReset); - - vm.startPrank(address(stakingManagerInstance)); - vm.expectRevert(); - managerInstance.unregisterValidator(bidId[0]); - } - - function test_SendExitRequestWorksCorrectly() public { - assertEq(managerInstance.isExitRequested(bidId[0]), false); - - hoax(alice); - vm.expectRevert("INVALID"); - managerInstance.batchSendExitRequest(_to_uint256_array(bidId[0])); - - hoax(0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf); - managerInstance.batchSendExitRequest(_to_uint256_array(bidId[0])); - - assertEq(managerInstance.isExitRequested(bidId[0]), true); - - uint256[] memory ids = new uint256[](1); - ids[0] = bidId[0]; - address etherFiNode = managerInstance.etherfiNodeAddress(bidId[0]); - IEtherFiNodesManager.ValidatorInfo memory info = managerInstance.getValidatorInfo(bidId[0]); - uint32 exitRequestTimestamp = info.exitRequestTimestamp; - - uint64 nonExitPenaltyPrincipal = managerInstance.nonExitPenaltyPrincipal(); - uint64 nonExitPenaltyDailyRate = managerInstance.nonExitPenaltyDailyRate(); - - assertEq(IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), 0); - - // 1 day passed - vm.warp(block.timestamp + (1 + 86400)); - assertEq(IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), 0.03 ether); - - vm.warp(block.timestamp + (1 + (86400 + 3600))); - assertEq(IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), 0.0591 ether); - - vm.warp(block.timestamp + (1 + 2 * 86400)); - assertEq( - IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), - 0.114707190000000000 ether - ); - - // 10 days passed - vm.warp(block.timestamp + (1 + 10 * 86400)); - assertEq( - IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), - 0.347163722539392386 ether - ); - - // 28 days passed - vm.warp(block.timestamp + (1 + 28 * 86400)); - assertEq( - IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), - 0.721764308786155954 ether - ); - - // 365 days passed - vm.warp(block.timestamp + (1 + 365 * 86400)); - assertEq( - IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), - 1 ether - ); - - // more than 1 year passed - vm.warp(block.timestamp + (1 + 366 * 86400)); - assertEq(IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), 1 ether); - - vm.warp(block.timestamp + (1 + 400 * 86400)); - assertEq(IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), 1 ether); - - vm.warp(block.timestamp + (1 + 1000 * 86400)); - assertEq(IEtherFiNode(etherFiNode).getNonExitPenalty(exitRequestTimestamp, uint32(block.timestamp)), 1 ether); - } - - function test_PausableModifierWorks() public { - hoax(alice); - managerInstance.pauseContract(); - - hoax(0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf); - vm.expectRevert("Pausable: paused"); - managerInstance.batchSendExitRequest(_to_uint256_array(bidId[0])); - - uint256[] memory ids = new uint256[](1); - ids[0] = bidId[0]; - - hoax(0x9154a74AAfF2F586FB0a884AeAb7A64521c64bCf); - vm.expectRevert("Pausable: paused"); - managerInstance.batchSendExitRequest(ids); - - uint32[] memory timeStamps = new uint32[](1); - ids[0] = block.timestamp; - - hoax(alice); - vm.expectRevert("Pausable: paused"); - managerInstance.processNodeExit(ids, timeStamps); - - hoax(alice); - vm.expectRevert("Pausable: paused"); - managerInstance.partialWithdraw(0); - - hoax(alice); - vm.expectRevert("Pausable: paused"); - managerInstance.batchPartialWithdraw(ids); - - hoax(alice); - vm.expectRevert("Pausable: paused"); - managerInstance.fullWithdraw(0); - - hoax(alice); - vm.expectRevert("Pausable: paused"); - managerInstance.batchFullWithdraw(ids); - - } - - function test_eip1271_signature() public { - address node = managerInstance.etherfiNodeAddress(bidId[0]); - bytes32 digestHash = keccak256(abi.encode("test")); - - // the signature signed by 'chad' is not accepted because he is not an admin - { - (uint8 v, bytes32 r, bytes32 s) = vm.sign(4, digestHash); - bytes memory signature = abi.encodePacked(r, s, v); - assertEq(EtherFiNode(payable(node)).isValidSignature(digestHash, signature), bytes4(0xffffffff)); - } - - // Chad becomes an admin - vm.prank(owner); - managerInstance.updateEigenLayerOperatingAdmin(chad, true); - assertTrue(managerInstance.operatingAdmin(chad)); - - // it works now - { - (uint8 v, bytes32 r, bytes32 s) = vm.sign(4, digestHash); - bytes memory signature = abi.encodePacked(r, s, v); - assertEq(EtherFiNode(payable(node)).isValidSignature(digestHash, signature), EtherFiNode.isValidSignature.selector); - } - } } diff --git a/test/EtherFiTimelock.sol b/test/EtherFiTimelock.sol index 22ebbe1db..d806068a9 100644 --- a/test/EtherFiTimelock.sol +++ b/test/EtherFiTimelock.sol @@ -281,12 +281,14 @@ contract TimelockTest is TestSetup { managerInstance.forwardExternalCall(nodes, datas, 0xec53bF9167f50cDEB3Ae105f56099aaaB9061F83); } + /* function test_add_updateEigenLayerOperatingAdmin() internal { initializeRealisticFork(MAINNET_FORK); address target = address(managerInstance); bytes memory data = abi.encodeWithSelector(EtherFiNodesManager.updateEigenLayerOperatingAdmin.selector, 0x44358b1cc2C296fFc7419835438D1BD97Ec1FB78, true); _execute_timelock(target, data, true, true, true, true); } + */ function test_efip4() public { initializeRealisticFork(MAINNET_FORK); @@ -342,6 +344,8 @@ contract TimelockTest is TestSetup { } } + // TODO(dave): rework? + /* function test_whitelist_DelegationManager() public { initializeRealisticFork(MAINNET_FORK); address target = address(managerInstance); @@ -372,6 +376,7 @@ contract TimelockTest is TestSetup { vm.stopPrank(); } + */ function test_unpause_liquifier() public { initializeRealisticFork(MAINNET_FORK); diff --git a/test/EtherFiViewer.t.sol b/test/EtherFiViewer.t.sol index 339679a81..97508a68f 100644 --- a/test/EtherFiViewer.t.sol +++ b/test/EtherFiViewer.t.sol @@ -17,6 +17,8 @@ contract EtherFiViewerTest is Test { etherFiViewer.initialize(address(0x8487c5F8550E3C3e7734Fe7DCF77DB2B72E4A848)); } + // TODO(dave): rework? + /* function test_EtherFiNodesManager() public { uint256[] memory validatorIds = new uint256[](2); validatorIds[0] = 25678; @@ -29,6 +31,7 @@ contract EtherFiViewerTest is Test { etherFiViewer.EtherFiNodesManager_splitBalanceInExecutionLayer(validatorIds); etherFiViewer.EtherFiNodesManager_withdrawableBalanceInExecutionLayer(validatorIds); } + */ } diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index 7315b9025..261d557a6 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -796,13 +796,13 @@ contract LiquidityPoolTest is TestSetup { // verify that created nodes have associated eigenPods IEtherFiNode node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[0])); - assertFalse(address(node.eigenPod()) == address(0x0)); + assertFalse(address(node.getEigenPod()) == address(0x0)); node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[1])); - assertFalse(address(node.eigenPod()) == address(0x0)); + assertFalse(address(node.getEigenPod()) == address(0x0)); node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[2])); - assertFalse(address(node.eigenPod()) == address(0x0)); + assertFalse(address(node.getEigenPod()) == address(0x0)); node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[3])); - assertFalse(address(node.eigenPod()) == address(0x0)); + assertFalse(address(node.getEigenPod()) == address(0x0)); } function test_RegisterAsBNFTHolder() public { diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 3545aa828..6174c8f4a 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -519,7 +519,14 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen regulationsManagerInstance.initialize(); regulationsManagerInstance.updateAdmin(alice, true); - node = new EtherFiNode(); + + revert("FILL IN ADDRESSES"); + address eigenPodManager; + address delegationManager; + address liquidityPool; + address etherFiNodesManager; + node = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager); + rETH = new TestERC20("Rocket Pool ETH", "rETH"); rETH.mint(alice, 10e18); @@ -650,6 +657,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen 3600 ); + /* managerInstance.initialize( address(treasuryInstance), address(auctionInstance), @@ -662,6 +670,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen ); managerInstance.updateAdmin(address(etherFiAdminInstance), true); managerInstance.updateAdmin(alice, true); + */ membershipManagerInstance.updateAdmin(alice, true); @@ -691,8 +700,8 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen vm.stopPrank(); vm.startPrank(alice); - managerInstance.setStakingRewardsSplit(0, 0, 1_000_000, 0); - managerInstance.setNonExitPenalty(300, 1 ether); + //managerInstance.setStakingRewardsSplit(0, 0, 1_000_000, 0); + //managerInstance.setNonExitPenalty(300, 1 ether); membershipManagerInstance.setTopUpCooltimePeriod(28 days); vm.stopPrank(); @@ -736,7 +745,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen etherFiOracleInstance.setEtherFiAdmin(address(etherFiAdminInstance)); liquidityPoolInstance.initializeOnUpgrade(address(auctionManagerProxy), address(liquifierInstance)); - stakingManagerInstance.initializeOnUpgrade(address(nodeOperatorManagerInstance), address(etherFiAdminInstance)); + //stakingManagerInstance.initializeOnUpgrade(address(nodeOperatorManagerInstance), address(etherFiAdminInstance)); auctionInstance.initializeOnUpgrade(address(membershipManagerInstance), 1 ether, address(etherFiAdminInstance), address(nodeOperatorManagerInstance)); membershipNftInstance.initializeOnUpgrade(address(liquidityPoolInstance)); @@ -780,11 +789,11 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen protocolRevenueManagerInstance.setAuctionManagerAddress(address(auctionInstance)); protocolRevenueManagerInstance.setEtherFiNodesManagerAddress(address(managerInstance)); - stakingManagerInstance.setEtherFiNodesManagerAddress(address(managerInstance)); - stakingManagerInstance.setLiquidityPoolAddress(address(liquidityPoolInstance)); - stakingManagerInstance.registerEtherFiNodeImplementationContract(address(node)); - stakingManagerInstance.registerTNFTContract(address(TNFTInstance)); - stakingManagerInstance.registerBNFTContract(address(BNFTInstance)); + //stakingManagerInstance.setEtherFiNodesManagerAddress(address(managerInstance)); + //stakingManagerInstance.setLiquidityPoolAddress(address(liquidityPoolInstance)); + //stakingManagerInstance.registerEtherFiNodeImplementationContract(address(node)); + //stakingManagerInstance.registerTNFTContract(address(TNFTInstance)); + //stakingManagerInstance.registerBNFTContract(address(BNFTInstance)); vm.stopPrank(); @@ -1216,7 +1225,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes32 depositRoot = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.getWithdrawalCredentials(createdBids[0]), + managerInstance.addressToWithdrawalCredentials(managerInstance.etherFiNodeFromId(createdBids[0])), 32 ether ); IStakingManager.DepositData memory depositData = IStakingManager @@ -1320,20 +1329,20 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes[] memory sig = new bytes[](_numValidators); for (uint256 i = 0; i < newValidators.length; i++) { - address safe = managerInstance.getWithdrawalSafeAddress( + address safe = address(managerInstance.getEigenPod( newValidators[i] - ); + )); root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(safe), + managerInstance.addressToWithdrawalCredentials(safe), 1 ether ); rootForApproval = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"ad899d85dcfcc2506a8749020752f81353dd87e623b2982b7bbfbbdd7964790eab4e06e226917cba1253f063d64a7e5407d8542776631b96c4cea78e0968833b36d4e0ae0b94de46718f905ca6d9b8279e1044a41875640f8cb34dc3f6e4de65", - managerInstance.generateWithdrawalCredentials(safe), + managerInstance.addressToWithdrawalCredentials(safe), 31 ether ); @@ -1447,7 +1456,14 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } function _upgrade_etherfi_node_contract() internal { - EtherFiNode etherFiNode = new EtherFiNode(); + + revert("FILL IN ADDRESSES"); + address eigenPodManager; + address delegationManager; + address liquidityPool; + address etherFiNodesManager; + + EtherFiNode etherFiNode = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager); address newImpl = address(etherFiNode); vm.prank(stakingManagerInstance.owner()); stakingManagerInstance.upgradeEtherFiNode(newImpl); @@ -1463,8 +1479,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen address newImpl = address(new StakingManager()); vm.prank(stakingManagerInstance.owner()); stakingManagerInstance.upgradeTo(newImpl); - - assert(stakingManagerInstance.isFullStakeEnabled() == false); } function _upgrade_liquidity_pool_contract() internal { @@ -1523,12 +1537,12 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes[] memory pubKey = new bytes[](_validatorIds.length); for (uint256 i = 0; i < _validatorIds.length; i++) { - address etherFiNode = managerInstance.etherfiNodeAddress(_validatorIds[i]); + address etherFiNode = managerInstance.etherFiNodeFromId(_validatorIds[i]); pubKey[i] = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; bytes32 root = generateDepositRoot( pubKey[i], hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), _depositAmount ); @@ -1554,7 +1568,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes32 root = generateDepositRoot( pubKey[i], hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.getWithdrawalCredentials(_validatorIds[i]), + managerInstance.addressToWithdrawalCredentials(managerInstance.etherFiNodeFromId(_validatorIds[i])), 1 ether ); depositDataArray[i] = IStakingManager.DepositData({ @@ -1567,7 +1581,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen depositDataRootsForApproval[i] = generateDepositRoot( pubKey[i], hex"ad899d85dcfcc2506a8749020752f81353dd87e623b2982b7bbfbbdd7964790eab4e06e226917cba1253f063d64a7e5407d8542776631b96c4cea78e0968833b36d4e0ae0b94de46718f905ca6d9b8279e1044a41875640f8cb34dc3f6e4de65", - managerInstance.getWithdrawalCredentials(_validatorIds[i]), + managerInstance.addressToWithdrawalCredentials(managerInstance.etherFiNodeFromId(_validatorIds[i])), 31 ether ); From 78abdbe60bd3fcbe844c2123a1b233312029e34b Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 29 Apr 2025 16:00:51 -0600 Subject: [PATCH 02/47] WIP: still sorting out dependencies --- script/deploys/DeployPhaseOne.s.sol | 2 + script/deploys/DeployPhaseTwo.s.sol | 4 +- script/specialized/RegisterValidator.s.sol | 2 +- script/specialized/UpdateAdminScripts.s.sol | 4 +- .../EtherFiNodesManagerUpgradeScript.s.sol | 8 +- .../StakingManagerUpgradeScript.s.sol | 6 +- src/EtherFiNodesManager.sol | 729 +----------------- src/StakingManager.sol | 6 +- src/helpers/EtherFiViewer.sol | 2 + src/interfaces/IEtherFiNodesManager.sol | 2 +- test/AuctionManager.t.sol | 16 +- test/BNFT.t.sol | 8 +- test/EigenLayerIntegration.t.sol | 6 +- test/EtherFiTimelock.sol | 9 +- test/LiquidityPool.t.sol | 38 +- test/MembershipManager.t.sol | 3 + test/MembershipManagerV0.t.sol | 2 + test/StakingManager.t.sol | 72 +- test/TNFT.t.sol | 4 +- test/eethPayoutUpgrade.t.sol | 4 +- 20 files changed, 123 insertions(+), 804 deletions(-) diff --git a/script/deploys/DeployPhaseOne.s.sol b/script/deploys/DeployPhaseOne.s.sol index 7c1d6ff7e..2f0fa9a29 100644 --- a/script/deploys/DeployPhaseOne.s.sol +++ b/script/deploys/DeployPhaseOne.s.sol @@ -127,10 +127,12 @@ contract DeployPhaseOne is Script { protocolRevenueManager.setAuctionManagerAddress(address(auctionManager)); protocolRevenueManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); + /* stakingManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); stakingManager.registerEtherFiNodeImplementationContract(address(etherFiNode)); stakingManager.registerTNFTContract(address(TNFTInstance)); stakingManager.registerBNFTContract(address(BNFTInstance)); + */ vm.stopBroadcast(); diff --git a/script/deploys/DeployPhaseTwo.s.sol b/script/deploys/DeployPhaseTwo.s.sol index 3ea414e85..d62c25bd3 100644 --- a/script/deploys/DeployPhaseTwo.s.sol +++ b/script/deploys/DeployPhaseTwo.s.sol @@ -167,9 +167,9 @@ contract DeployPhaseTwoScript is Script { IStakingManager(address(stakingManagerAddress)).updateAdmin(admin, true); // ILiquidityPool(address(liquidityPoolAddress)).updateAdmin(admin, true); IMembershipManager(address(membershipManagerAddress)).updateAdmin(admin, true); - IEtherFiNodesManager(address(managerAddress)).updateAdmin(admin, true); + //IEtherFiNodesManager(address(managerAddress)).updateAdmin(admin, true); } addressProvider.addContract(address(etherFiAdminProxy), "EtherFiAdmin"); } -} \ No newline at end of file +} diff --git a/script/specialized/RegisterValidator.s.sol b/script/specialized/RegisterValidator.s.sol index 3770bf8b3..e5f11f027 100644 --- a/script/specialized/RegisterValidator.s.sol +++ b/script/specialized/RegisterValidator.s.sol @@ -45,7 +45,7 @@ contract RegisterValidator is Script { _validatorIds[0] = 1; - address etherFiNode = managerInstance.etherfiNodeAddress(_validatorIds[0]); + address etherFiNode = managerInstance.etherFiNodeFromId(_validatorIds[0]); depositDataArray[0] = IStakingManager.DepositData({ publicKey: hex"ad85894db60881bcee956116beae6bc6934d7eca8317dc3084adf665be426a21a1855b5196a7515fd791bf0b6e3727c5", diff --git a/script/specialized/UpdateAdminScripts.s.sol b/script/specialized/UpdateAdminScripts.s.sol index c0f3394d3..c48bdc746 100644 --- a/script/specialized/UpdateAdminScripts.s.sol +++ b/script/specialized/UpdateAdminScripts.s.sol @@ -40,7 +40,7 @@ contract UpdateAdmins is Script { address admin = vm.envAddress("ADMIN"); - EtherFiNodesManager(payable(etherFiNodesManager)).updateAdmin(admin, true); + //EtherFiNodesManager(payable(etherFiNodesManager)).updateAdmin(admin, true); // ProtocolRevenueManager(payable(protocolRevenueManager)).updateAdmin(admin); // DEPRECATED AuctionManager(auctionManager).updateAdmin(admin, true); StakingManager(stakingManager).updateAdmin(admin, true); @@ -53,4 +53,4 @@ contract UpdateAdmins is Script { vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol b/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol index 6a91d681c..e4a451d7c 100644 --- a/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol +++ b/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol @@ -36,8 +36,8 @@ contract EtherFiNodesManagerUpgrade is Script { require(false); } - uint64 numberOfValidators = IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).numberOfValidators(); - address treasury = IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).treasuryContract(); + //uint64 numberOfValidators = IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).numberOfValidators(); + //address treasury = IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).treasuryContract(); vm.startBroadcast(deployerPrivateKey); @@ -47,8 +47,8 @@ contract EtherFiNodesManagerUpgrade is Script { // EtherFiNodesManagerInstance.initializeOnUpgrade(etherFiAdminAddress, eigenPodManager, delayedWithdrawalRouter, maxEigenlayerWithdrawals); // EtherFiNodesManagerInstance.initializeOnUpgrade2(delegationManager); - require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).numberOfValidators() == numberOfValidators); - require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).treasuryContract() == treasury); + //require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).numberOfValidators() == numberOfValidators); + //require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).treasuryContract() == treasury); // require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).maxEigenlayerWithdrawals() == maxEigenlayerWithdrawals); // require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); diff --git a/script/upgrades/StakingManagerUpgradeScript.s.sol b/script/upgrades/StakingManagerUpgradeScript.s.sol index a73609988..26ba9cf02 100644 --- a/script/upgrades/StakingManagerUpgradeScript.s.sol +++ b/script/upgrades/StakingManagerUpgradeScript.s.sol @@ -29,10 +29,10 @@ contract StakingManagerUpgrade is Script { StakingManager stakingManagerV2Implementation = new StakingManager(); stakingManagerInstance.upgradeTo(address(stakingManagerV2Implementation)); - stakingManagerInstance.initializeOnUpgrade(nodeOperatorManagerAddress, etherFiAdminAddress); + //stakingManagerInstance.initializeOnUpgrade(nodeOperatorManagerAddress, etherFiAdminAddress); - require(stakingManagerInstance.admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); + //require(stakingManagerInstance.admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index a8651cc8d..7cf507b37 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -74,8 +74,8 @@ contract EtherFiNodesManager is mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; */ - IEigenPodManager public immutable eigenPodManager; - IDelegationManager public immutable delegationManager; + address public immutable eigenPodManager; + address public immutable delegationManager; IStakingManager public immutable stakingManager; //----------------------------------------------------------------- @@ -100,6 +100,13 @@ contract EtherFiNodesManager is event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event QueuedRestakingWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, bytes32[] withdrawalRoots); + + error AlreadyLinked(); + error InvalidPubKeyLength(); + error InvalidCaller(); + event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, bytes pubkey); + event NodeDeployed(address indexed nodeAddress, uint256 indexed nodeNonce); + /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); @@ -111,6 +118,9 @@ contract EtherFiNodesManager is error InvalidParams(); + // TODO(dave): reimplement pausing with role registry + function pauseContract() external { _pause(); } + function unPauseContract() external { _unpause(); } function etherFiNodeFromId(uint256 id) public view returns (address) { // if the ID is a legacy validatorID use the old storage array @@ -138,7 +148,7 @@ contract EtherFiNodesManager is ///@notice Calculates the pubkey hash of a validator's pubkey as per SSZ spec function calculateValidatorPubkeyHash(bytes memory pubkey) public pure returns (bytes32) { - require(pubkey.length == 48, InvalidPubKeyLength()); + if (pubkey.length != 48) revert InvalidPubKeyLength(); return sha256(abi.encodePacked(pubkey, bytes16(0))); } @@ -147,26 +157,6 @@ contract EtherFiNodesManager is return abi.encodePacked(bytes1(0x01), bytes11(0x0), addr); } - /* - function getDeterministicNodeAddress(uint256 nodeNonce) public view returns (address) { - return Create2.computeAddress( - bytes32(nodeNonce), //salt - keccak256(abi.encodePacked(beaconProxyBytecode, abi.encode(podOwnerBeacon, ""))) //bytecode - ); - } - - function getWithdrawalCredentials(uint256 nodeNonce) public view returns (bytes memory) { - // this is fine even if the node doesn't exist yet - address nodeAddress = getDeterministicNodeAddress(nodeNonce); - return addressToWithdrawalCredentials(getDeterministicEigpenpodAddress(nodeAddress)); - } - */ - error AlreadyLinked(); - error InvalidPubKeyLength(); - error InvalidCaller(); - error InvalidPubKeyLength(); - event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, bytes pubkey); - event NodeDeployed(address indexed nodeAddress, uint256 indexed nodeNonce); function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external { if (msg.sender != address(stakingManager)) revert InvalidCaller(); @@ -247,697 +237,4 @@ contract EtherFiNodesManager is } } - /* - function forwardExternalCall(bytes32 pubkey, bytes calldata data, address target) external returns (bytes memory returnData) { - bytes32[] memory pubkeys = new bytes32[](1); - bytes[] calldata datas = new bytes[](1); - return forwardExternalCall(pubkeys, datas, target)[0]; - } - */ - - - /* - - /// @dev Sets the revenue splits on deployment - /// @dev AuctionManager, treasury and deposit contracts must be deployed first - /// @param _treasuryContract The address of the treasury contract for interaction - /// @param _auctionContract The address of the auction contract for interaction - /// @param _stakingManagerContract The address of the staking contract for interaction - /// @param _tnftContract The address of the TNFT contract for interaction - /// @param _bnftContract The address of the BNFT contract for interaction - function initialize( - address _treasuryContract, - address _auctionContract, - address _stakingManagerContract, - address _tnftContract, - address _bnftContract, - address _eigenPodManager, - address _delayedWithdrawalRouter, - address _delegationManager - ) external initializer { - __Ownable_init(); - __UUPSUpgradeable_init(); - __ReentrancyGuard_init(); - - SCALE = 1_000_000; - - treasuryContract = _treasuryContract; - stakingManagerContract = _stakingManagerContract; - - auctionManager = IAuctionManager(_auctionContract); - tnft = TNFT(_tnftContract); - bnft = BNFT(_bnftContract); - - maxEigenlayerWithdrawals = 5; - - eigenPodManager = IEigenPodManager(_eigenPodManager); - delayedWithdrawalRouter = IDelayedWithdrawalRouter(_delayedWithdrawalRouter); - delegationManager = IDelegationManager(_delegationManager); - } - - /// @notice Send the request to exit the validators as their T-NFT holder - /// The B-NFT holder must serve the request otherwise their bond will get penalized gradually - /// @param _validatorIds IDs of the validators - function batchSendExitRequest(uint256[] calldata _validatorIds) external whenNotPaused { - for (uint256 i = 0; i < _validatorIds.length; i++) { - uint256 _validatorId = _validatorIds[i]; - address etherfiNode = etherfiNodeAddress[_validatorId]; - - // require (msg.sender == tnft.ownerOf(_validatorId), "NOT_TNFT_OWNER"); - // require (phase(_validatorId) == IEtherFiNode.VALIDATOR_PHASE.LIVE, "NOT_LIVE"); - // require (!isExitRequested(_validatorId), "ASKED"); - require (msg.sender == tnft.ownerOf(_validatorId) && phase(_validatorId) == IEtherFiNode.VALIDATOR_PHASE.LIVE && !isExitRequested(_validatorId), "INVALID"); - - _updateEtherFiNode(_validatorId); - _updateExitRequestTimestamp(_validatorId, etherfiNode, uint32(block.timestamp)); - - emit NodeExitRequested(_validatorId); - } - } - - /// @notice Start a PEPE pod checkpoint balance proof. A new proof cannot be started until - /// the previous proof is completed - /// @dev Eigenlayer's PEPE proof system operates on pod-level and will require checkpoint proofs for - /// every single validator associated with the pod. For efficiency you will want to try to only - /// do checkpoints whene you wish to update most of the validators in the associated pod at once - function startCheckpoint(uint256 _validatorId, bool _revertIfNoBalance) external onlyAdmin { - address etherfiNode = etherfiNodeAddress[_validatorId]; - IEtherFiNode(etherfiNode).startCheckpoint(_revertIfNoBalance); - } - - // @notice you can delegate 1 additional wallet that is allowed to call startCheckpoint() and - // verifyWithdrawalCredentials() on behalf of this pod - /// @dev this will affect all validators in the pod, not just the provided validator - function setProofSubmitter(uint256 _validatorId, address _newProofSubmitter) external onlyAdmin { - address etherfiNode = etherfiNodeAddress[_validatorId]; - IEtherFiNode(etherfiNode).setProofSubmitter(_newProofSubmitter); - } - - /// @notice Once the node's exit & funds withdrawal from Beacon is observed, the protocol calls this function to process their exits. - /// @param _validatorIds The list of validators which exited - /// @param _exitTimestamps The list of exit timestamps of the validators - function processNodeExit( - uint256[] calldata _validatorIds, - uint32[] calldata _exitTimestamps - ) external onlyAdmin nonReentrant whenNotPaused { - if (_validatorIds.length != _exitTimestamps.length) revert InvalidParams(); - for (uint256 i = 0; i < _validatorIds.length; i++) { - uint256 _validatorId = _validatorIds[i]; - address etherfiNode = etherfiNodeAddress[_validatorId]; - - _updateEtherFiNode(_validatorId); - - bytes32[] memory withdrawalRoots = IEtherFiNode(etherfiNode).processNodeExit(_validatorId); - validatorInfos[_validatorId].exitTimestamp = _exitTimestamps[i]; - - _setValidatorPhase(etherfiNode, _validatorId, IEtherFiNode.VALIDATOR_PHASE.EXITED); - - numberOfValidators -= 1; - - emit NodeExitProcessed(_validatorId); - emit QueuedRestakingWithdrawal(_validatorId, etherfiNode, withdrawalRoots); - } - } - - /// @notice queue a withdrawal of eth from an eigenPod. You must wait for the queuing period - /// defined by eigenLayer before you can finish the withdrawal via etherFiNode.claimDelayedWithdrawalRouterWithdrawals() - /// @param _validatorIds The validator Ids - function batchQueueRestakedWithdrawal(uint256[] calldata _validatorIds) public onlyAdmin whenNotPaused { - for (uint256 i = 0; i < _validatorIds.length; i++) { - address etherfiNode = etherfiNodeAddress[_validatorIds[i]]; - IEtherFiNode(etherfiNode).queueEigenpodFullWithdrawal(); - } - } - - function completeQueuedWithdrawals(uint256[] calldata _validatorIds, IDelegationManager.Withdrawal[] memory withdrawals, bool _receiveAsTokens) external onlyOperatingAdmin { - for (uint256 i = 0; i < _validatorIds.length; i++) { - address etherfiNode = etherfiNodeAddress[_validatorIds[i]]; - IEtherFiNode(etherfiNode).completeQueuedWithdrawal(withdrawals[i], _receiveAsTokens); - } - } - - /// @dev With Eigenlayer's PEPE model, shares are at the pod level, not validator level - /// so uncareful use of this function will result in distributing rewards from - /// mulitiple validators, not just the rewards of the provided ID. We fundamentally should - /// rework this mechanism as it no longer makes much sense as implemented. - /// @notice Process the rewards skimming from the safe of the validator - /// when the safe is being shared by the multiple validatators, it batch process all of their rewards skimming in one shot - /// @param _validatorId The validator Id - /// Full Flow of the partial withdrawal for a validator - // 1. ETH is withdrawn from the beacon chain to the EigenPod - // 2. perform `EigenPod.startCheckpoint()` - // 3. perform `EigenPod.verifyCheckpointProofs()` - // 4. perform `DelegationManager.queueWithdrawals` - // 5. wait for 'withdrawalDelayBlocks' (= 7 days) delay to be passed - // 6. perform `DelegationManager.completeQueuedWithdrawals` - // 7. Finally, perform `EtherFiNodesManager.partialWithdraw` for the validator - /// @dev This function will be re-considered in the future for simpler operations using the advanced rewards distribution mechanisms - function partialWithdraw(uint256 _validatorId) public nonReentrant whenNotPaused onlyOperatingAdmin { - address etherfiNode = etherfiNodeAddress[_validatorId]; - _updateEtherFiNode(_validatorId); - - (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury ) = (0, 0, 0, 0); - if (stakingRewardsSplit.tnft == SCALE) { - // the ETH validators earn the staking rewards and they are sent to the `EtherFiNode` contracts. - // Process of sweeping those ETH is called `partialWithdraw`. - - // Currently, `EtherFiNodesManager.partialWithdraw` can't process the withdrawal beyond 16 ETH - // because the `EtherFiNodesManager._getTotalRewardsPayoutsFromSafe` reverts if the contract's balance >= 16 ETH. - // This constraint was added in the past due to the complexity in handling the distribution of staking rewards and principal (= 32 ETH); - // while the earned staking rewards were distributed to (T-NFT, B-NFT, NodeOperator, Treasury), the principal (32 ETH) were sent to (B-NFT, T-NFT). - - // This complexity was removed while ago and now the below constraints are true: - // - for all validators, its T-NFT holder == B-NFT holder - // - `stakingRewardsSplit.tnft` is set to `SCALE` (100%); stakingRewardsSplit.{treasury, nodeOperator, bnft} are 0. - - // Therefore, all ETH in (EtherFiNode) contracts belongs to the T-NFT holder (= LiquidityPool). - // This allows us to remove the constraint on the withdrawal amount. - (toOperator, toTnft, toBnft, toTreasury ) = _getTotalRewardsPayoutsFromSafe(_validatorId, false); - } else { - // Note that this flow is deprecated, we keep it for backward compatibility - (toOperator, toTnft, toBnft, toTreasury ) = _getTotalRewardsPayoutsFromSafe(_validatorId, true); - } - _distributePayouts(etherfiNode, _validatorId, toTreasury, toOperator, toTnft, toBnft); - - emit PartialWithdrawal(_validatorId, etherfiNode, toOperator, toTnft, toBnft, toTreasury); - } - - function batchPartialWithdraw(uint256[] calldata _validatorIds) external whenNotPaused{ - for (uint256 i = 0; i < _validatorIds.length; i++) { - partialWithdraw( _validatorIds[i]); - } - } - - /// @notice process the full withdrawal - /// @dev This fullWithdrawal is allowed only after it's marked as EXITED. - /// @dev EtherFi will be monitoring the status of the validator nodes and mark them EXITED if they do; - /// @dev It is a point of centralization in Phase 1 - /// @param _validatorId the validator Id to withdraw from - /// Full Flow of the full withdrawal for a validator - // 1. validator is exited & fund is withdrawn from the beacon chain - // 2. perform `EigenPod.startCheckpoint()` - // 3. perform `EigenPod.verifyCheckpointProofs()` - // 4. perform `EtherFiNodesManager.processNodeExit` which calls `DelegationManager.queueWithdrawals` - // 5. wait for 'minWithdrawalDelayBlocks' (= 7 days) delay to be passed - // 6. perform `EtherFiNodesManager.completeQueuedWithdrawals` which calls `DelegationManager.completeQueuedWithdrawal` - // 7. Finally, perform `EtherFiNodesManager.fullWithdraw` - function fullWithdraw(uint256 _validatorId) public nonReentrant whenNotPaused{ - address etherfiNode = etherfiNodeAddress[_validatorId]; - _updateEtherFiNode(_validatorId); - require(phase(_validatorId) == IEtherFiNode.VALIDATOR_PHASE.EXITED, "NOT_EXITED"); - - (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = getFullWithdrawalPayouts(_validatorId); - _setValidatorPhase(etherfiNode, _validatorId, IEtherFiNode.VALIDATOR_PHASE.FULLY_WITHDRAWN); // EXITED -> FULLY_WITHDRAWN - _unRegisterValidator(_validatorId); - _distributePayouts(etherfiNode, _validatorId, toTreasury, toOperator, toTnft, toBnft); - - tnft.burnFromWithdrawal(_validatorId); - bnft.burnFromWithdrawal(_validatorId); - - emit FullWithdrawal(_validatorId, etherfiNode, toOperator, toTnft, toBnft, toTreasury); - } - - /// @notice Process the full withdrawal for multiple validators - /// @param _validatorIds The validator Ids - function batchFullWithdraw(uint256[] calldata _validatorIds) external whenNotPaused { - for (uint256 i = 0; i < _validatorIds.length; i++) { - fullWithdraw(_validatorIds[i]); - } - } - - /// @notice Once the Oracle observes that the validator is being slashed, it marks the validator as being slashed - /// The validator marked as being slashed must exit in order to withdraw funds - /// @param _validatorIds The validator Ids - function markBeingSlashed( - uint256[] calldata _validatorIds - ) external whenNotPaused onlyAdmin { - for (uint256 i = 0; i < _validatorIds.length; i++) { - _updateEtherFiNode(_validatorIds[i]); - _setValidatorPhase(etherfiNodeAddress[_validatorIds[i]], _validatorIds[i], IEtherFiNode.VALIDATOR_PHASE.BEING_SLASHED); - } - } - - error AlreadyInstalled(); - error NotInstalled(); - error InvalidEtherFiNodeVersion(); - - function allocateEtherFiNode(bool _enableRestaking) external onlyStakingManagerContract returns (address withdrawalSafeAddress) { - // can I re-use an existing safe - if (unusedWithdrawalSafes.length > 0) { - // pop - withdrawalSafeAddress = unusedWithdrawalSafes[unusedWithdrawalSafes.length-1]; - unusedWithdrawalSafes.pop(); - } else { - // make a new one - withdrawalSafeAddress = IStakingManager(stakingManagerContract).instantiateEtherFiNode(_enableRestaking); - } - - // make sure the safe is migrated to v1 - ValidatorInfo memory info = ValidatorInfo(0, 0, 0, IEtherFiNode.VALIDATOR_PHASE.NOT_INITIALIZED); - IEtherFiNode(withdrawalSafeAddress).migrateVersion(0, info); - } - - function updateEtherFiNode(uint256 _validatorId) external { - _updateEtherFiNode(_validatorId); - } - - function _updateEtherFiNode(uint256 _validatorId) internal { - address etherfiNode = etherfiNodeAddress[_validatorId]; - if (IEtherFiNode(etherfiNode).version() != 0) return; - - validatorInfos[_validatorId] = ValidatorInfo({ - validatorIndex: 0, // not initialized yet. TODO: update it by the Oracle - exitRequestTimestamp: IEtherFiNode(etherfiNode).DEPRECATED_exitRequestTimestamp(), - exitTimestamp: IEtherFiNode(etherfiNode).DEPRECATED_exitTimestamp(), - phase: IEtherFiNode(etherfiNode).DEPRECATED_phase() - }); - - IEtherFiNode(etherfiNode).migrateVersion(_validatorId, validatorInfos[_validatorId]); - } - - /// @notice Registers the validator with the EtherFiNode contract - /// @param _validatorId ID of the validator associated to the node - /// @param _enableRestaking whether or not to enable restaking - /// @param _withdrawalSafeAddress address of the withdrawal safe - function registerValidator(uint256 _validatorId, bool _enableRestaking, address _withdrawalSafeAddress) external onlyStakingManagerContract { - if (etherfiNodeAddress[_validatorId] != address(0)) revert AlreadyInstalled(); - if (IEtherFiNode(_withdrawalSafeAddress).version() != 1) revert InvalidEtherFiNodeVersion(); - - etherfiNodeAddress[_validatorId] = _withdrawalSafeAddress; - - IEtherFiNode(_withdrawalSafeAddress).registerValidator(_validatorId, _enableRestaking); - _setValidatorPhase(_withdrawalSafeAddress, _validatorId, IEtherFiNode.VALIDATOR_PHASE.STAKE_DEPOSITED); - } - - /// @notice Unset the EtherFiNode contract for the validator ID - /// @param _validatorId ID of the validator associated - function unregisterValidator(uint256 _validatorId) external onlyStakingManagerContract { - // Called by StakingManager.CancelDeposit - // {STAKE_DEPOSITED, WAITING_FOR_APPROVAL} -> {NOT_INITIALIZED} - _updateEtherFiNode(_validatorId); - _setValidatorPhase(etherfiNodeAddress[_validatorId], _validatorId, IEtherFiNode.VALIDATOR_PHASE.NOT_INITIALIZED); - _unRegisterValidator(_validatorId); - } - - //-------------------------------------------------------------------------------------- - //-------------------------------- CALL FORWARDING ------------------------------------ - //-------------------------------------------------------------------------------------- - - /// @notice Update the whitelist for external calls that can be executed by an EtherfiNode - /// @param _selector method selector - /// @param _target call target for forwarded call - /// @param _allowed enable or disable the call - function updateAllowedForwardedExternalCalls(bytes4 _selector, address _target, bool _allowed) external onlyAdmin { - allowedForwardedExternalCalls[_selector][_target] = _allowed; - emit AllowedForwardedExternalCallsUpdated(_selector, _target, _allowed); - } - - /// @notice Update the whitelist for external calls that can be executed against the corresponding eigenpod - /// @param _selector method selector - /// @param _allowed enable or disable the call - function updateAllowedForwardedEigenpodCalls(bytes4 _selector, bool _allowed) external onlyAdmin { - allowedForwardedEigenpodCalls[_selector] = _allowed; - emit AllowedForwardedEigenpodCallsUpdated(_selector, _allowed); - } - - function forwardEigenpodCall(uint256[] calldata _validatorIds, bytes[] calldata _data) external nonReentrant whenNotPaused onlyOperatingAdmin returns (bytes[] memory returnData) { - returnData = new bytes[](_validatorIds.length); - for (uint256 i = 0; i < _validatorIds.length; i++) { - _verifyForwardedEigenpodCall(_data[i]); - returnData[i] = IEtherFiNode(etherfiNodeAddress[_validatorIds[i]]).callEigenPod(_data[i]); - } - } - - function forwardEigenpodCall(address[] calldata _etherfiNodes, bytes[] calldata _data) external nonReentrant whenNotPaused onlyOperatingAdmin returns (bytes[] memory returnData) { - returnData = new bytes[](_etherfiNodes.length); - for (uint256 i = 0; i < _etherfiNodes.length; i++) { - _verifyForwardedEigenpodCall(_data[i]); - returnData[i] = IEtherFiNode(_etherfiNodes[i]).callEigenPod(_data[i]); - } - } - - function forwardExternalCall(uint256[] calldata _validatorIds, bytes[] calldata _data, address _target) external nonReentrant whenNotPaused onlyOperatingAdmin returns (bytes[] memory returnData) { - returnData = new bytes[](_validatorIds.length); - for (uint256 i = 0; i < _validatorIds.length; i++) { - _verifyForwardedExternalCall(_target, _data[i]); - returnData[i] = IEtherFiNode(etherfiNodeAddress[_validatorIds[i]]).forwardCall(_target, _data[i]); - } - } - - function forwardExternalCall(address[] calldata _etherfiNodes, bytes[] calldata _data, address _target) external nonReentrant whenNotPaused onlyOperatingAdmin returns (bytes[] memory returnData) { - returnData = new bytes[](_etherfiNodes.length); - for (uint256 i = 0; i < _etherfiNodes.length; i++) { - _verifyForwardedExternalCall(_target, _data[i]); - returnData[i] = IEtherFiNode(_etherfiNodes[i]).forwardCall(_target, _data[i]); - } - } - - function _verifyForwardedEigenpodCall(bytes calldata _data) internal view { - if (_data.length < 4) revert InvalidForwardedCall(); - bytes4 selector = bytes4(_data[:4]); - if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); - } - - function _verifyForwardedExternalCall(address _to, bytes calldata _data) internal view { - if (_data.length < 4) revert InvalidForwardedCall(); - bytes4 selector = bytes4(_data[:4]); - if (!allowedForwardedExternalCalls[selector][_to]) revert ForwardedCallNotAllowed(); - } - - //-------------------------------------------------------------------------------------- - //------------------------------------- SETTER -------------------------------------- - //-------------------------------------------------------------------------------------- - - /// @notice Sets the staking rewards split - /// @notice Splits must add up to the SCALE of 1_000_000 - /// @param _treasury the split going to the treasury - /// @param _nodeOperator the split going to the nodeOperator - /// @param _tnft the split going to the tnft holder - /// @param _bnft the split going to the bnft holder - function setStakingRewardsSplit(uint64 _treasury, uint64 _nodeOperator, uint64 _tnft, uint64 _bnft) - public onlyAdmin - { - if (_treasury + _nodeOperator + _tnft + _bnft != SCALE) revert InvalidParams(); - stakingRewardsSplit.treasury = _treasury; - stakingRewardsSplit.nodeOperator = _nodeOperator; - stakingRewardsSplit.tnft = _tnft; - stakingRewardsSplit.bnft = _bnft; - } - - error InvalidPenaltyRate(); - /// @notice Sets the Non Exit Penalty - /// @param _nonExitPenaltyPrincipal the new principal amount - /// @param _nonExitPenaltyDailyRate the new non exit daily rate - function setNonExitPenalty(uint64 _nonExitPenaltyDailyRate, uint64 _nonExitPenaltyPrincipal) public onlyAdmin { - if(_nonExitPenaltyDailyRate > 10000) revert InvalidPenaltyRate(); - nonExitPenaltyPrincipal = _nonExitPenaltyPrincipal; - nonExitPenaltyDailyRate = _nonExitPenaltyDailyRate; - } - - - /// @notice Sets the phase of the validator - /// @param _validatorId id of the validator associated to this etherfi node - /// @param _phase phase of the validator - function setValidatorPhase(uint256 _validatorId, IEtherFiNode.VALIDATOR_PHASE _phase) public onlyStakingManagerContract { - address etherfiNode = etherfiNodeAddress[_validatorId]; - _setValidatorPhase(etherfiNode, _validatorId, _phase); - } - - /// @notice set maximum number of queued eigenlayer withdrawals that can be processed in 1 tx - /// @param _max max number of queued withdrawals - function setMaxEigenLayerWithdrawals(uint8 _max) external onlyAdmin { - maxEigenlayerWithdrawals = _max; - } - - /// @notice Increments the number of validators by a certain amount - /// @param _count how many new validators to increment by - function incrementNumberOfValidators(uint64 _count) external onlyStakingManagerContract { - numberOfValidators += _count; - } - - /// @notice Updates the address of the admin - /// @param _address the new address to set as admin - function updateAdmin(address _address, bool _isAdmin) external onlyOwner { - admins[_address] = _isAdmin; - } - - function updateEigenLayerOperatingAdmin(address _address, bool _isAdmin) external onlyOwner { - operatingAdmin[_address] = _isAdmin; - } - - // Pauses the contract - function pauseContract() external onlyAdmin { - _pause(); - } - - // Unpauses the contract - function unPauseContract() external onlyAdmin { - _unpause(); - } - - //-------------------------------------------------------------------------------------- - //------------------------------- INTERNAL FUNCTIONS -------------------------------- - //-------------------------------------------------------------------------------------- - - function _updateExitRequestTimestamp(uint256 _validatorId, address _etherfiNode, uint32 _exitRequestTimestamp) internal { - IEtherFiNode(_etherfiNode).updateNumExitRequests(_exitRequestTimestamp > 0 ? 1 : 0, _exitRequestTimestamp == 0 ? 1 : 0); - validatorInfos[_validatorId].exitRequestTimestamp = _exitRequestTimestamp; - } - - function _setValidatorPhase(address _node, uint256 _validatorId, IEtherFiNode.VALIDATOR_PHASE _newPhase) internal { - IEtherFiNode(_node).validatePhaseTransition(phase(_validatorId), _newPhase); - validatorInfos[_validatorId].phase = _newPhase; - - if (_newPhase == IEtherFiNode.VALIDATOR_PHASE.LIVE) { - IEtherFiNode(_node).updateNumberOfAssociatedValidators(1, 0); - } - if (_newPhase == IEtherFiNode.VALIDATOR_PHASE.FULLY_WITHDRAWN) { - IEtherFiNode(_node).processFullWithdraw(_validatorId); - } - if (_newPhase == IEtherFiNode.VALIDATOR_PHASE.EXITED) { - IEtherFiNode(_node).updateNumExitedValidators(1, 0); - } - - emit PhaseChanged(_validatorId, _newPhase); - } - - function _unRegisterValidator(uint256 _validatorId) internal { - address safeAddress = etherfiNodeAddress[_validatorId]; - if (safeAddress == address(0)) revert NotInstalled(); - - bool doRecycle = IEtherFiNode(safeAddress).unRegisterValidator(_validatorId, validatorInfos[_validatorId]); - - delete etherfiNodeAddress[_validatorId]; - // delete validatorInfos[_validatorId]; - - if (doRecycle) { - unusedWithdrawalSafes.push(safeAddress); - } - } - - // it returns the "total" payout amounts from the safe that the validator is associated with - // it performs some sanity-checks on the validator status, safe balance - function _getTotalRewardsPayoutsFromSafe( - uint256 _validatorId, - bool _checkExit - ) internal view returns (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) { - require(phase(_validatorId) == IEtherFiNode.VALIDATOR_PHASE.LIVE, "NOT_LIVE"); - address etherfiNode = etherfiNodeAddress[_validatorId]; - - // When there is any pending exit request from T-NFT holder, - // the corresponding valiator must exit - // Only the admin can bypass it to provide the liquidity to the liquidity pool - require(!_checkExit || IEtherFiNode(etherfiNode).numExitRequestsByTnft() == 0, "PENDING_EXIT_REQUEST"); - require(IEtherFiNode(etherfiNode).numExitedValidators() == 0, "NEED_FULL_WITHDRAWAL"); - - // Once the balance of the safe goes over 16 ETH, - // it is impossible to tell if that ETH is from staking rewards or from principal (16 ETH ~ 32 ETH) - // In such a case, the validator must exit and perform the full withdrawal - // This is to prevent the principal of the exited validators from being mistakenly distributed out as rewards - // - // Therefore, someone should trigger 'partialWithdraw' from the safe before its accrued staking rewards goes above 16 ETH - // The ether.fi's bot will handle this for a while, but in the long-term we will make it an incentivzed process such that the caller can get some fees - // - // The boolean flag '_checkMaxBalance' is FALSE only when this is called for 'forcePartialWithdraw' - // where the Admin handles the case when the balance goes over 16 ETH - require(!_checkExit || address(etherfiNode).balance < 16 ether, "MUST_EXIT"); - - return IEtherFiNode(etherfiNode).getRewardsPayouts( - validatorInfos[_validatorId].exitRequestTimestamp, - stakingRewardsSplit - ); - } - - function _distributePayouts(address _etherfiNode, uint256 _validatorId, uint256 _toTreasury, uint256 _toOperator, uint256 _toTnft, uint256 _toBnft) internal { - IEtherFiNode(_etherfiNode).withdrawFunds( - treasuryContract, _toTreasury, - auctionManager.getBidOwner(_validatorId), _toOperator, - tnft.ownerOf(_validatorId), _toTnft, - bnft.ownerOf(_validatorId), _toBnft - ); - } - - error SendFail(); - - function _sendFund(address _recipient, uint256 _amount) internal { - uint256 balanace = address(this).balance; - (bool sent, ) = _recipient.call{value: _amount}(""); - if (!sent || address(this).balance != balanace - _amount) revert SendFail(); - } - - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} - - - //-------------------------------------------------------------------------------------- - //------------------------------------- GETTER -------------------------------------- - //-------------------------------------------------------------------------------------- - - function numAssociatedValidators(uint256 _validatorId) external view returns (uint256) { - address etherfiNode = etherfiNodeAddress[_validatorId]; - if (etherfiNode == address(0)) return 0; - return IEtherFiNode(etherfiNode).numAssociatedValidators(); - } - - /// @notice Fetches the phase a specific node is in - /// @param _validatorId id of the validator associated to etherfi node - /// @return validatorPhase the phase the node is in - function phase(uint256 _validatorId) public view returns (IEtherFiNode.VALIDATOR_PHASE validatorPhase) { - address etherfiNode = etherfiNodeAddress[_validatorId]; - ValidatorInfo memory info = validatorInfos[_validatorId]; - if (info.exitTimestamp == 0) { - if (etherfiNode == address(0)) { - validatorPhase = IEtherFiNode.VALIDATOR_PHASE.NOT_INITIALIZED; - } else if (IEtherFiNode(etherfiNode).version() == 0) { - validatorPhase = IEtherFiNode(etherfiNode).DEPRECATED_phase(); - } else { - validatorPhase = info.phase; - } - } else { - validatorPhase = info.phase; - } - } - - /// @notice Generates withdraw credentials for a validator - /// @param _address associated with the validator for the withdraw credentials - /// @return the generated withdraw key for the node - function generateWithdrawalCredentials(address _address) public pure returns (bytes memory) { - return abi.encodePacked(bytes1(0x01), bytes11(0x0), _address); - } - - /// @notice get the length of the unusedWithdrawalSafes array - function getUnusedWithdrawalSafesLength() external view returns (uint256) { - return unusedWithdrawalSafes.length; - } - - function getWithdrawalSafeAddress(uint256 _validatorId) public view returns (address) { - address etherfiNode = etherfiNodeAddress[_validatorId]; - return IEtherFiNode(etherfiNode).isRestakingEnabled() ? IEtherFiNode(etherfiNode).eigenPod() : etherfiNode; - } - - /// @notice Fetches the withdraw credentials for a specific node - /// @param _validatorId id of the validator associated to etherfi node - /// @return the generated withdraw key for the node - function getWithdrawalCredentials(uint256 _validatorId) external view returns (bytes memory) { - return generateWithdrawalCredentials(getWithdrawalSafeAddress(_validatorId)); - } - - /// @notice Fetches if the node has an exit request - /// @param _validatorId id of the validator associated to etherfi node - /// @return bool value based on if an exit request has been sent - function isExitRequested(uint256 _validatorId) public view returns (bool) { - ValidatorInfo memory info = getValidatorInfo(_validatorId); - return info.exitRequestTimestamp > 0; - } - - /// @notice Fetches the nodes non exit penalty amount - /// @param _validatorId id of the validator associated to etherfi node - /// @return nonExitPenalty the amount of the penalty - function getNonExitPenalty(uint256 _validatorId) public view returns (uint256 nonExitPenalty) { - address etherfiNode = etherfiNodeAddress[_validatorId]; - ValidatorInfo memory info = getValidatorInfo(_validatorId); - return IEtherFiNode(etherfiNode).getNonExitPenalty(info.exitRequestTimestamp, info.exitTimestamp); - } - - function getValidatorInfo(uint256 _validatorId) public view returns (ValidatorInfo memory) { - ValidatorInfo memory info = validatorInfos[_validatorId]; - info.phase = phase(_validatorId); - return info; - } - - /// @notice Get the rewards payouts for a specific validator = (total payouts from the safe / N) where N is the number of the validators associated with the same safe - /// @param _validatorId ID of the validator - /// - /// @return toNodeOperator the TVL for the Node Operator - /// @return toTnft the TVL for the T-NFT holder - /// @return toBnft the TVL for the B-NFT holder - /// @return toTreasury the TVL for the Treasury - function getRewardsPayouts( - uint256 _validatorId - ) public view returns (uint256, uint256, uint256, uint256) { - address etherfiNode = etherfiNodeAddress[_validatorId]; - uint256 n = IEtherFiNode(etherfiNode).numAssociatedValidators(); - (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury ) = _getTotalRewardsPayoutsFromSafe(_validatorId, true); - return (toOperator / n, toTnft / n, toBnft / n, toTreasury / n); - } - - /// @notice Fetches the full withdraw payouts for a specific validator - /// @param _validatorId id of the validator associated to etherfi node - /// - /// @return toNodeOperator the TVL for the Node Operator - /// @return toTnft the TVL for the T-NFT holder - /// @return toBnft the TVL for the B-NFT holder - /// @return toTreasury the TVL for the Treasury - function getFullWithdrawalPayouts( - uint256 _validatorId - ) public view returns (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) { - require(phase(_validatorId) == IEtherFiNode.VALIDATOR_PHASE.EXITED, "NOT_EXITED"); - - address etherfiNode = etherfiNodeAddress[_validatorId]; - return IEtherFiNode(etherfiNode).getFullWithdrawalPayouts(getValidatorInfo(_validatorId), stakingRewardsSplit); - } - - /// @notice Compute the TVLs for {node operator, t-nft holder, b-nft holder, treasury} - /// @param _validatorId id of the validator associated to etherfi node - /// @param _beaconBalance the balance of the validator in Consensus Layer - /// - /// @return toNodeOperator the TVL for the Node Operator - /// @return toTnft the TVL for the T-NFT holder - /// @return toBnft the TVL for the B-NFT holder - /// @return toTreasury the TVL for the Treasury - function calculateTVL( - uint256 _validatorId, - uint256 _beaconBalance - ) public view returns (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) { - address etherfiNode = etherfiNodeAddress[_validatorId]; - return IEtherFiNode(etherfiNode).calculateTVL(_beaconBalance, getValidatorInfo(_validatorId), stakingRewardsSplit, false); - } - - /// @notice return the eigenpod associated with the etherFiNode connected to the provided validator - /// @dev The existence of a connected eigenpod does not imply the node is currently configured for restaking. - /// use isRestakingEnabled() instead - function getEigenPod(uint256 _validatorId) public view returns (address) { - IEtherFiNode etherfiNode = IEtherFiNode(etherfiNodeAddress[_validatorId]); - return etherfiNode.eigenPod(); - } - - /// @notice Fetches the address of the implementation contract currently being used by the proxy - /// @return The address of the currently used implementation contract - function getImplementation() external view returns (address) { - return _getImplementation(); - } - - error NotAdmin(); - error NotStakingManager(); - - function _requireAdmin() internal view virtual { - if (!admins[msg.sender] && msg.sender != owner()) revert NotAdmin(); - } - - function _onlyStakingManagerContract() internal view virtual { - if (msg.sender != stakingManagerContract) revert NotStakingManager(); - } - - function _operatingAdmin() internal view virtual { - if (!operatingAdmin[msg.sender] && !admins[msg.sender] && msg.sender != owner()) revert NotAdmin(); - } - - //-------------------------------------------------------------------------------------- - //----------------------------------- MODIFIERS -------------------------------------- - //-------------------------------------------------------------------------------------- - - modifier onlyStakingManagerContract() { - _onlyStakingManagerContract(); - _; - } - - modifier onlyAdmin() { - _requireAdmin(); - _; - } - - modifier onlyOperatingAdmin() { - _operatingAdmin(); - _; - } - */ } diff --git a/src/StakingManager.sol b/src/StakingManager.sol index c0ae95554..2c23e76d9 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -93,6 +93,8 @@ contract StakingManager is _disableInitializers(); } + function _authorizeUpgrade(address newImplementation) internal override {} + // TODO(dave): are we using a slightly different proxy for this contract? /// @notice Fetches the address of the implementation contract currently being used by the proxy @@ -188,8 +190,6 @@ contract StakingManager is // TODO(dave): reimplement pausing with role registry function pauseContract() external { _pause(); } function unPauseContract() external { _unpause(); } - /* - */ //-------------------------------------------------------------------------------------- @@ -203,7 +203,7 @@ contract StakingManager is /// @notice Fetches the address of the implementation contract currently being used by the beacon proxy /// @return the address of the currently used implementation contract - function getEtherFiNodeImplementation() public view override returns (address) { + function getEtherFiNodeImplementation() public view returns (address) { return upgradableBeacon.implementation(); } } diff --git a/src/helpers/EtherFiViewer.sol b/src/helpers/EtherFiViewer.sol index f945c2846..750f0f624 100644 --- a/src/helpers/EtherFiViewer.sol +++ b/src/helpers/EtherFiViewer.sol @@ -124,6 +124,7 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { } */ + /* function EtherFiNodesManager_aggregatedBalanceOfUnusedSafes() external view returns (uint256 total) { uint256 n = nodesManager.getUnusedWithdrawalSafesLength(); @@ -133,6 +134,7 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { total += safe.balance + eigenpod.balance; } } + */ function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index a0716552f..8e67abcc5 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -84,7 +84,7 @@ interface IEtherFiNodesManager { function getEigenPod(uint256 id) external view returns (address); - function etherFiNodeFromId(uint256 id) public view returns (address); + function etherFiNodeFromId(uint256 id) external view returns (address); function eigenPodManager() external view returns (address); diff --git a/test/AuctionManager.t.sol b/test/AuctionManager.t.sol index 708bd787b..231052a9f 100644 --- a/test/AuctionManager.t.sol +++ b/test/AuctionManager.t.sol @@ -699,11 +699,11 @@ contract AuctionManagerTest is TestSetup { IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](1); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -867,11 +867,11 @@ contract AuctionManagerTest is TestSetup { 0.5 ether ); stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId, false); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -912,11 +912,11 @@ contract AuctionManagerTest is TestSetup { IStakingManager.DepositData[] memory depositDataArray2 = new IStakingManager.DepositData[](1); - etherFiNode = managerInstance.etherfiNodeAddress(2); + etherFiNode = managerInstance.etherFiNodeFromId(2); root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -962,11 +962,11 @@ contract AuctionManagerTest is TestSetup { 0.5 ether ); stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId, false); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); diff --git a/test/BNFT.t.sol b/test/BNFT.t.sol index 62602bc3e..442290b21 100644 --- a/test/BNFT.t.sol +++ b/test/BNFT.t.sol @@ -35,11 +35,11 @@ contract BNFTTest is TestSetup { IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](1); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -90,11 +90,11 @@ contract BNFTTest is TestSetup { IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](1); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); diff --git a/test/EigenLayerIntegration.t.sol b/test/EigenLayerIntegration.t.sol index 47500dd88..aba75a2e3 100644 --- a/test/EigenLayerIntegration.t.sol +++ b/test/EigenLayerIntegration.t.sol @@ -52,7 +52,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { // {EigenPod, EigenPodOwner} used in EigenLayer's unit test eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - podOwner = managerInstance.etherfiNodeAddress(validatorId); + podOwner = managerInstance.etherFiNodeFromId(validatorId); validatorIds = new uint256[](1); validatorIds[0] = validatorId; ws = EtherFiNode(payable(podOwner)); @@ -121,7 +121,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { function create_validator() public returns (uint256, address, EtherFiNode) { uint256[] memory validatorIds = launch_validator(1, 0, true); - address nodeAddress = managerInstance.etherfiNodeAddress(validatorIds[0]); + address nodeAddress = managerInstance.etherFiNodeFromId(validatorIds[0]); EtherFiNode node = EtherFiNode(payable(nodeAddress)); return (validatorIds[0], nodeAddress, node); @@ -295,7 +295,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { validatorIds[0] = 338; uint32[] memory timeStamps = new uint32[](1); timeStamps[0] = 0; - address nodeAddress = managerInstance.etherfiNodeAddress(validatorIds[0]); + address nodeAddress = managerInstance.etherFiNodeFromId(validatorIds[0]); IDelegationManager mgr = managerInstance.delegationManager(); diff --git a/test/EtherFiTimelock.sol b/test/EtherFiTimelock.sol index d806068a9..fd42afc0b 100644 --- a/test/EtherFiTimelock.sol +++ b/test/EtherFiTimelock.sol @@ -31,10 +31,13 @@ contract TimelockTest is TestSetup { managerInstance.transferOwnership(address(tl)); assertEq(managerInstance.owner(), address(tl)); + // TODO(dave): fix test with role registry + /* // attempt to call an onlyOwner function with the previous owner vm.prank(owner); vm.expectRevert("Ownable: caller is not the owner"); managerInstance.updateAdmin(admin, true); + */ // encoded data for EtherFiNodesManager.UpdateAdmin(admin, true) bytes memory data = hex"670a6fd9000000000000000000000000cf03dd0a894ef79cb5b601a43c4b25e3ae4c67ed0000000000000000000000000000000000000000000000000000000000000001"; @@ -127,8 +130,11 @@ contract TimelockTest is TestSetup { 0 // optional salt ); + + // TODO(dave): update test with role registry changes + /* // admin account should now have admin permissions on EtherfiNodesManager - assertEq(managerInstance.admins(admin), true); + //assertEq(managerInstance.admins(admin), true); // queue and execute a tx to undo that change bytes memory undoData = hex"670a6fd9000000000000000000000000cf03dd0a894ef79cb5b601a43c4b25e3ae4c67ed0000000000000000000000000000000000000000000000000000000000000000"; @@ -206,6 +212,7 @@ contract TimelockTest is TestSetup { 0 // optional salt ); assertEq(managerInstance.owner(), newOwner); + */ } function test_generate_EtherFiOracle_updateAdmin() public { diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index 261d557a6..d4d7aec78 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -324,6 +324,8 @@ contract LiquidityPoolTest is TestSetup { liquidityPoolInstance.sendExitRequests(newValidators); } + // TODO(dave): update when v3 changes finalized + /* function test_bnftFlowWithLiquidityPoolAsBnftHolder() public { setUpBnftHolders(); @@ -360,7 +362,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(liquidityPoolInstance.totalValueOutOfLp(), 32 ether); assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorIds[0]); + address etherfiNode = managerInstance.etherFiNodeFromId(validatorIds[0]); vm.deal(address(etherfiNode), 1 ether); managerInstance.batchPartialWithdraw(validatorIds); @@ -380,7 +382,7 @@ contract LiquidityPoolTest is TestSetup { uint256 totalTnftRewards = 0; for (uint256 i = 0; i < validatorIds.length; i++) { - address etherfiNode = managerInstance.etherfiNodeAddress( + address etherfiNode = managerInstance.etherFiNodeFromId( validatorIds[i] ); _transferTo(etherfiNode, 1 ether); @@ -390,7 +392,9 @@ contract LiquidityPoolTest is TestSetup { // managerInstance.batchPartialWithdrawOptimized(validatorIds); assertEq(address(liquidityPoolInstance).balance, lastBalance + totalTnftRewards); } + */ + /* function test_ProcessNodeExit() public { vm.deal(owner, 100 ether); @@ -424,7 +428,7 @@ contract LiquidityPoolTest is TestSetup { liquidityPoolInstance.batchRegister(zeroRoot, newValidators, depositDataArray, depositDataRootsForApproval, sig); for (uint256 i = 0; i < newValidators.length; i++) { - address etherFiNode = managerInstance.etherfiNodeAddress( + address etherFiNode = managerInstance.etherFiNodeFromId( newValidators[i] ); @@ -436,7 +440,7 @@ contract LiquidityPoolTest is TestSetup { vm.prank(alice); liquidityPoolInstance.batchApproveRegistration(newValidators, pubKey, sig); for (uint256 i = 0; i < newValidators.length; i++) { - address etherFiNode = managerInstance.etherfiNodeAddress( + address etherFiNode = managerInstance.etherFiNodeFromId( newValidators[i] ); @@ -466,8 +470,8 @@ contract LiquidityPoolTest is TestSetup { vm.warp(1681351200 + 12 * 6); - address etherfiNode1 = managerInstance.etherfiNodeAddress(newValidators[0]); - address etherfiNode2 = managerInstance.etherfiNodeAddress(newValidators[1]); + address etherfiNode1 = managerInstance.etherFiNodeFromId(newValidators[0]); + address etherfiNode2 = managerInstance.etherFiNodeFromId(newValidators[1]); _transferTo(etherfiNode1, 32 ether - slashingPenalties[0]); _transferTo(etherfiNode2, 32 ether - slashingPenalties[1]); @@ -485,6 +489,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(liquidityPoolInstance.getTotalPooledEther(), 63 ether); assertEq(address(liquidityPoolInstance).balance, 63 ether); } + */ function test_fallback() public { assertEq(liquidityPoolInstance.getTotalPooledEther(), 0 ether); @@ -508,6 +513,8 @@ contract LiquidityPoolTest is TestSetup { assertEq(liquidityPoolInstance.getTotalPooledEther(), 103 ether); } + // TODO(dave): update after v3 changes + /* function test_rebase_withdraw_flow() public { uint256[] memory validatorIds = launch_validator(); @@ -542,8 +549,8 @@ contract LiquidityPoolTest is TestSetup { exitRequestTimestamps[0] = uint32(block.timestamp); exitRequestTimestamps[1] = uint32(block.timestamp); - address etherfiNode1 = managerInstance.etherfiNodeAddress(validatorIds[0]); - address etherfiNode2 = managerInstance.etherfiNodeAddress(validatorIds[1]); + address etherfiNode1 = managerInstance.etherFiNodeFromId(validatorIds[0]); + address etherfiNode2 = managerInstance.etherFiNodeFromId(validatorIds[1]); _transferTo(etherfiNode1, 17 ether); _transferTo(etherfiNode2, 33 ether); @@ -571,6 +578,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(eETHInstance.totalSupply(), 0); assertEq(eETHInstance.balanceOf(bob), 0); } + */ function test_RegisterAsBnftHolder() public { //Move past one week @@ -795,13 +803,13 @@ contract LiquidityPoolTest is TestSetup { assertEq(stakingManagerInstance.bidIdToStaker(bidIds[3]), bnftHolder); // verify that created nodes have associated eigenPods - IEtherFiNode node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[0])); + IEtherFiNode node = IEtherFiNode(managerInstance.etherFiNodeFromId(bidIds[0])); assertFalse(address(node.getEigenPod()) == address(0x0)); - node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[1])); + node = IEtherFiNode(managerInstance.etherFiNodeFromId(bidIds[1])); assertFalse(address(node.getEigenPod()) == address(0x0)); - node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[2])); + node = IEtherFiNode(managerInstance.etherFiNodeFromId(bidIds[2])); assertFalse(address(node.getEigenPod()) == address(0x0)); - node = IEtherFiNode(managerInstance.etherfiNodeAddress(bidIds[3])); + node = IEtherFiNode(managerInstance.etherFiNodeFromId(bidIds[3])); assertFalse(address(node.getEigenPod()) == address(0x0)); } @@ -819,18 +827,18 @@ contract LiquidityPoolTest is TestSetup { bytes32[] memory depositDataRootsForApproval = new bytes32[](1); - address etherFiNode = managerInstance.etherfiNodeAddress(11); + address etherFiNode = managerInstance.etherFiNodeFromId(11); root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 1 ether ); depositDataRootsForApproval[0] = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 31 ether ); diff --git a/test/MembershipManager.t.sol b/test/MembershipManager.t.sol index ee43857c7..36d0c8e7e 100644 --- a/test/MembershipManager.t.sol +++ b/test/MembershipManager.t.sol @@ -907,6 +907,8 @@ contract MembershipManagerTest is TestSetup { assertEq(tier2_apr_bp, 0); // 00.00% for tier 2 with weight 3, because there is no deposited ETH in tier 2 } +// TODO(dave): does this break ether.fan? +/* function calculateAggregatedTVL(uint256[] memory _validatorIds) internal view returns (uint256[] memory) { uint256[] memory tvls = new uint256[](4); @@ -922,6 +924,7 @@ contract MembershipManagerTest is TestSetup { return tvls; } + */ function test_Pausable() public { assertEq(membershipManagerV1Instance.paused(), false); diff --git a/test/MembershipManagerV0.t.sol b/test/MembershipManagerV0.t.sol index 8e0e8d842..a9d70521e 100644 --- a/test/MembershipManagerV0.t.sol +++ b/test/MembershipManagerV0.t.sol @@ -741,6 +741,7 @@ contract MembershipManagerV0Test is TestSetup { assertEq(tier2_apr_bp, 0); // 00.00% for tier 2 with weight 3, because there is no deposited ETH in tier 2 } +/* function calculateAggregatedTVL(uint256[] memory _validatorIds) internal view returns (uint256[] memory) { uint256[] memory tvls = new uint256[](4); @@ -756,6 +757,7 @@ contract MembershipManagerV0Test is TestSetup { return tvls; } + */ function test_Pausable() public { assertEq(membershipManagerInstance.paused(), false); diff --git a/test/StakingManager.t.sol b/test/StakingManager.t.sol index 89e38163b..135010724 100644 --- a/test/StakingManager.t.sol +++ b/test/StakingManager.t.sol @@ -32,19 +32,16 @@ contract StakingManagerTest is TestSetup { liquidityPoolInstance.setStakingTargetWeights(50, 50); } + // TODO(dave): redo stakingManager tests + /* function test_fake() public view { console.logBytes32(_getDepositRoot()); } - function test_StakingManagerContractInstantiatedCorrectly() public { - assertEq(stakingManagerInstance.stakeAmount(), 32 ether); - assertEq(stakingManagerInstance.owner(), owner); - } - function test_GenerateWithdrawalCredentialsCorrectly() public { address exampleWithdrawalAddress = 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931; bytes memory withdrawalCredential = managerInstance - .generateWithdrawalCredentials(exampleWithdrawalAddress); + .addressToWithdrawalCredentials(exampleWithdrawalAddress); // Generated with './deposit new-mnemonic --eth1_withdrawal_address 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931' bytes memory trueOne = hex"010000000000000000000000cd5ebc2dd4cb3dc52ac66ceecc72c838b40a5931"; @@ -88,11 +85,11 @@ contract StakingManagerTest is TestSetup { bytes32[] memory depositDataRootsForApproval = new bytes32[](1); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 1 ether ); IStakingManager.DepositData memory depositData = IStakingManager @@ -106,7 +103,7 @@ contract StakingManagerTest is TestSetup { bytes32 rootForApproval = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 31 ether ); @@ -130,7 +127,7 @@ contract StakingManagerTest is TestSetup { liquidityPoolInstance.batchApproveRegistration(validatorArray, pubKey, sig); uint256 selectedBidId = bidIds[0]; - etherFiNode = managerInstance.etherfiNodeAddress(selectedBidId); + etherFiNode = managerInstance.etherFiNodeFromId(selectedBidId); assertEq(selectedBidId, 1); assertEq(address(managerInstance).balance, 0 ether); @@ -138,7 +135,7 @@ contract StakingManagerTest is TestSetup { //Revenue not about auction threshold so still 1 ether assertEq(address(auctionInstance).balance, 1 ether); - address safeAddress = managerInstance.etherfiNodeAddress(bidIds[0]); + address safeAddress = managerInstance.etherFiNodeFromId(bidIds[0]); assertEq(safeAddress, etherFiNode); } @@ -165,11 +162,11 @@ contract StakingManagerTest is TestSetup { IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](1); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -188,10 +185,9 @@ contract StakingManagerTest is TestSetup { uint256 validatorId = bidId[0]; uint256 winningBid = bidId[0]; address staker = stakingManagerInstance.bidIdToStaker(validatorId); - address etherfiNode = managerInstance.etherfiNodeAddress(validatorId); + address etherfiNode = managerInstance.etherFiNodeFromId(validatorId); assertEq(staker, 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - assertEq(stakingManagerInstance.stakeAmount(), 32 ether); assertEq(winningBid, bidId[0]); assertEq(validatorId, bidId[0]); @@ -483,11 +479,11 @@ contract StakingManagerTest is TestSetup { false ); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -556,11 +552,11 @@ contract StakingManagerTest is TestSetup { false ); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -580,7 +576,7 @@ contract StakingManagerTest is TestSetup { stakingManagerInstance.batchRegisterValidators(zeroRoot, bidId, depositDataArray); uint256 selectedBidId = bidId[0]; - etherFiNode = managerInstance.etherfiNodeAddress(bidId[0]); + etherFiNode = managerInstance.etherFiNodeFromId(bidId[0]); // assertEq(address(protocolRevenueManagerInstance).balance, 0.05 ether); // protocolRevenueManager is being deprecated assertEq(selectedBidId, 1); @@ -591,7 +587,7 @@ contract StakingManagerTest is TestSetup { address operatorAddress = auctionInstance.getBidOwner(bidId[0]); assertEq(operatorAddress, 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - address safeAddress = managerInstance.etherfiNodeAddress(bidId[0]); + address safeAddress = managerInstance.etherFiNodeFromId(bidId[0]); assertEq(safeAddress, etherFiNode); assertEq( @@ -644,13 +640,13 @@ contract StakingManagerTest is TestSetup { memory depositDataArray = new IStakingManager.DepositData[](4); for (uint256 i = 0; i < processedBidIds.length; i++) { - address etherFiNode = managerInstance.etherfiNodeAddress( + address etherFiNode = managerInstance.etherFiNodeFromId( processedBidIds[i] ); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); depositDataArray[i] = IStakingManager.DepositData({ @@ -784,13 +780,13 @@ contract StakingManagerTest is TestSetup { bytes32 root; for (uint256 i = 0; i < processedBidIds.length; i++) { - address etherFiNode = managerInstance.etherfiNodeAddress( + address etherFiNode = managerInstance.etherFiNodeFromId( processedBidIds[i] ); bytes32 generatedRoot = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); depositDataArray[i] = IStakingManager.DepositData({ @@ -860,13 +856,13 @@ contract StakingManagerTest is TestSetup { bytes32 root; for (uint256 i = 0; i < bidIdArray.length; i++) { - address etherFiNode = managerInstance.etherfiNodeAddress( + address etherFiNode = managerInstance.etherFiNodeFromId( bidIdArray[i] ); bytes32 generatedRoot = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 1 ether ); depositDataArray[i] = IStakingManager.DepositData({ @@ -962,11 +958,11 @@ contract StakingManagerTest is TestSetup { false ); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -1041,7 +1037,7 @@ contract StakingManagerTest is TestSetup { uint256 selectedBidId = bidId2[0]; address staker = stakingManagerInstance.bidIdToStaker(bidId2[0]); - address etherFiNode = managerInstance.etherfiNodeAddress(bidId2[0]); + address etherFiNode = managerInstance.etherFiNodeFromId(bidId2[0]); assertEq(staker, 0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); assertEq(selectedBidId, bidId2[0]); @@ -1061,7 +1057,7 @@ contract StakingManagerTest is TestSetup { assertEq(managerInstance.getUnusedWithdrawalSafesLength(), 0); stakingManagerInstance.batchCancelDeposit(bidId2); - assertEq(managerInstance.etherfiNodeAddress(bidId2[0]), address(0)); + assertEq(managerInstance.etherFiNodeFromId(bidId2[0]), address(0)); assertEq(stakingManagerInstance.bidIdToStaker(bidId2[0]), address(0)); assertTrue( managerInstance.phase(bidId2[0]) == @@ -1102,11 +1098,11 @@ contract StakingManagerTest is TestSetup { false ); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -1139,11 +1135,11 @@ contract StakingManagerTest is TestSetup { false ); - etherFiNode = managerInstance.etherfiNodeAddress(2); + etherFiNode = managerInstance.etherFiNodeFromId(2); root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -1242,11 +1238,11 @@ contract StakingManagerTest is TestSetup { startHoax(alice); stakingManagerInstance.batchDepositWithBidIds{value: 32 ether}(bidId1, false); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); @@ -1320,4 +1316,6 @@ contract StakingManagerTest is TestSetup { vm.expectRevert("DepositContract: reconstructed DepositData does not match supplied deposit_data_root"); mockDepositContractEth2.deposit{value: 32 ether}(pubkey, withdrawal_credentials, signature, deposit_data_root); } + */ } + diff --git a/test/TNFT.t.sol b/test/TNFT.t.sol index c6309aa37..e8b4d3d56 100644 --- a/test/TNFT.t.sol +++ b/test/TNFT.t.sol @@ -41,11 +41,11 @@ contract TnftTest is TestSetup { false ); - address etherFiNode = managerInstance.etherfiNodeAddress(1); + address etherFiNode = managerInstance.etherFiNodeFromId(1); bytes32 root = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.generateWithdrawalCredentials(etherFiNode), + managerInstance.addressToWithdrawalCredentials(etherFiNode), 32 ether ); diff --git a/test/eethPayoutUpgrade.t.sol b/test/eethPayoutUpgrade.t.sol index e9f6396ac..74e3e7abb 100644 --- a/test/eethPayoutUpgrade.t.sol +++ b/test/eethPayoutUpgrade.t.sol @@ -35,7 +35,7 @@ contract eethPayoutUpgradeTest is TestSetup { treasury = address(alice); upgradeContract(); vm.startPrank(managerInstance.owner()); - managerInstance.setStakingRewardsSplit(0, 0, 1000000, 0); +// managerInstance.setStakingRewardsSplit(0, 0, 1000000, 0); vm.stopPrank(); } @@ -221,4 +221,4 @@ contract eethPayoutUpgradeTest is TestSetup { liquidityPoolInstance.depositToRecipient(treasury, 10 ether, address(0)); vm.stopPrank(); } -} \ No newline at end of file +} From 11110ad575e182928a3e72f178936f7254d5bbc4 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 30 Apr 2025 16:31:20 -0600 Subject: [PATCH 03/47] compiling again. still a bunch of stuff to fix/implement --- script/deploys/DeployEtherFISuite.s.sol | 2 +- script/deploys/DeployPhaseOne.s.sol | 2 +- script/deploys/DeployPhaseTwo.s.sol | 2 +- script/specialized/UpdateAdminScripts.s.sol | 2 +- src/LiquidityPool.sol | 40 ++++++++++-- src/StakingManager.sol | 71 ++++++++++----------- src/interfaces/IEtherFiNodesManager.sol | 7 +- src/interfaces/IStakingManager.sol | 16 +++++ test/AuctionManager.t.sol | 14 ++++ test/BNFT.t.sol | 4 ++ test/LiquidityPool.t.sol | 14 ++++ test/TNFT.t.sol | 2 + test/TestSetup.sol | 15 ++++- 13 files changed, 140 insertions(+), 51 deletions(-) diff --git a/script/deploys/DeployEtherFISuite.s.sol b/script/deploys/DeployEtherFISuite.s.sol index 4dc139811..644138f30 100644 --- a/script/deploys/DeployEtherFISuite.s.sol +++ b/script/deploys/DeployEtherFISuite.s.sol @@ -117,7 +117,7 @@ contract DeployEtherFiSuiteScript is Script { stakingManagerImplementation = new StakingManager(); stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation),""); stakingManager = StakingManager(address(stakingManagerProxy)); - stakingManager.initialize(address(auctionManager), ethDepositContractAddress); + //stakingManager.initialize(address(auctionManager), ethDepositContractAddress); BNFTImplementation = new BNFT(); BNFTProxy = new UUPSProxy(address(BNFTImplementation),""); diff --git a/script/deploys/DeployPhaseOne.s.sol b/script/deploys/DeployPhaseOne.s.sol index 2f0fa9a29..c648a22fe 100644 --- a/script/deploys/DeployPhaseOne.s.sol +++ b/script/deploys/DeployPhaseOne.s.sol @@ -84,7 +84,7 @@ contract DeployPhaseOne is Script { stakingManagerImplementation = new StakingManager(); stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation),""); stakingManager = StakingManager(address(stakingManagerProxy)); - stakingManager.initialize(address(auctionManager), ethDepositContractAddress); + //stakingManager.initialize(address(auctionManager), ethDepositContractAddress); BNFTImplementation = new BNFT(); BNFTProxy = new UUPSProxy(address(BNFTImplementation),""); diff --git a/script/deploys/DeployPhaseTwo.s.sol b/script/deploys/DeployPhaseTwo.s.sol index d62c25bd3..f45a7cecd 100644 --- a/script/deploys/DeployPhaseTwo.s.sol +++ b/script/deploys/DeployPhaseTwo.s.sol @@ -164,7 +164,7 @@ contract DeployPhaseTwoScript is Script { if (false) { address admin = address(etherFiAdminInstance); IAuctionManager(address(auctionAddress)).updateAdmin(admin, true); - IStakingManager(address(stakingManagerAddress)).updateAdmin(admin, true); + //IStakingManager(address(stakingManagerAddress)).updateAdmin(admin, true); // ILiquidityPool(address(liquidityPoolAddress)).updateAdmin(admin, true); IMembershipManager(address(membershipManagerAddress)).updateAdmin(admin, true); //IEtherFiNodesManager(address(managerAddress)).updateAdmin(admin, true); diff --git a/script/specialized/UpdateAdminScripts.s.sol b/script/specialized/UpdateAdminScripts.s.sol index c48bdc746..d8b6e92b0 100644 --- a/script/specialized/UpdateAdminScripts.s.sol +++ b/script/specialized/UpdateAdminScripts.s.sol @@ -43,7 +43,7 @@ contract UpdateAdmins is Script { //EtherFiNodesManager(payable(etherFiNodesManager)).updateAdmin(admin, true); // ProtocolRevenueManager(payable(protocolRevenueManager)).updateAdmin(admin); // DEPRECATED AuctionManager(auctionManager).updateAdmin(admin, true); - StakingManager(stakingManager).updateAdmin(admin, true); + //StakingManager(stakingManager).updateAdmin(admin, true); // LiquidityPool(payable(liquidityPool)).updateAdmin(admin, true); // RegulationsManager(regulationsManager).updateAdmin(admin, true); MembershipManager(payable(membershipManager)).updateAdmin(admin, true); diff --git a/src/LiquidityPool.sol b/src/LiquidityPool.sol index 29510cbf9..3e459dce8 100644 --- a/src/LiquidityPool.sol +++ b/src/LiquidityPool.sol @@ -277,12 +277,11 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL } // [Liquidty Pool Staking flow] - // Step 1: [Deposit] initiate spinning up the validators & allocate withdrawal safe contracts - // Step 2: (Off-chain) create the keys using the desktop app - // Step 3: [Register] register the validator keys sending 1 ETH to the eth deposit contract - // Step 4: wait for the oracle to approve and send the rest 31 ETH to the eth deposit contract + // Step 1: (Off-chain) create the keys using the desktop app + // Step 2: create validators with 1 eth deposits to official deposit contract + // Step 3: oracle approves and funds the remaining balance for the validator - /// Step 1. [Deposit] + /// Step 2. createValidators sending 1 eth deposits for each /// @param _candidateBidIds validator IDs that have been matched with the BNFT holder on the FE /// @param _numberOfValidators how many validators the user wants to spin up. This can be less than the candidateBidIds length. function batchDeposit(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external whenNotPaused returns (uint256[] memory) { @@ -300,10 +299,14 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL require(validatorSpawner[msg.sender].registered, "Incorrect Caller"); require(totalValueInLp >= 32 ether * _numberOfValidators, "Not enough balance"); + /* uint256[] memory newValidators = stakingManager.batchDepositWithBidIds(_candidateBidIds, _numberOfValidators, msg.sender, tnftHolder, bnftHolder, SourceOfFunds.EETH, restakeBnftDeposits, _validatorIdToShareSafeWith); numPendingDeposits += uint32(newValidators.length); return newValidators; + */ + uint256[] memory newValidators = new uint256[](_candidateBidIds.length); + return newValidators; } /// Step 3. [Register] @@ -319,6 +322,18 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL bytes32[] calldata _depositDataRootApproval, bytes[] calldata _signaturesForApprovalDeposit ) external whenNotPaused { + require(validatorSpawner[msg.sender].registered, "Incorrect Caller"); + + // TODO(dave): add as param. + address DebugNodeAddress = address(0x1234); + + // As the LP is the B-nft holder, the 1 ether (for each validator) is taken from the LP + uint256 outboundEthAmountFromLp = 1 ether * _validatorIds.length; + _accountForEthSentOut(outboundEthAmountFromLp); + + stakingManager.createBeaconValidators{value: outboundEthAmountFromLp}(_registerValidatorDepositData, _validatorIds, DebugNodeAddress); + + /* address _bnftRecipient = address(this); require(validatorSpawner[msg.sender].registered, "Incorrect Caller"); @@ -336,6 +351,7 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL depositDataRootForApprovalDeposits[_validatorIds[i]] = _depositDataRootApproval[i]; emit ValidatorRegistered(_validatorIds[i], _signaturesForApprovalDeposit[i], _registerValidatorDepositData[i].publicKey, _depositDataRootApproval[i]); } + */ } //. Step 4. [Approve] @@ -351,6 +367,8 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL bytes[] calldata _signature ) external whenNotPaused { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + + /* require(_validatorIds.length == _pubKey.length && _validatorIds.length == _signature.length, "lengths differ"); bytes32[] memory depositDataRootApproval = new bytes32[](_validatorIds.length); @@ -366,6 +384,16 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL _accountForEthSentOut(outboundEthAmountFromLp); stakingManager.batchApproveRegistration{value: outboundEthAmountFromLp}(_validatorIds, _pubKey, _signature, depositDataRootApproval); + */ + + // TODO(dave): need to update params. to take deposit data + validatorSize + uint256 outboundEthAmountFromLp = 31 ether * _validatorIds.length; + _accountForEthSentOut(outboundEthAmountFromLp); + + IStakingManager.DepositData[] memory debugDepositData = new IStakingManager.DepositData[](_validatorIds.length); + //stakingManager.confirmAndFundBeaconValidators(debugDepositData, _validatorIds, 32 ether); + stakingManager.confirmAndFundBeaconValidators(debugDepositData, 32 ether); + } /// @notice Cancels the process @@ -389,7 +417,7 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL } */ - stakingManager.batchCancelDepositAsBnftHolder(_validatorIds, msg.sender); + //stakingManager.batchCancelDepositAsBnftHolder(_validatorIds, msg.sender); } /// @notice The admin can register an address to become a BNFT holder diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 2c23e76d9..e0847f3c3 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -63,6 +63,7 @@ contract StakingManager is UpgradeableBeacon private upgradableBeacon; address public etherFiNodeImplementation; + address public immutable liquidityPool; IEtherFiNodesManager public immutable etherFiNodesManager; IDepositContract public immutable depositContractEth2; IAuctionManager public immutable auctionManager; @@ -78,11 +79,17 @@ contract StakingManager is error InactiveBid(); error IncorrectValidatorFunds(); error InvalidEtherFiNode(); + error InvalidValidatorSize(); + error InvalidUpgrade(); event validatorCreated(bytes32 indexed pubkeyHash, address indexed etherFiNode, bytes pubkey); event validatorConfirmed(bytes32 indexed pubkeyHash, address indexed bnftRecipient, address indexed tnftRecipient, bytes pubkey); event linkLegacyValidatorId(bytes32 indexed pubkeyHash, uint256 indexed legacyId); + // legacy event still being emitted in its original form to play nice with existing external tooling + event ValidatorRegistered(address indexed operator, address indexed bNftOwner, address indexed tNftOwner, + uint256 validatorId, bytes validatorPubKey, string ipfsHashForEncryptedValidatorKey); + //-------------------------------------------------------------------------------------- //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ @@ -93,23 +100,16 @@ contract StakingManager is _disableInitializers(); } - function _authorizeUpgrade(address newImplementation) internal override {} - - // TODO(dave): are we using a slightly different proxy for this contract? - - /// @notice Fetches the address of the implementation contract currently being used by the proxy - /// @return the address of the currently used implementation contract - function getImplementation() external view returns (address) { - return _getImplementation(); + // TODO(dave): implement + function initialize() external { + } - - /// @notice send 1 eth to deposit contract to create the validator. + /// @notice send 1 eth to deposit contract to create the validator. /// The rest of the eth will not be sent until the oracle confirms the withdrawal credentials - function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode, address nodeOperator) external payable { - if (msg.sender != etherfiOracle) revert InvalidCaller(); + function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode) external payable { + if (msg.sender != liquidityPool) revert InvalidCaller(); if (depositData.length != bidIds.length) revert InvalidDepositData(); - if (msg.value != 1 ether * depositData.length) revert IncorrectValidatorFunds(); if (address(IEtherFiNode(etherFiNode).getEigenPod()) == address(0)) revert InvalidEtherFiNode(); // process each 1 eth deposit to create validators for later verification from oracle @@ -120,7 +120,7 @@ contract StakingManager is auctionManager.updateSelectedBidInformation(bidIds[i]); // verify deposit root - bytes memory withdrawalCredentials = etherFiNodesManager.addressToWithdrawalCredentials(etherFiNode); + bytes memory withdrawalCredentials = etherFiNodesManager.addressToWithdrawalCredentials(address(IEtherFiNode(etherFiNode).getEigenPod())); bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, 1 ether); if (computedDataRoot != depositData[i].depositDataRoot) revert IncorrectBeaconRoot(); @@ -136,10 +136,11 @@ contract StakingManager is } } - /// @notice send 31 eth to activate validators created by "createBeaconValidators" + /// @notice send remaining eth to activate validators created by "createBeaconValidators" /// The oracle is expected to have confirmed the withdrawal credentials - function confirmAndFundBeaconValidators(DepositData[] calldata depositData, address BnftRecipient, address TnftRecipient) external payable { + function confirmAndFundBeaconValidators(DepositData[] calldata depositData, uint256 validatorSizeWei) external payable { if (msg.sender != etherfiOracle) revert InvalidCaller(); + if (validatorSizeWei < 32 ether || validatorSizeWei > 2048 ether) revert InvalidValidatorSize(); for (uint256 i = 0; i < depositData.length; i++) { @@ -154,18 +155,16 @@ contract StakingManager is bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, 31 ether); if (computedDataRoot != depositData[i].depositDataRoot) revert IncorrectBeaconRoot(); - // Deposit the remaining 31 eth to activate validator - depositContractEth2.deposit{value: 31 ether}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); + // Deposit the remaining eth to the validator + uint256 amountToDeposit = validatorSizeWei - 1 ether; // already did 1 eth deposit + depositContractEth2.deposit{value: amountToDeposit}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); // Use pubkey hash as the minted token ID - tnft.mint(BnftRecipient, uint256(pubkeyHash)); - bnft.mint(TnftRecipient, uint256(pubkeyHash)); + tnft.mint(liquidityPool, uint256(pubkeyHash)); + bnft.mint(liquidityPool, uint256(pubkeyHash)); - emit validatorConfirmed(pubkeyHash, BnftRecipient, TnftRecipient, depositData[i].publicKey); + emit validatorConfirmed(pubkeyHash, liquidityPool, liquidityPool, depositData[i].publicKey); } - - // TODO: emission of the ipfs key has been left out to await the decisions - // around validator key management changes, as they will effect this. } ///@notice Calculates the pubkey hash of a validator's pubkey as per SSZ spec @@ -174,27 +173,24 @@ contract StakingManager is return sha256(abi.encodePacked(pubkey, bytes16(0))); } + // TODO(dave): reimplement pausing with role registry + function pauseContract() external { _pause(); } + function unPauseContract() external { _unpause(); } - - /////////////////////////////////////////////////////////////////////////////////////// + //-------------------------------------------------------------------------------------- + //--------------------- EtherFiNode Beacon Proxy ---------------------------------- + //-------------------------------------------------------------------------------------- /// @notice Upgrades the etherfi node /// @param _newImplementation The new address of the etherfi node function upgradeEtherFiNode(address _newImplementation) public onlyOwner { - require(_newImplementation != address(0), "ZERO_ADDRESS"); - + if (_newImplementation == address(0)) revert InvalidUpgrade(); + upgradableBeacon.upgradeTo(_newImplementation); etherFiNodeImplementation = _newImplementation; } - // TODO(dave): reimplement pausing with role registry - function pauseContract() external { _pause(); } - function unPauseContract() external { _unpause(); } - - - //-------------------------------------------------------------------------------------- - //--------------------- EtherFiNode Beacon Proxy ---------------------------------- - //-------------------------------------------------------------------------------------- + function _authorizeUpgrade(address newImplementation) internal override {} /// @notice Fetches the address of the beacon contract for future EtherFiNodes (withdrawal safes) function getEtherFiNodeBeacon() external view returns (address) { @@ -203,7 +199,8 @@ contract StakingManager is /// @notice Fetches the address of the implementation contract currently being used by the beacon proxy /// @return the address of the currently used implementation contract - function getEtherFiNodeImplementation() public view returns (address) { + function implementation() public view override returns (address) { return upgradableBeacon.implementation(); } + } diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 8e67abcc5..7c4472270 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -18,6 +18,9 @@ import "../interfaces/IStakingManager.sol"; interface IEtherFiNodesManager { struct LegacyManagerState { + uint64 test; + mapping(uint256 => address) etherfiNodeAddress; + /* uint64 numberOfValidators; // # of validators in LIVE or WAITING_FOR_APPROVAL phases uint64 nonExitPenaltyPrincipal; uint64 nonExitPenaltyDailyRate; // in basis points @@ -28,8 +31,9 @@ interface IEtherFiNodesManager { address DEPRECATED_protocolRevenueManagerContract; // validatorId == bidId -> withdrawalSafeAddress - mapping(uint256 => address) etherfiNodeAddress; + */ + /* address tnft; address bnft; IAuctionManager auctionManager; @@ -61,6 +65,7 @@ interface IEtherFiNodesManager { mapping(bytes4 => bool) allowedForwardedEigenpodCalls; // function -> target_address -> allowed mapping(bytes4 => mapping(address => bool)) allowedForwardedExternalCalls; + */ } struct ValidatorInfo { diff --git a/src/interfaces/IStakingManager.sol b/src/interfaces/IStakingManager.sol index 75e4d56c7..e4640bb20 100644 --- a/src/interfaces/IStakingManager.sol +++ b/src/interfaces/IStakingManager.sol @@ -11,6 +11,21 @@ interface IStakingManager { string ipfsHashForEncryptedValidatorKey; } + function pauseContract() external; + function unPauseContract() external; + + function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode) external payable; + function confirmAndFundBeaconValidators(DepositData[] calldata depositData, uint256 validatorSizeWei) external payable; + + function calculateValidatorPubkeyHash(bytes memory pubkey) external pure returns (bytes32); + function upgradeEtherFiNode(address _newImplementation) external; + + function getEtherFiNodeBeacon() external view returns (address); + + // this is implementation for the etherFiNode as the staking manager serves as the beacon + //function implementation() external view returns (address); + + /* struct StakerInfo { address staker; ILiquidityPool.SourceOfFunds sourceOfFund; @@ -41,4 +56,5 @@ interface IStakingManager { function updateAdmin(address _address, bool _isAdmin) external; function pauseContract() external; function unPauseContract() external; + */ } diff --git a/test/AuctionManager.t.sol b/test/AuctionManager.t.sol index 231052a9f..34a8ff350 100644 --- a/test/AuctionManager.t.sol +++ b/test/AuctionManager.t.sol @@ -39,6 +39,7 @@ contract AuctionManagerTest is TestSetup { assertTrue(auctionInstance.whitelistEnabled()); } + /* function test_ReEnterAuctionManagerFailsIfNotCorrectCaller() public { vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -62,7 +63,9 @@ contract AuctionManagerTest is TestSetup { vm.expectRevert("Only staking manager contract function"); auctionInstance.reEnterAuction(1); } + */ + /* function test_ReEnterAuctionManagerFailsIfBidAlreadyActive() public { vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -96,7 +99,9 @@ contract AuctionManagerTest is TestSetup { vm.expectRevert("Bid already active"); auctionInstance.reEnterAuction(bidId1[0]); } + */ + /* function test_ReEnterAuctionManagerWorks() public { vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -141,6 +146,7 @@ contract AuctionManagerTest is TestSetup { assertEq(isBid2Active, true); assertEq(address(auctionInstance).balance, 0.15 ether); } + */ function test_DisableWhitelist() public { assertTrue(auctionInstance.whitelistEnabled()); @@ -677,6 +683,7 @@ contract AuctionManagerTest is TestSetup { assertEq(auctionInstance.numberOfActiveBids(), 1); } + /* function test_ProcessAuctionFeeTransfer() public { vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -723,6 +730,7 @@ contract AuctionManagerTest is TestSetup { assertEq(etherFiNode.balance, 0 ether); assertEq(auctionInstance.accumulatedRevenue(), 0.1 ether); } + */ function test_SetMaxBidAmount() public { vm.prank(alice); @@ -793,6 +801,7 @@ contract AuctionManagerTest is TestSetup { auctionInstance.createBid{value: 0.2 ether}(1, 0.2 ether); } + /* function test_EventBidReEnteredAuction() public { vm.prank(alice); nodeOperatorManagerInstance.registerNodeOperator( @@ -810,6 +819,7 @@ contract AuctionManagerTest is TestSetup { emit BidReEnteredAuction(bidIds[0]); stakingManagerInstance.batchCancelDeposit(bidIds); } + */ function test_EventBidCancelled() public { @@ -849,6 +859,7 @@ contract AuctionManagerTest is TestSetup { assertEq(auctionInstance.accumulatedRevenueThreshold(), 2 ether); } + /* function test_AccumulateAuctionRevenue() public { vm.startPrank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator(_ipfsHash, 5); @@ -943,7 +954,9 @@ contract AuctionManagerTest is TestSetup { assertEq(address(membershipManagerInstance).balance, 1.2 ether); } + */ + /* function test_transferAccumulatedRevenue() public { vm.startPrank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator(_ipfsHash, 5); @@ -1001,4 +1014,5 @@ contract AuctionManagerTest is TestSetup { assertEq(address(auctionInstance).balance, 0 ether); assertEq(address(membershipManagerInstance).balance, 0.5 ether); } + */ } diff --git a/test/BNFT.t.sol b/test/BNFT.t.sol index 442290b21..80b51fa83 100644 --- a/test/BNFT.t.sol +++ b/test/BNFT.t.sol @@ -14,6 +14,7 @@ contract BNFTTest is TestSetup { BNFTImplementation.initialize(address(stakingManagerInstance)); } + /* function test_Mint() public { startHoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -61,6 +62,7 @@ contract BNFTTest is TestSetup { assertEq(BNFTInstance.ownerOf(1), alice); assertEq(BNFTInstance.balanceOf(alice), 1); } + */ function test_BNFTMintsFailsIfNotCorrectCaller() public { vm.startPrank(alice); @@ -68,6 +70,7 @@ contract BNFTTest is TestSetup { BNFTInstance.mint(address(alice), 1); } + /* function test_BNFTCannotBeTransferred() public { vm.prank(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -117,4 +120,5 @@ contract BNFTTest is TestSetup { 1 ); } + */ } diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index d4d7aec78..e143f4bfa 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -260,6 +260,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(eETHInstance.balanceOf(bob), 3 ether); } + /* function test_batchCancelDepositAsBnftHolder1() public { vm.deal(owner, 100 ether); @@ -316,6 +317,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(address(stakingManagerInstance).balance, 0 ether); assertEq(address(liquidityPoolInstance).balance, 64 ether); } + */ function test_sendExitRequestFails() public { uint256[] memory newValidators = new uint256[](10); @@ -734,6 +736,7 @@ contract LiquidityPoolTest is TestSetup { vm.stopPrank(); } + /* function test_DepositFromBNFTHolder() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); @@ -768,7 +771,9 @@ contract LiquidityPoolTest is TestSetup { assertEq(stakingManagerInstance.bidIdToStaker(13), alice); assertEq(stakingManagerInstance.bidIdToStaker(14), alice); } + */ + /* function test_RestakedDepositFromBNFTHolder() public { initializeRealisticFork(MAINNET_FORK); _initBid(); @@ -812,7 +817,9 @@ contract LiquidityPoolTest is TestSetup { node = IEtherFiNode(managerInstance.etherFiNodeFromId(bidIds[3])); assertFalse(address(node.getEigenPod()) == address(0x0)); } + */ + /* function test_RegisterAsBNFTHolder() public { test_DepositFromBNFTHolder(); @@ -870,7 +877,9 @@ contract LiquidityPoolTest is TestSetup { assertEq(BNFTInstance.balanceOf(address(liquidityPoolInstance)), 1); assertEq(TNFTInstance.balanceOf(address(liquidityPoolInstance)), 1); } + */ + /* function test_DepositFromBNFTHolderTwice() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); @@ -911,6 +920,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(stakingManagerInstance.bidIdToStaker(17), address(0)); assertEq(stakingManagerInstance.bidIdToStaker(18), address(0)); } + */ function test_SD_17() public { vm.deal(owner, 100 ether); @@ -959,6 +969,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(liquidityPoolInstance.getTotalPooledEther(), 128 ether); } + /* function test_goerli_test() internal { initializeRealisticFork(TESTNET_FORK); @@ -991,6 +1002,7 @@ contract LiquidityPoolTest is TestSetup { liquidityPoolInstance.batchRegister(depositRoot, newValidators, depositDataArray, depositDataRootsForApproval, sig); } + */ function test_bnftFlowCancel_1() public { setUpBnftHolders(); @@ -1130,6 +1142,7 @@ contract LiquidityPoolTest is TestSetup { vm.stopPrank(); } + /* function test_Zellic_PoC() public { setUpBnftHolders(); @@ -1157,6 +1170,7 @@ contract LiquidityPoolTest is TestSetup { vm.stopPrank(); } + */ function test_Upgrade2_49_pause_unpause() public { // only protocol pauser can pause or unpause diff --git a/test/TNFT.t.sol b/test/TNFT.t.sol index e8b4d3d56..5aab94c2a 100644 --- a/test/TNFT.t.sol +++ b/test/TNFT.t.sol @@ -23,6 +23,7 @@ contract TnftTest is TestSetup { TNFTInstance.mint(address(alice), 1); } + /* function test_Mint() public { startHoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); nodeOperatorManagerInstance.registerNodeOperator( @@ -69,4 +70,5 @@ contract TnftTest is TestSetup { assertEq(TNFTInstance.ownerOf(1), alice); assertEq(TNFTInstance.balanceOf(alice), 1); } + */ } diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 6174c8f4a..ce404fea0 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -483,8 +483,8 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen stakingManagerImplementation = new StakingManager(); stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation), ""); stakingManagerInstance = StakingManager(address(stakingManagerProxy)); - stakingManagerInstance.initialize(address(auctionInstance), address(mockDepositContractEth2)); - stakingManagerInstance.updateAdmin(alice, true); + //stakingManagerInstance.initialize(address(auctionInstance), address(mockDepositContractEth2)); + //stakingManagerInstance.updateAdmin(alice, true); TNFTImplementation = new TNFT(); TNFTProxy = new UUPSProxy(address(TNFTImplementation), ""); @@ -1015,7 +1015,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen etherFiOracleInstance.updateAdmin(alice, true); address admin = address(etherFiAdminInstance); - stakingManagerInstance.updateAdmin(admin, true); + //stakingManagerInstance.updateAdmin(admin, true); // liquidityPoolInstance.updateAdmin(admin, true); membershipManagerInstance.updateAdmin(admin, true); etherFiOracleInstance.updateAdmin(admin, true); @@ -1206,10 +1206,12 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen assertEq(registered, true); } + // TODO(dave): re-implement with v3 updates function depositAndRegisterValidator(bool restaked) public returns (uint256) { vm.deal(alice, 33 ether); vm.startPrank(alice); + /* // if we call this multiple times in a test, don't blow up try nodeOperatorManagerInstance.registerNodeOperator("fake_ipfs_hash", 10) { } catch {} @@ -1241,6 +1243,8 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen vm.stopPrank(); return createdBids[0]; + */ + return 0; } function launch_validator() internal returns (uint256[] memory) { @@ -1557,11 +1561,15 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen return depositDataArray; } + //TODO(dave): why stack too deep here? function _prepareForValidatorRegistration(uint256[] memory _validatorIds) internal returns (IStakingManager.DepositData[] memory, bytes32[] memory, bytes[] memory, bytes[] memory pubKey) { IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](_validatorIds.length); bytes32[] memory depositDataRootsForApproval = new bytes32[](_validatorIds.length); bytes[] memory sig = new bytes[](_validatorIds.length); bytes[] memory pubKey = new bytes[](_validatorIds.length); + return (depositDataArray, depositDataRootsForApproval, sig, pubKey); + + /* for (uint256 i = 0; i < _validatorIds.length; i++) { pubKey[i] = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; @@ -1590,6 +1598,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } return (depositDataArray, depositDataRootsForApproval, sig, pubKey); + */ } function _execute_timelock(address target, bytes memory data, bool _schedule, bool _log_schedule, bool _execute, bool _log_execute) internal { From 1b9da1fb74d228b455e5a1d9aeb1fa51c5cf0e3b Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 30 Apr 2025 20:12:04 -0600 Subject: [PATCH 04/47] start cleaning up interfaces --- src/EtherFiNode.sol | 6 +- src/EtherFiNodesManager.sol | 28 +++++-- src/interfaces/IEtherFiNode.sol | 106 ++---------------------- src/interfaces/IEtherFiNodesManager.sol | 90 +++++--------------- 4 files changed, 54 insertions(+), 176 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 8778eba67..60b5229a2 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -1,18 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; -//import "../lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; -//import "../lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IDelegationManager} from "../src/eigenlayer-interfaces/IDelegationManager.sol"; import {IEigenPodManager} from "../src/eigenlayer-interfaces/IEigenPodManager.sol"; import {IEigenPod} from "../src/eigenlayer-interfaces/IEigenPod.sol"; import {IERC20} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; -//import {IPodOwner} from "../src/iEtherFiNode/IPodOwner.sol"; import {IEtherFiNode} from "../src/interfaces/IEtherFiNode.sol"; import {IEtherFiNodesManager} from "../src/interfaces/IEtherFiNodesManager.sol"; import {ILiquidityPool} from "../src/interfaces/ILiquidityPool.sol"; -//import {IRewardsManager} from "../src/RewardsManager.sol"; import {LibCall} from "../lib/solady/src/utils/LibCall.sol"; contract EtherFiNode is IEtherFiNode { @@ -90,7 +86,7 @@ contract EtherFiNode is IEtherFiNode { // TODO(dave): Permissions - function callEigenPod(bytes calldata data) external returns (bytes memory) { + function forwardEigenPodCall(bytes calldata data) external returns (bytes memory) { // callContract will revert if targeting an EOA so it is safe if getEigenPod() returns the zero address return LibCall.callContract(address(getEigenPod()), 0, data); } diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 7cf507b37..e4cfce0a7 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -76,7 +76,7 @@ contract EtherFiNodesManager is address public immutable eigenPodManager; address public immutable delegationManager; - IStakingManager public immutable stakingManager; + address public immutable stakingManager; //----------------------------------------------------------------- //----------------------- Storage ------------------------------- @@ -94,7 +94,7 @@ contract EtherFiNodesManager is //-------------------------------------------------------------------------------------- event NodeExitRequested(uint256 _validatorId); event NodeExitProcessed(uint256 _validatorId); - event PhaseChanged(uint256 indexed _validatorId, IEtherFiNode.VALIDATOR_PHASE _phase); + //event PhaseChanged(uint256 indexed _validatorId, IEtherFiNode.VALIDATOR_PHASE _phase); event PartialWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); @@ -118,10 +118,22 @@ contract EtherFiNodesManager is error InvalidParams(); + function setProofSubmitter(uint256 id, address proofSubmitter) external { + // TODO(dave): implement + } + function startCheckpoint(uint256 id) external { + // TODO(dave): implement + } + // TODO(dave): reimplement pausing with role registry function pauseContract() external { _pause(); } function unPauseContract() external { _unpause(); } + function etherfiNodeAddress(uint256) public view returns (address) { + // TODO(dave): implement + return address(0); + } + function etherFiNodeFromId(uint256 id) public view returns (address) { // if the ID is a legacy validatorID use the old storage array // otherwise assume it is a pubkey hash. @@ -133,7 +145,7 @@ contract EtherFiNodesManager is if (mask & id > 0) { return address(etherFiNodeFromPubkeyHash[bytes32(id)]); } else { - return legacyState.etherfiNodeAddress[id]; + return legacyState.DEPRECATED_etherfiNodeAddress[id]; } } @@ -162,10 +174,10 @@ contract EtherFiNodesManager is if (msg.sender != address(stakingManager)) revert InvalidCaller(); bytes32 pubkeyHash = calculateValidatorPubkeyHash(pubkey); if (address(etherFiNodeFromPubkeyHash[pubkeyHash]) != address(0)) revert AlreadyLinked(); - if (legacyState.etherfiNodeAddress[legacyId] != address(0)) revert AlreadyLinked(); + if (legacyState.DEPRECATED_etherfiNodeAddress[legacyId] != address(0)) revert AlreadyLinked(); // link legacyId for now. We can remove this in a future upgrade - legacyState.etherfiNodeAddress[legacyId] = nodeAddress; + legacyState.DEPRECATED_etherfiNodeAddress[legacyId] = nodeAddress; etherFiNodeFromPubkeyHash[pubkeyHash] = IEtherFiNode(nodeAddress); emit PubkeyLinked(pubkeyHash, nodeAddress, pubkey); @@ -207,7 +219,7 @@ contract EtherFiNodesManager is bytes4 selector = bytes4(data[i][:4]); if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); - returnData[i] = etherFiNodeFromPubkeyHash[pubkeys[i]].callEigenPod(data[i]); + returnData[i] = etherFiNodeFromPubkeyHash[pubkeys[i]].forwardEigenPodCall(data[i]); } } @@ -237,4 +249,8 @@ contract EtherFiNodesManager is } } + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes memory) { + // TODO(dave): implement + } + } diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index 38fd7b43b..f98ea3b6a 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -5,108 +5,18 @@ import "./IEtherFiNodesManager.sol"; import "../eigenlayer-interfaces/IDelegationManager.sol"; - interface IEtherFiNode { - // State Transition Diagram for StateMachine contract: - // - // NOT_INITIALIZED <- - // | | - // ↓ | - // STAKE_DEPOSITED -- - // / \ | - // ↓ ↓ | - // LIVE <- WAITING_FOR_APPROVAL - // | \ - // | ↓ - // | BEING_SLASHED - // | / - // ↓ ↓ - // EXITED - // | - // ↓ - // FULLY_WITHDRAWN - // - // Transitions are only allowed as directed above. - // For instance, a transition from STAKE_DEPOSITED to either LIVE or CANCELLED is allowed, - // but a transition from LIVE to NOT_INITIALIZED is not. - // - // All phase transitions should be made through the setPhase function, - // which validates transitions based on these rules. - // - enum VALIDATOR_PHASE { - NOT_INITIALIZED, - STAKE_DEPOSITED, - LIVE, - EXITED, - FULLY_WITHDRAWN, - DEPRECATED_CANCELLED, - BEING_SLASHED, - DEPRECATED_EVICTED, - WAITING_FOR_APPROVAL, - DEPRECATED_READY_FOR_DEPOSIT - } - - function startCheckpoint() external; - function setProofSubmitter(address _newProofSubmitter) external; - function createEigenPod() external; - function getEigenPod() external view returns (IEigenPod); - function callEigenPod(bytes memory data) external returns (bytes memory); - function forwardExternalCall(address to, bytes memory data) external returns (bytes memory); - /* - // VIEW functions - function numAssociatedValidators() external view returns (uint256); - function numExitRequestsByTnft() external view returns (uint16); - function numExitedValidators() external view returns (uint16); - function version() external view returns (uint16); - function eigenPod() external view returns (address); - function calculateTVL(uint256 _beaconBalance, IEtherFiNodesManager.ValidatorInfo memory _info, IEtherFiNodesManager.RewardsSplit memory _SRsplits, bool _onlyWithdrawable) external view returns (uint256, uint256, uint256, uint256); - function getNonExitPenalty(uint32 _tNftExitRequestTimestamp, uint32 _bNftExitRequestTimestamp) external view returns (uint256); - function getRewardsPayouts(uint32 _exitRequestTimestamp, IEtherFiNodesManager.RewardsSplit memory _splits) external view returns (uint256, uint256, uint256, uint256); - function getFullWithdrawalPayouts(IEtherFiNodesManager.ValidatorInfo memory _info, IEtherFiNodesManager.RewardsSplit memory _SRsplits) external view returns (uint256, uint256, uint256, uint256); - function associatedValidatorIds(uint256 _index) external view returns (uint256); - function associatedValidatorIndices(uint256 _validatorId) external view returns (uint256); - function validatePhaseTransition(VALIDATOR_PHASE _currentPhase, VALIDATOR_PHASE _newPhase) external pure returns (bool); - function DEPRECATED_exitRequestTimestamp() external view returns (uint32); - function DEPRECATED_exitTimestamp() external view returns (uint32); - function DEPRECATED_phase() external view returns (VALIDATOR_PHASE); - - // Non-VIEW functions - function initialize(address _etherFiNodesManager) external; + // eigenlayer function createEigenPod() external; - function isRestakingEnabled() external view returns (bool); - function processNodeExit(uint256 _validatorId) external returns (bytes32[] memory withdrawalRoots); - function processFullWithdraw(uint256 _validatorId) external; - function queueEigenpodFullWithdrawal() external returns (bytes32[] memory withdrawalRoots); - function completeQueuedWithdrawals(IDelegationManager.Withdrawal[] memory withdrawals, bool _receiveAsTokens) external; - function completeQueuedWithdrawal(IDelegationManager.Withdrawal memory withdrawals, bool _receiveAsTokens) external; - function updateNumberOfAssociatedValidators(uint16 _up, uint16 _down) external; - function updateNumExitedValidators(uint16 _up, uint16 _down) external; - function registerValidator(uint256 _validatorId, bool _enableRestaking) external; - function unRegisterValidator(uint256 _validatorId, IEtherFiNodesManager.ValidatorInfo memory _info) external returns (bool); - function splitBalanceInExecutionLayer() external view returns (uint256 _withdrawalSafe, uint256 _eigenPod, uint256 _delayedWithdrawalRouter); - function totalBalanceInExecutionLayer() external view returns (uint256); - function withdrawableBalanceInExecutionLayer() external view returns (uint256); - function updateNumExitRequests(uint16 _up, uint16 _down) external; - function migrateVersion(uint256 _validatorId, IEtherFiNodesManager.ValidatorInfo memory _info) external; - - function startCheckpoint(bool _revertIfNoBalance) external; + function getEigenPod() external view returns (IEigenPod); + function startCheckpoint() external; function setProofSubmitter(address _newProofSubmitter) external; - function callEigenPod(bytes memory data) external returns (bytes memory); - function forwardCall(address to, bytes memory data) external returns (bytes memory); - - function withdrawFunds( - address _treasury, - uint256 _treasuryAmount, - address _operator, - uint256 _operatorAmount, - address _tnftHolder, - uint256 _tnftAmount, - address _bnftHolder, - uint256 _bnftAmount - ) external; + function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot); + function completeQueuedWithdrawals(bool receiveAsTokens) external; - function moveFundsToManager(uint256 _amount) external; - */ + // call forwarding + function forwardEigenPodCall(bytes memory data) external returns (bytes memory); + function forwardExternalCall(address to, bytes memory data) external returns (bytes memory); } diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 7c4472270..a0d20e960 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -17,9 +17,12 @@ import "../interfaces/IStakingManager.sol"; interface IEtherFiNodesManager { + function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); + function etherFiNodeFromId(uint256 id) external view returns (address); + struct LegacyManagerState { uint64 test; - mapping(uint256 => address) etherfiNodeAddress; + mapping(uint256 => address) DEPRECATED_etherfiNodeAddress; /* uint64 numberOfValidators; // # of validators in LIVE or WAITING_FOR_APPROVAL phases uint64 nonExitPenaltyPrincipal; @@ -68,88 +71,41 @@ interface IEtherFiNodesManager { */ } + /* struct ValidatorInfo { uint32 validatorIndex; uint32 exitRequestTimestamp; uint32 exitTimestamp; IEtherFiNode.VALIDATOR_PHASE phase; } + */ - struct RewardsSplit { - uint64 treasury; - uint64 nodeOperator; - uint64 tnft; - uint64 bnft; - } + //function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; + //function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); + //function etherFiNodeFromId(uint256 id) external view returns (address); - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); function addressToWithdrawalCredentials(address addr) external pure returns (bytes memory); + function etherfiNodeAddress(uint256 id) external view returns(address); function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; - function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); - - function getEigenPod(uint256 id) external view returns (address); - - function etherFiNodeFromId(uint256 id) external view returns (address); - function eigenPodManager() external view returns (address); function delegationManager() external view returns (address); + function stakingManager() external view returns (address); - function pauseContract() external; - function unPauseContract() external; - + // eigenlayer interactions + function getEigenPod(uint256 id) external view returns (address); + function startCheckpoint(uint256 id) external; + function setProofSubmitter(uint256 id, address _newProofSubmitter) external; + // call forwarding + function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; + function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external; + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes memory); + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); - // VIEW functions - /* - function delayedWithdrawalRouter() external view returns (IDelayedWithdrawalRouter); - function eigenPodManager() external view returns (IEigenPodManager); - function delegationManager() external view returns (IDelegationManager); - function treasuryContract() external view returns (address); - function unusedWithdrawalSafes(uint256 _index) external view returns (address); - - function etherfiNodeAddress(uint256 _validatorId) external view returns (address); - function calculateTVL(uint256 _validatorId, uint256 _beaconBalance) external view returns (uint256, uint256, uint256, uint256); - function getFullWithdrawalPayouts(uint256 _validatorId) external view returns (uint256, uint256, uint256, uint256); - function getNonExitPenalty(uint256 _validatorId) external view returns (uint256); - function getRewardsPayouts(uint256 _validatorId) external view returns (uint256, uint256, uint256, uint256); - function getWithdrawalCredentials(uint256 _validatorId) external view returns (bytes memory); - function getValidatorInfo(uint256 _validatorId) external view returns (ValidatorInfo memory); - function numAssociatedValidators(uint256 _validatorId) external view returns (uint256); - function phase(uint256 _validatorId) external view returns (IEtherFiNode.VALIDATOR_PHASE phase); - function getEigenPod(uint256 _validatorId) external view returns (IEigenPod); - - function generateWithdrawalCredentials(address _address) external view returns (bytes memory); - function nonExitPenaltyDailyRate() external view returns (uint64); - function nonExitPenaltyPrincipal() external view returns (uint64); - function numberOfValidators() external view returns (uint64); - function maxEigenlayerWithdrawals() external view returns (uint8); - - function admins(address _address) external view returns (bool); - function operatingAdmin(address _address) external view returns (bool); - - // Non-VIEW functions - function updateEtherFiNode(uint256 _validatorId) external; - - function batchQueueRestakedWithdrawal(uint256[] calldata _validatorIds) external; - function batchSendExitRequest(uint256[] calldata _validatorIds) external; - function batchFullWithdraw(uint256[] calldata _validatorIds) external; - function batchPartialWithdraw(uint256[] calldata _validatorIds) external; - function fullWithdraw(uint256 _validatorId) external; - function getUnusedWithdrawalSafesLength() external view returns (uint256); - function incrementNumberOfValidators(uint64 _count) external; - function markBeingSlashed(uint256[] calldata _validatorIds) external; - function partialWithdraw(uint256 _validatorId) external; - function processNodeExit(uint256[] calldata _validatorIds, uint32[] calldata _exitTimestamp) external; - function allocateEtherFiNode(bool _enableRestaking) external returns (address); - function registerValidator(uint256 _validatorId, bool _enableRestaking, address _withdrawalSafeAddress) external; - function setValidatorPhase(uint256 _validatorId, IEtherFiNode.VALIDATOR_PHASE _phase) external; - function setNonExitPenalty(uint64 _nonExitPenaltyDailyRate, uint64 _nonExitPenaltyPrincipal) external; - function setStakingRewardsSplit(uint64 _treasury, uint64 _nodeOperator, uint64 _tnft, uint64 _bnf) external; - function unregisterValidator(uint256 _validatorId) external; - - function updateAdmin(address _address, bool _isAdmin) external; + // protocol function pauseContract() external; function unPauseContract() external; - */ + + } From 67c0735e1de59ff046b9a1bbd2267c01f5d4da78 Mon Sep 17 00:00:00 2001 From: ReposCollector Date: Thu, 1 May 2025 17:02:19 +0900 Subject: [PATCH 05/47] repo cleaning, remove unused fields from Oracle report --- script/Create2Factory.sol | 26 +- script/DeployEigenlayerSlashing.s.sol | 44 --- script/DeployOnlyAddressCheck.js | 147 -------- script/DeployPatch2.s.sol | 87 ----- script/Deploy_Instructions_Phase_One.md | 54 --- script/EigenlayerSlashingUpgrade.s.sol | 116 ------ script/GnosisHelpers.sol | 84 ----- script/Merkletree.js | 53 --- script/README.md | 130 ------- script/addressConfig.json | 224 ------------ script/contracts.json | 1 - script/deploys/BucketRateLimiter.s.sol | 54 --- ...ployAndPopulateAddressProviderScript.s.sol | 86 ----- script/deploys/DeployBucketLimiter.s.sol | 19 - script/deploys/DeployEarlyAdopterPool.s.sol | 95 ----- script/deploys/DeployEtherFISuite.s.sol | 335 ------------------ .../DeployEtherFiOperationParameters.s.sol | 47 --- script/deploys/DeployEtherFiRestaker.s.sol | 44 --- .../deploys/DeployEtherFiRewardsRouter.s.sol | 40 --- script/deploys/DeployEtherFiViewer.s.sol | 36 -- .../DeployEtherFiWithdrawalBuffer.s.sol | 40 --- .../DeployImplementationContract.s.sol | 29 -- script/deploys/DeployLiquifier.s.sol | 97 ----- .../deploys/DeployLoyaltyPointsMarketSafe.sol | 29 -- script/deploys/DeployMultiCall.s.sol | 62 ---- script/deploys/DeployPhaseOne.s.sol | 250 ------------- script/deploys/DeployPhaseOnePointFive.s.sol | 149 -------- script/deploys/DeployPhaseTwo.s.sol | 175 --------- script/deploys/DeployRoleRegistry.s.sol | 31 -- script/deploys/DeployTVLOracle.s.sol | 32 -- script/deploys/DeployV2Dot49.s.sol | 289 --------------- script/deploys/EtherFiTimelock.sol | 43 --- .../DeployNewNodeOperatorManager.s.sol | 103 ------ script/specialized/DeployProxyForMock.s.sol | 35 -- script/specialized/RegisterValidator.s.sol | 73 ---- script/specialized/TransferOwnership.s.sol | 65 ---- script/specialized/UpdateAdminScripts.s.sol | 56 --- script/specialized/weEth_withdrawal_v2.s.sol | 131 ------- script/upgrade_v1.5.sh | 24 -- .../AuctionManagerUpgradeScript.s.sol | 35 -- script/upgrades/BNFTUpgradeScript.s.sol | 34 -- script/upgrades/BucketRateLimiter.s.sol | 34 -- script/upgrades/EETHUpgradeScript.s.sol | 33 -- .../upgrades/EtherFiAdminUpgradeScript.s.sol | 29 -- script/upgrades/EtherFiNodeScript.s.sol | 36 -- .../EtherFiNodesManagerUpgradeScript.s.sol | 57 --- .../upgrades/EtherFiOracleUpgradeScript.s.sol | 29 -- .../upgrades/LiquidityPoolUpgradeScript.s.sol | 71 ---- script/upgrades/Liquifier.s.sol | 99 ------ .../MembershipManagerUpgradeScript.s.sol | 52 --- .../upgrades/MembershipNFTUpgradeScript.s.sol | 42 --- .../upgrades/MultipleValidatorsPerSafe.s.sol | 101 ------ .../upgrades/NFTExchangeUpgradeScript.s.sol | 29 -- .../NodeOperatorManagerUpgradeScript.s.sol | 29 -- .../ProtocolRevenueManagerUpgradeScript.s.sol | 29 -- .../RegulationsManagerUpgradeScript.s.sol | 29 -- .../StakingManagerUpgradeScript.s.sol | 38 -- script/upgrades/TNFTUpgradeScript.s.sol | 34 -- script/upgrades/UpgradeForEigenLayerM2.s.sol | 104 ------ script/upgrades/WeETHUpgradeScript.s.sol | 33 -- .../WithdrawRequestNFTUpgradeScript.s.sol | 29 -- src/EtherFiAdmin.sol | 6 +- src/EtherFiOracle.sol | 9 +- src/{ => archive}/LoyaltyPointsMarketSafe.sol | 0 src/{ => archive}/NFTExchange.sol | 6 +- src/{ => archive}/Treasury.sol | 2 +- src/interfaces/IEtherFiAdmin.sol | 1 - src/interfaces/IEtherFiNode.sol | 1 + src/interfaces/IEtherFiOracle.sol | 5 - test/EtherFiAdminUpgrade.t.sol | 7 +- test/EtherFiOracle.t.sol | 7 - test/L2Constants.sol | 262 -------------- test/LiquidityPool.t.sol | 54 --- test/StakingManager.t.sol | 1 - test/TestSetup.sol | 52 +-- test/Treasury.t.sol | 67 ---- test/eethPayoutUpgrade.t.sol | 224 ------------ test/eethPayoutUpgradeFork.t.sol | 119 ------- 78 files changed, 45 insertions(+), 5219 deletions(-) delete mode 100644 script/DeployEigenlayerSlashing.s.sol delete mode 100644 script/DeployOnlyAddressCheck.js delete mode 100644 script/DeployPatch2.s.sol delete mode 100644 script/Deploy_Instructions_Phase_One.md delete mode 100644 script/EigenlayerSlashingUpgrade.s.sol delete mode 100644 script/GnosisHelpers.sol delete mode 100644 script/Merkletree.js delete mode 100644 script/README.md delete mode 100644 script/addressConfig.json delete mode 100644 script/contracts.json delete mode 100644 script/deploys/BucketRateLimiter.s.sol delete mode 100644 script/deploys/DeployAndPopulateAddressProviderScript.s.sol delete mode 100644 script/deploys/DeployBucketLimiter.s.sol delete mode 100644 script/deploys/DeployEarlyAdopterPool.s.sol delete mode 100644 script/deploys/DeployEtherFISuite.s.sol delete mode 100644 script/deploys/DeployEtherFiOperationParameters.s.sol delete mode 100644 script/deploys/DeployEtherFiRestaker.s.sol delete mode 100644 script/deploys/DeployEtherFiRewardsRouter.s.sol delete mode 100644 script/deploys/DeployEtherFiViewer.s.sol delete mode 100644 script/deploys/DeployEtherFiWithdrawalBuffer.s.sol delete mode 100644 script/deploys/DeployImplementationContract.s.sol delete mode 100644 script/deploys/DeployLiquifier.s.sol delete mode 100644 script/deploys/DeployLoyaltyPointsMarketSafe.sol delete mode 100644 script/deploys/DeployMultiCall.s.sol delete mode 100644 script/deploys/DeployPhaseOne.s.sol delete mode 100644 script/deploys/DeployPhaseOnePointFive.s.sol delete mode 100644 script/deploys/DeployPhaseTwo.s.sol delete mode 100644 script/deploys/DeployRoleRegistry.s.sol delete mode 100644 script/deploys/DeployTVLOracle.s.sol delete mode 100644 script/deploys/DeployV2Dot49.s.sol delete mode 100644 script/deploys/EtherFiTimelock.sol delete mode 100644 script/specialized/DeployNewNodeOperatorManager.s.sol delete mode 100644 script/specialized/DeployProxyForMock.s.sol delete mode 100644 script/specialized/RegisterValidator.s.sol delete mode 100644 script/specialized/TransferOwnership.s.sol delete mode 100644 script/specialized/UpdateAdminScripts.s.sol delete mode 100644 script/specialized/weEth_withdrawal_v2.s.sol delete mode 100644 script/upgrade_v1.5.sh delete mode 100644 script/upgrades/AuctionManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/BNFTUpgradeScript.s.sol delete mode 100644 script/upgrades/BucketRateLimiter.s.sol delete mode 100644 script/upgrades/EETHUpgradeScript.s.sol delete mode 100644 script/upgrades/EtherFiAdminUpgradeScript.s.sol delete mode 100644 script/upgrades/EtherFiNodeScript.s.sol delete mode 100644 script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/EtherFiOracleUpgradeScript.s.sol delete mode 100644 script/upgrades/LiquidityPoolUpgradeScript.s.sol delete mode 100644 script/upgrades/Liquifier.s.sol delete mode 100644 script/upgrades/MembershipManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/MembershipNFTUpgradeScript.s.sol delete mode 100644 script/upgrades/MultipleValidatorsPerSafe.s.sol delete mode 100644 script/upgrades/NFTExchangeUpgradeScript.s.sol delete mode 100644 script/upgrades/NodeOperatorManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/ProtocolRevenueManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/RegulationsManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/StakingManagerUpgradeScript.s.sol delete mode 100644 script/upgrades/TNFTUpgradeScript.s.sol delete mode 100644 script/upgrades/UpgradeForEigenLayerM2.s.sol delete mode 100644 script/upgrades/WeETHUpgradeScript.s.sol delete mode 100644 script/upgrades/WithdrawRequestNFTUpgradeScript.s.sol rename src/{ => archive}/LoyaltyPointsMarketSafe.sol (100%) rename src/{ => archive}/NFTExchange.sol (97%) rename src/{ => archive}/Treasury.sol (96%) delete mode 100644 test/L2Constants.sol delete mode 100644 test/Treasury.t.sol delete mode 100644 test/eethPayoutUpgrade.t.sol delete mode 100644 test/eethPayoutUpgradeFork.t.sol diff --git a/script/Create2Factory.sol b/script/Create2Factory.sol index ba8b2a470..a11006936 100644 --- a/script/Create2Factory.sol +++ b/script/Create2Factory.sol @@ -4,12 +4,32 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/Create2.sol"; contract Create2Factory { - event Deployed(address addr, address deployer, bytes32 bytecode_hash, bytes32 salt); + event Deployed(address addr, address deployer, bytes32 bytecodeHash, bytes32 salt); + /// @notice Deploys a contract using CREATE2 + /// @param code The contract bytecode including constructor arguments + /// @param salt A unique value to influence contract address function deploy(bytes memory code, bytes32 salt) external payable returns (address) { address addr = Create2.deploy(msg.value, salt, code); - emit Deployed(addr, address(this), keccak256(code), salt); + emit Deployed(addr, msg.sender, keccak256(code), salt); return addr; } -} \ No newline at end of file + + /// @notice Computes the deterministic address of a contract + /// @param salt The salt used for deployment + /// @param code The bytecode including constructor arguments + /// @return predicted The predicted deterministic contract address + function computeAddress(bytes32 salt, bytes memory code) public view returns (address predicted) { + predicted = Create2.computeAddress(salt, keccak256(code), address(this)); + } + + /// @notice Verifies whether a given deployed address matches provided code and salt + /// @param addr The address to verify + /// @param salt The salt used for the deployment + /// @param code The contract bytecode including constructor arguments + /// @return True if the address matches the computed address, false otherwise + function verify(address addr, bytes32 salt, bytes memory code) external view returns (bool) { + return (addr == computeAddress(salt, code)); + } +} diff --git a/script/DeployEigenlayerSlashing.s.sol b/script/DeployEigenlayerSlashing.s.sol deleted file mode 100644 index 74709b56a..000000000 --- a/script/DeployEigenlayerSlashing.s.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "../src/EtherFiNode.sol"; -import "../src/EtherFiNodesManager.sol"; -import "../src/EtherFiRestaker.sol"; -import "../src/helpers/AddressProvider.sol"; -import "../src/UUPSProxy.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "forge-std/console2.sol"; - -contract DeployEigenlayerSlashingScript is Script { - using Strings for string; - - UUPSProxy public liquifierProxy; - - EtherFiNode public etherFiNodeImplementation; - EtherFiNode public etherFiNodeInstance; - EtherFiNodesManager public etherFiNodesManagerImplementation; - EtherFiNodesManager public etherFiNodesManagerInstance; - EtherFiRestaker public etherFiRestakerImplementation; - EtherFiRestaker public etherFiRestakerInstance; - - AddressProvider public addressProvider; - - address rewardsCoordinator = 0x7750d328b314EfFa365A0402CcfD489B80B0adda; - - function run() external { - - vm.startBroadcast(); - - etherFiNodeImplementation = new EtherFiNode(address(0),address(0),address(0),address(0)); - etherFiNodesManagerImplementation = new EtherFiNodesManager(); - etherFiRestakerImplementation = new EtherFiRestaker(rewardsCoordinator); - - console2.log("etherFiNode Impl:", address(etherFiNodeImplementation)); - console2.log("etherFiNodesManager Impl:", address(etherFiNodesManagerImplementation)); - console2.log("etherFiRestaker Impl:", address(etherFiRestakerImplementation)); - - vm.stopBroadcast(); - } -} diff --git a/script/DeployOnlyAddressCheck.js b/script/DeployOnlyAddressCheck.js deleted file mode 100644 index 2ae5384f1..000000000 --- a/script/DeployOnlyAddressCheck.js +++ /dev/null @@ -1,147 +0,0 @@ -const fs = require('fs'); -const ethers = require('ethers'); -require('dotenv').config(); - -/* -how to run: -1. Create .env file where: - ETHERSCAN_API_KEY=, MAINNET_ADDRESS_PROVIDER=0x8487c5F8550E3C3e7734Fe7DCF77DB2B72E4A848, and GOERLI_ADDRESS_PROVIDER=0x6E429db4E1a77bCe9B6F9EDCC4e84ea689c1C97e -2. need ethers, dotenv, and fs modules installed - 3. Write config file: node DeployOnlyAddressCheck.js -write - 4. Validate that the config file is correct - 5. To check addresses: node DeployOnlyAddressCheck.js (network is optional, defaults to mainnet) -*/ - -function getContractNames() { - const contracts = fs.readFileSync('contracts.json', - { encoding: 'utf8', flag: 'r' }); - var arr = JSON.parse(contracts); - return arr -} - -function getABI(fileName) { - abiDirectory = "../release/abis" - const files = fs.readdirSync(abiDirectory) - var arr = [] - retval = "" - for (const file of files) { - if (String(fileName + ".json").toLowerCase() == String(file).toLowerCase()) { - abi = fs.readFileSync(abiDirectory + "/" + file, - { encoding: 'utf8', flag: 'r' }); - retval = abi - break - } - } - return retval -} - -async function callMethod(contractAddress, abi, functionName, args, network) { - let provider = new ethers.providers.EtherscanProvider(network, process.env.ETHERSCAN_API_KEY) - let contract = new ethers.Contract(contractAddress, abi, provider); - return await contract[functionName](...args).catch((err) => { - console.log("ERROR CALLING METHOD " + functionName) - }) -} - -function contractSubstring(method, contracts) { - for (contract of contracts) { - if (method.toLowerCase() == contract.toLowerCase()) return contract - } - for (contract of contracts) { - meth = method.toLowerCase() - con = contract.toLowerCase() - if (meth.includes(con) || con.includes(meth)) { - return contract - } - } - return "" -} - -function isDeprecated(method) { - return (method.toLowerCase().includes("deprecated")) -} - -function writeConfigFile() { - contracts = getContractNames() - jsonConfig = {} - - for (contract of contracts) { - arr = [] - jsonConfig[contract] = { arr }; - } - contracts.forEach(contract => { - abi = getABI(contract) - abi = JSON.parse(abi) - methods = [] - for (let i = 0; i < abi.length; i++) { - method = abi[i] - if (method["type"] != undefined && method["stateMutability"] != undefined) { - if (method["type"] == "function" && method["stateMutability"] == "view" && - method["inputs"].length == 0 && method["outputs"].length == 1 && method["outputs"][0]["type"] == "address" && !isDeprecated(method["name"])) { //check if returns address - methodSubstring = contractSubstring(method["name"], contracts) - if (methodSubstring != "") { - methods.push({ - "methodName": method["name"], - "value": methodSubstring, - "isReference": true - }) - } - } - } - } - jsonConfig[contract] = methods - }) - const filePath = 'addressConfig.json'; - if (!fs.existsSync(filePath)) { - fs.writeFileSync(filePath, JSON.stringify(jsonConfig)); - } else { - console.log('File already exists'); - } -} - -async function checkFunctionAddress(network) { - const file = fs.readFileSync('addressConfig.json', - { encoding: 'utf8', flag: 'r' }); - var contract_Methods = JSON.parse(file); - contracts = getContractNames() - contract_address = {} - addressProvider = "" - - if (network == "mainnet") addressProvider = process.env.MAINNET_ADDRESS_PROVIDER - else if (network == "goerli") addressProvider = process.env.GOERLI_ADDRESS_PROVIDER - addressProviderABI = getABI("AddressProvider") - addyProviderFunName = "getContractAddress" - - for (contract of contracts) { //populate map of contract addresses - var addy = await callMethod(addressProvider, addressProviderABI, addyProviderFunName, [contract], network) - contract_address[contract] = addy - } - for (contract of contracts) { - methods = contract_Methods[contract] - for (method of methods) { - if (method["value"] != "" && method["isReference"] == true) { - address = await callMethod(contract_address[contract], getABI(contract), method["methodName"], [], network) - if (address != contract_address[method["value"]]) { - console.log("contract:" + contract + " method:" + method["methodName"] + " address:" + address + " correct address:" + contract_address[method["value"]]) - } - } - } - } -} - -async function main() { - const args = process.argv; - network = "mainnet" - if (args.length > 2) { - if (args[2] == "-write") { - writeConfigFile() - return - } else { //assume network - network = args[2] - } - } - checkFunctionAddress(network) -} - -main() - diff --git a/script/DeployPatch2.s.sol b/script/DeployPatch2.s.sol deleted file mode 100644 index 97466628a..000000000 --- a/script/DeployPatch2.s.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../src/StakingManager.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract DeployPatchV3 is Script { - using Strings for string; - - struct UpgradeAddresses { - address stakingManager; - } - - UpgradeAddresses upgradeAddressesStruct; - - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address stakingManagerProxyAddress = vm.envAddress("STAKING_MANAGER_PROXY_ADDRESS"); - - //mainnet - require(stakingManagerProxyAddress == 0x44F5759C47e052E5Cf6495ce236aB0601F1f98fF, "stakingManagerProxyAddress incorrect see .env"); - - vm.startBroadcast(deployerPrivateKey); - - StakingManager stakingManagerInstance = StakingManager(stakingManagerProxyAddress); - StakingManager stakingManagerV3Implementation = new StakingManager(); - - stakingManagerInstance.upgradeTo(address(stakingManagerV3Implementation)); - vm.stopBroadcast(); - - upgradeAddressesStruct = UpgradeAddresses({ - stakingManager: address(stakingManagerV3Implementation) - }); - } - - function _stringToUint( - string memory numString - ) internal pure returns (uint256) { - uint256 val = 0; - bytes memory stringBytes = bytes(numString); - for (uint256 i = 0; i < stringBytes.length; i++) { - uint256 exp = stringBytes.length - i; - bytes1 ival = stringBytes[i]; - uint8 uval = uint8(ival); - uint256 jval = uval - uint256(0x30); - - val += (uint256(jval) * (10 ** (exp - 1))); - } - return val; - } - - function writeUpgradeToFile() internal { - // Read Current version - string memory versionString = vm.readLine("release/logs/Upgrades/version.txt"); - - // Cast string to uint256 - uint256 version = _stringToUint(versionString); - - version++; - - // Overwrites the version.txt file with incremented version - vm.writeFile( - "release/logs/Upgrades/version.txt", - string(abi.encodePacked(Strings.toString(version))) - ); - - // Writes the data to .release file - vm.writeFile( - string( - abi.encodePacked( - "release/logs/Upgrades/", - Strings.toString(version), - ".release" - ) - ), - string( - abi.encodePacked( - Strings.toString(version), - "\nNew Staking Manager Implementation: ", - Strings.toHexString(upgradeAddressesStruct.stakingManager) - ) - ) - ); - } -} diff --git a/script/Deploy_Instructions_Phase_One.md b/script/Deploy_Instructions_Phase_One.md deleted file mode 100644 index 1b639a4a6..000000000 --- a/script/Deploy_Instructions_Phase_One.md +++ /dev/null @@ -1,54 +0,0 @@ -# EtherFi Contract Phase One Deploy Instructions - -# Step 1: -## Setup Environment - -Once you have the environment on VS code, you will need to run the following commands to get everything working. -* curl -L https://foundry.paradigm.xyz | bash -* foundryup -* git submodule update --init --recursive - -Make sure you have xcode installed. You will need it to run the makefile. -* xcode-select -p -If not, install it with -* xcode-select --install - -Install the jq utility. -For windows use Chocolatey NuGet to install jq 1.5 with: -* chocolatey install jq -For Mac use Homebrew to install jq 1.6 with: -* brew install jq - -# Step 2: -## Deploy EtherFi Suite - -Deploy the EtherFi phase one suite. - -This consists of the Node Operator Manager, Auction Manager, Staking Manager, EtherFi Nodes Manager, Protocol Revenue Manager, EtherFi Node, Treasury, TNFT, BNFT and Score Manager contracts. The deploy script will set all dependencies automatically. - -There are a few important variables to set before running the deploy command. - -If you currently do not have a .env file, and only a .example.env, perform the following: -1. Copy the .example.env file and create a new file with the same contents called .env (this name will hide it from public sources) -2. The file will consist of the following: - - * GOERLI_RPC_URL= - * PRIVATE_KEY= - * ETHERSCAN_API_KEY= - -3. Please fill in the data accordingly. You can find a GOERLI_RPC_URL or MAINNET_RPC_URL in the case of mainnet deployment, on Alchemy. The private key used here will be the multisig wallet you wish to use. And lastly you can retrieve a ETHERSCAN_API_KEY from etherscan if you sign up. - -4. Once your environment is set up, run - source .env - -5. Lastly, run the following command to deploy - ```make deploy-phase-1``` - -# Step 3 -## Set Merkle Root - -Once all contracts have been deployed and dependencies set up, we will need to update the merkle roots. - -1. Generate the merkle tree for the Node Operators and call the updateMerkleRoot function in the Node Operator Manager to set the root. -2. Generate the merkle tree for stakers who are whitelisted and call the updateMerkleRoot function in the Staking Manager to set the root. - diff --git a/script/EigenlayerSlashingUpgrade.s.sol b/script/EigenlayerSlashingUpgrade.s.sol deleted file mode 100644 index 743c9dab0..000000000 --- a/script/EigenlayerSlashingUpgrade.s.sol +++ /dev/null @@ -1,116 +0,0 @@ -// // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "test/TestSetup.sol"; - -import "../src/EtherFiNode.sol"; -import "../src/EtherFiNodesManager.sol"; -import "../src/EtherFiRestaker.sol"; -import "../src/StakingManager.sol"; -import "../src/helpers/AddressProvider.sol"; -import "../script/GnosisHelpers.sol"; - -contract Upgrade is Script, GnosisHelpers { - - address public etherFiTimelock = 0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761; - address public addressProviderAddress = 0x8487c5F8550E3C3e7734Fe7DCF77DB2B72E4A848; - AddressProvider public addressProvider = AddressProvider(addressProviderAddress); - address public roleRegistry = 0x1d3Af47C1607A2EF33033693A9989D1d1013BB50; - address public treasury = 0x0c83EAe1FE72c390A02E426572854931EefF93BA; - address public pauser = 0x9AF1298993DC1f397973C62A5D47a284CF76844D; - - address public etherFiNodeImplementation; - address public etherFiNodesManagerImplementation; - address public etherFiRestakerImplementation; - - EtherFiNodesManager public etherFiNodesManagerInstance; - EtherFiRestaker public etherFiRestakerInstance; - StakingManager public stakingManagerInstance; - - function run() external { - - /* - // Tenderly values - etherFiNodeImplementation = address(0xC1dD9Fd7DD43Bbde426A74AAca1Ed208aAD9d9e1); - etherFiNodesManagerImplementation = address(0x98Fe79a199624c4a2280001303C8356fA3e4B0B9); - etherFiRestakerImplementation = address(0x58e97Ce26b29F3B490A137bE6ABB81b08790B107); - */ - - // new implementation addresses - etherFiNodeImplementation = address(0xc5F2764383f93259Fba1D820b894B1DE0d47937e); - etherFiNodesManagerImplementation = address(0xE9EE6923D41Cf5F964F11065436BD90D4577B5e4); - etherFiRestakerImplementation = address(0x0052F731a6BEA541843385ffBA408F52B74Cb624); - require(etherFiNodeImplementation != address(0x0), "invalid node implementation"); - require(etherFiNodesManagerImplementation != address(0x0), "invalid node implementation"); - require(etherFiRestakerImplementation != address(0x0), "invalid node implementation"); - - // proxy addresses - stakingManagerInstance = StakingManager(payable(addressProvider.getContractAddress("StakingManager"))); - etherFiNodesManagerInstance = EtherFiNodesManager(payable(addressProvider.getContractAddress("EtherFiNodesManager"))); - etherFiRestakerInstance = EtherFiRestaker(payable(address(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf))); // EtherFiRestaker is missing from addressProvideEtherfaddressr - require(address(stakingManagerInstance) != address(0x0), "failed to lookup stakingManagerInstance"); - require(address(etherFiNodesManagerInstance) != address(0x0), "failed to lookup etherFiNodeInstance"); - require(address(etherFiRestakerInstance) != address(0x0), "failed to lookup etherFiRestakerInstance"); - - vm.startBroadcast(); - - deploy_upgrade(); - - vm.stopBroadcast(); - } - - function deploy_upgrade() internal { - predecessor = 0x0000000000000000000000000000000000000000000000000000000000000000; - salt = keccak256("EigenlayerSlashingUpgrade-4_13"); - delay = 3 days; - - // entry point for each timelock subcall - address[] memory targets = new address[](3); - targets[0] = address(stakingManagerInstance); // staking manager is the beacon proxy owner for etherfiNode - targets[1] = address(etherFiNodesManagerInstance); - targets[2] = address(etherFiRestakerInstance); - - // eth to send (tx.value) in each timelock subcall - uint256[] memory values = new uint256[](3); - - // calldata for each timelock subcall - bytes[] memory payloads = new bytes[](3); - bytes memory upgradeEtherFiNodeCalldata = abi.encodeWithSignature( - "upgradeEtherFiNode(address)", - etherFiNodeImplementation - ); - bytes memory upgradeEtherFiNodesManagerCalldata = abi.encodeWithSignature( - "upgradeToAndCall(address,bytes)", - etherFiNodesManagerImplementation, - "" - ); - bytes memory upgradeEtherFiRestakerCalldata = abi.encodeWithSignature( - "upgradeToAndCall(address,bytes)", - etherFiRestakerImplementation, - "" - ); - - payloads[0] = upgradeEtherFiNodeCalldata; - payloads[1] = upgradeEtherFiNodesManagerCalldata; - payloads[2] = upgradeEtherFiRestakerCalldata; - - string memory scheduleGnosisTx = _getGnosisHeader("1"); - string memory scheduleUpgrade = iToHex(abi.encodeWithSignature("scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256)", targets, values, payloads, predecessor, salt, delay)); - scheduleGnosisTx = string(abi.encodePacked(scheduleGnosisTx, _getGnosisTransaction(addressToHex(timelock), scheduleUpgrade, true))); - - string memory path = "./operations/20250413_upgrade_eigenlayer_slashing_schedule.json"; - vm.writeFile(path, scheduleGnosisTx); - - string memory executeGnosisTx = _getGnosisHeader("1"); - string memory executeUpgrade = iToHex(abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[],bytes32,bytes32)", targets, values, payloads, predecessor, salt)); - executeGnosisTx = string(abi.encodePacked(executeGnosisTx, _getGnosisTransaction(addressToHex(timelock), executeUpgrade, true))); - - path = "./operations/20250413_upgrade_eigenlayer_slashing_execute.json"; - vm.writeFile(path, executeGnosisTx); - - console2.log("timestamp: ", block.timestamp); - } - - -} diff --git a/script/GnosisHelpers.sol b/script/GnosisHelpers.sol deleted file mode 100644 index 76ea6f3e7..000000000 --- a/script/GnosisHelpers.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; - -import "@openzeppelin/contracts/utils/Strings.sol"; - - -contract GnosisHelpers is Test { - - /** - * @dev Simulations the execution of a gnosis transaction bundle on the current fork - * @param transactionPath The path to the transaction bundle json file - * @param sender The address of the gnosis safe that will execute the transaction - */ - function executeGnosisTransactionBundle(string memory transactionPath, address sender) public { - string memory json = vm.readFile(transactionPath); - for (uint256 i = 0; vm.keyExistsJson(json, string.concat(".transactions[", Strings.toString(i), "]")); i++) { - address to = vm.parseJsonAddress(json, string.concat(string.concat(".transactions[", Strings.toString(i)), "].to")); - uint256 value = vm.parseJsonUint(json, string.concat(string.concat(".transactions[", Strings.toString(i)), "].value")); - bytes memory data = vm.parseJsonBytes(json, string.concat(string.concat(".transactions[", Strings.toString(i)), "].data")); - - vm.prank(sender); - (bool success,) = address(to).call{value: value}(data); - require(success, "Transaction failed"); - } - } - - // Get the gnosis transaction header - function _getGnosisHeader(string memory chainId) internal pure returns (string memory) { - return string.concat('{"chainId":"', chainId, '","meta": { "txBuilderVersion": "1.16.5" }, "transactions": ['); - } - - // Create a gnosis transaction - // ether sent value is always 0 for our usecase - function _getGnosisTransaction(string memory to, string memory data, bool isLast) internal pure returns (string memory) { - string memory suffix = isLast ? ']}' : ','; - return string.concat('{"to":"', to, '","value":"0","data":"', data, '"}', suffix); - } - - // Helper function to convert bytes to hex strings - // soldity encodes returns a bytes object and this must be converted to a hex string to be used in gnosis transactions - function iToHex(bytes memory buffer) public pure returns (string memory) { - // Fixed buffer size for hexadecimal convertion - bytes memory converted = new bytes(buffer.length * 2); - - bytes memory _base = "0123456789abcdef"; - - for (uint256 i = 0; i < buffer.length; i++) { - converted[i * 2] = _base[uint8(buffer[i]) / _base.length]; - converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length]; - } - - return string(abi.encodePacked("0x", converted)); - } - - // Helper function to convert an address to a hex string of the bytes - function addressToHex(address addr) public pure returns (string memory) { - return iToHex(abi.encodePacked(addr)); - } - - address public timelock = 0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761; - bytes32 public predecessor = 0x0000000000000000000000000000000000000000000000000000000000000000; - bytes32 public salt = 0x0000000000000000000000000000000000000000000000000000000000000000; - uint256 public delay = 259200; - - // Generates the schedule transaction for a gnosis safe - function _getGnosisScheduleTransaction(address to, bytes memory data, bool isLasts) internal view returns (string memory) { - - string memory timelockAddressHex = iToHex(abi.encodePacked(address(timelock))); - string memory scheduleTransactionData = iToHex(abi.encodeWithSignature("schedule(address,uint256,bytes,bytes32,bytes32,uint256)", to, 0, data, predecessor, salt, delay)); - - return _getGnosisTransaction(timelockAddressHex, scheduleTransactionData, isLasts); - } - - function _getGnosisExecuteTransaction(address to, bytes memory data, bool isLasts) internal view returns (string memory) { - - string memory timelockAddressHex = iToHex(abi.encodePacked(address(timelock))); - string memory executeTransactionData = iToHex(abi.encodeWithSignature("execute(address,uint256,bytes,bytes32,bytes32)", to, 0, data, predecessor, salt)); - - return _getGnosisTransaction(timelockAddressHex, executeTransactionData, isLasts); - } - -} \ No newline at end of file diff --git a/script/Merkletree.js b/script/Merkletree.js deleted file mode 100644 index 0ee0ea461..000000000 --- a/script/Merkletree.js +++ /dev/null @@ -1,53 +0,0 @@ -const { MerkleTree } = require("merkletreejs"); - const keccak256 = require("keccak256"); - const fs = require('fs'); - const { ethers } = require("hardhat"); - - let walletAddresses = [ - "0x1c5fffDbFDE331A10Ab1e32da8c4Dff210B43145", - "0x2f2806e8b288428f23707A69faA60f52BC565c17", - "0x5dfb8BC4830ccF60d469D546aEC36531c97B96b5", - "0x4507cfB4B077d5DBdDd520c701E30173d5b59Fad", - "0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931", - "0x7631FCf7D45D821cB5FA688fADa7bbc76714B771", - ] - - let leafNodes = walletAddresses.map(addr => keccak256(addr)); - let merkletree = new MerkleTree(leafNodes, keccak256, {sortPairs: true}); - let merkleRoot = merkletree.getRoot(); - - let buyerOne = leafNodes[0]; - let buyerTwo = leafNodes[1]; - let buyerThree = leafNodes[2]; - let buyerFour = leafNodes[3]; - let buyerFive = leafNodes[4]; - let buyerSix = leafNodes[5]; - - let buyerOneMerkleProof = merkletree.getHexProof(buyerOne); - let buyerTwoMerkleProof = merkletree.getHexProof(buyerTwo); - let buyerThreeMerkleProof = merkletree.getHexProof(buyerThree); - let buyerFourMerkleProof = merkletree.getHexProof(buyerFour); - let buyerFiveMerkleProof = merkletree.getHexProof(buyerFive); - let buyerSixMerkleProof = merkletree.getHexProof(buyerSix); - - merkleRoot = merkleRoot.toString("hex"); - console.log(merkleRoot); - console.log(buyerOneMerkleProof); - console.log(buyerTwoMerkleProof); - console.log(buyerThreeMerkleProof); - console.log(buyerFourMerkleProof); - console.log(buyerFiveMerkleProof); - console.log(buyerSixMerkleProof); - - module.exports = { - walletAddresses, - leafNodes, - merkletree, - merkleRoot, - buyerOneMerkleProof, - buyerTwoMerkleProof, - buyerThreeMerkleProof, - buyerFourMerkleProof, - buyerFiveMerkleProof, - buyerSixMerkleProof, - } \ No newline at end of file diff --git a/script/README.md b/script/README.md deleted file mode 100644 index 73418bf39..000000000 --- a/script/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# EtherFi Merkle Explanation - -## Merkle Explanation - -Merkle trees allow systems to have whitelisted addresses, meaning that certain functionality can be limited to certain whitelisted addresses. Merkle trees need to be generated and a proof needs to be submitted while calling the specific limited function. The merkle works by creating a tree like structure of hashed addresses, each hash combining to ultimately form a final hash. This is called the root and needs to be submitted to the contract for verification purposes. - -## Merkle Script Guide - -The merkle script uses the Murky library to generate the merkle root and proofs. We import the library in the beginning. There are 6 steps which need to be taken to generate a new merkle and update the contract, here is a step-by-step guide in using the merkle script (scripts/Merkle.s.sol) to generate the new root and proofs: - -### Step 1 - -The following line generates the data structure which holds the whitelisted addresses: - -```zsh - bytes32[] memory data = new bytes32[](6); -``` - -At the end of the line is a number inside ( ). This number should be updated to how many addresses will be whitelisted. In the example above, there will be 6 whitelisted addresses. - -### Step 2 - -The lines which follow perform the functionality of actually adding the whitelisted addresses to the data structure declared above. We need to add all the wanted whitelisted addresses to the data structure. You will see in the Merkle.s.sol that adding an address looks as follows: - -```zsh - data[0] = bytes32( - keccak256( - abi.encodePacked(0x1c5fffDbFDE331A10Ab1e33da8c4Dff210B43145) - ) - ); -``` - -To break down the above code, there are 2 elements: - -```zsh - data[0] -``` - -The above code refers to the data structures name being used and the position in the data structure to store the address. Because it is an array, the indexing always starts at 0. Therefore, the first address will be stored at data[0]. - -The next part is where to fill in the address you are wishing to add. In the brackets which follow abi.encodePacked(), insert the public key of the account you want to add. In this case, 0x1c5fffDbFDE331A10Ab1e33da8c4Dff210B43145 is used. - -You will need to repeat this for as many addresses you would like to add. Therefore, if you would like to add 3 addresses to the data structure, it would look similar to this: - -```zsh - data[0] = bytes32( - keccak256( - abi.encodePacked(0x1c5fffDbFDE331A20Ab1e32da8c4Dff210B43145) - ) - ); - - data[1] = bytes32( - keccak256( - abi.encodePacked(0x2f2806e8b288428f24707A69faA60f52BC565c17) - ) - ); - - data[2] = bytes32( - keccak256( - abi.encodePacked(0x5dfb8BC4830ccF60d469D646aEC36531c97B96b5) - ) - ); -``` - -### Step 3 - -Once the addresses have been added to the data structure, the root can be generated. This gets performed through the following lines: - -```zsh - bytes32 root = merkle.getRoot(data); -``` - -This generates the root which will be used in the contracts for verification purposes. You will notice, further in the script, the root is logged to the terminal to allow you to fetch it and call the updateMerkleroot function on the contracts with this new root. You will need to call the function on the contracts on etherscan and pass the logged root as the parameter, this will update the contract. - -### Step 4 - -The second part of a merkle is the proof a specific address requires to perform the functions. Each addresses is generated a different proof which can be used to verify you are indeed on the whitelist when calling a limited function. The following line is an example of how the proof is generated: - -```zsh - bytes32[] memory proofOne = merkle.getProof(data, 0); -``` - -The getProof function generates a proof for a specific address in the data structure, however, you need to pass it the name of the data structure as well as the index of the address you want to generate a proof for. So, in the above code, we are generating a proof for the address in position 0 of the data structured called 'data'. To follow the example above, if you wanted to generate proofs for all three addresses, it would look like this: - -```zsh - bytes32[] memory proofOne = merkle.getProof(data, 0); - bytes32[] memory proofTwo = merkle.getProof(data, 1); - bytes32[] memory proofThree = merkle.getProof(data, 2); -``` - -If you have added more addresses to the data structure, you will need to generate more proofs. - -### Step 5 - -The next part of the script, is the logging of the proofs. An example of logging an addresses proof is as follows: - -```zsh - console.log("Merkle proof for address three"); - console.log(""); - - for (uint256 x = 0; x < proofThree.length; x++) { - console.logBytes32(proofThree[x]); - } -``` - -You will need as many of these for as many of addresses you have added. This is important to ensure that you can access the proofs in the terminal, to ensure you have them to use in the needed functions. The result of the console.log will look similar to this: - -```zsh - Merkle proof for address three - - 0x493a4cd18172510ab22441f3f81348a7f52ba4c0d02d50bb05e703d88fd3999c - 0xd6e464cc412334ceda2d5f051d11721e429e8f3867ed8ab32cbc7ade1b5fc5d1 - 0xe49c0d75d3dd8e3b6c0106e828c6012a5f3b0e3b92c1ed11584f630f4dfa1fee -``` - -The way you would use this proof in the actual function call would be as follows: - -```zsh -[0x493a4cd18172510ab22441f3f81348a7f52ba4c0d02d50bb05e703d88fd3999c,0xd6e464cc412334ceda2d5f051d11721e429e8f3867ed8ab32cbc7ade1b5fc5d1,0xe49c0d75d3dd8e3b6c0106e828c6012a5f3b0e3b92c1ed11584f630f4dfa1fee] -``` - -Please note that based on the number of addresses you are whitelisting, the log of the proof could incorporate more or less than what is seen above. You will need to save your merkle proof for when you need to call a function on Etherscan. - -### Step 6 - -Once you have added all your addresses to your data structure and updated the number of console.logs needed, you can run the script to generate the merkle root and proofs, which will be logged to the terminal for you to use accordingly. The command to run the script is as follows, please paste it in the terminal in your IDE. - -```zsh - forge script script/Merkle.s.sol:MerkleScript -``` \ No newline at end of file diff --git a/script/addressConfig.json b/script/addressConfig.json deleted file mode 100644 index 2c37d920b..000000000 --- a/script/addressConfig.json +++ /dev/null @@ -1,224 +0,0 @@ -{ - "AddressProvider": [], - "RegulationsManager": [], - "LiquidityPool": [ - { - "methodName": "bNftTreasury", - "value": "BNFT", - "isReference": true - }, - { - "methodName": "eETH", - "value": "EETH", - "isReference": true - }, - { - "methodName": "membershipManager", - "value": "MembershipManager", - "isReference": true - }, - { - "methodName": "nodesManager", - "value": "EtherFiNodesManager", - "isReference": true - }, - { - "methodName": "regulationsManager", - "value": "RegulationsManager", - "isReference": true - }, - { - "methodName": "stakingManager", - "value": "StakingManager", - "isReference": true - }, - { - "methodName": "tNft", - "value": "TNFT", - "isReference": true - } - ], - "EETH": [ - { - "methodName": "liquidityPool", - "value": "LiquidityPool", - "isReference": true - } - ], - "WeETH": [ - { - "methodName": "eETH", - "value": "EETH", - "isReference": true - }, - { - "methodName": "liquidityPool", - "value": "LiquidityPool", - "isReference": true - } - ], - "MembershipManager": [ - { - "methodName": "eETH", - "value": "EETH", - "isReference": true - }, - { - "methodName": "liquidityPool", - "value": "LiquidityPool", - "isReference": true - }, - { - "methodName": "membershipNFT", - "value": "MembershipNFT", - "isReference": true - }, - { - "methodName": "protocolRevenueManager", - "value": "ProtocolRevenueManager", - "isReference": true - }, - { - "methodName": "treasury", - "value": "Treasury", - "isReference": true - } - ], - "MembershipNFT": [], - "NFTExchange": [ - { - "methodName": "membershipNft", - "value": "MembershipNFT", - "isReference": true - }, - { - "methodName": "tNft", - "value": "TNFT", - "isReference": true - } - ], - "AuctionManager": [ - { - "methodName": "nodeOperatorManager", - "value": "NodeOperatorManager", - "isReference": true - }, - { - "methodName": "protocolRevenueManager", - "value": "ProtocolRevenueManager", - "isReference": true - }, - { - "methodName": "stakingManagerContractAddress", - "value": "StakingManager", - "isReference": true - } - ], - "StakingManager": [ - { - "methodName": "BNFTInterfaceInstance", - "value": "BNFT", - "isReference": true - }, - { - "methodName": "TNFTInterfaceInstance", - "value": "TNFT", - "isReference": true - }, - { - "methodName": "auctionManager", - "value": "AuctionManager", - "isReference": true - }, - { - "methodName": "liquidityPoolContract", - "value": "LiquidityPool", - "isReference": true - }, - { - "methodName": "nodesManager", - "value": "EtherFiNodesManager", - "isReference": true - } - ], - "EtherFiNodesManager": [ - { - "methodName": "auctionManager", - "value": "AuctionManager", - "isReference": true - }, - { - "methodName": "bnft", - "value": "BNFT", - "isReference": true - }, - { - "methodName": "protocolRevenueManager", - "value": "ProtocolRevenueManager", - "isReference": true - }, - { - "methodName": "protocolRevenueManagerContract", - "value": "ProtocolRevenueManager", - "isReference": true - }, - { - "methodName": "stakingManagerContract", - "value": "StakingManager", - "isReference": true - }, - { - "methodName": "tnft", - "value": "TNFT", - "isReference": true - }, - { - "methodName": "treasuryContract", - "value": "Treasury", - "isReference": true - } - ], - "ProtocolRevenueManager": [ - { - "methodName": "auctionManager", - "value": "AuctionManager", - "isReference": true - }, - { - "methodName": "etherFiNodesManager", - "value": "EtherFiNodesManager", - "isReference": true - } - ], - "TNFT": [ - { - "methodName": "stakingManagerAddress", - "value": "StakingManager", - "isReference": true - } - ], - "BNFT": [ - { - "methodName": "stakingManagerAddress", - "value": "StakingManager", - "isReference": true - } - ], - "Treasury": [], - "NodeOperatorManager": [ - { - "methodName": "auctionManagerContractAddress", - "value": "AuctionManager", - "isReference": true - } - ], - "EtherFiNode": [ - { - "methodName": "etherFiNodesManager", - "value": "EtherFiNodesManager", - "isReference": true - } - ], - "EarlyAdopterPool": [], - "LoyaltyPointsMarketSafe": [] -} \ No newline at end of file diff --git a/script/contracts.json b/script/contracts.json deleted file mode 100644 index 8718867ba..000000000 --- a/script/contracts.json +++ /dev/null @@ -1 +0,0 @@ -["AddressProvider","RegulationsManager","LiquidityPool","EETH","WeETH","MembershipManager","MembershipNFT","NFTExchange","AuctionManager","StakingManager","EtherFiNodesManager","ProtocolRevenueManager","TNFT","BNFT","Treasury","NodeOperatorManager","EtherFiNode","EarlyAdopterPool","LoyaltyPointsMarketSafe"] \ No newline at end of file diff --git a/script/deploys/BucketRateLimiter.s.sol b/script/deploys/BucketRateLimiter.s.sol deleted file mode 100644 index 60a2046d3..000000000 --- a/script/deploys/BucketRateLimiter.s.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "../../src/BucketRateLimiter.sol"; -import "../../src/UUPSProxy.sol"; - -interface IL2SyncPool { - function setRateLimiter(address rateLimiter) external; -} - -contract Deploy is Script { - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address deployer = vm.addr(deployerPrivateKey); - address l2syncpool; - - if (block.chainid == 59144) l2syncpool = 0x823106E745A62D0C2FC4d27644c62aDE946D9CCa; // LINEA - else if (block.chainid == 81457) l2syncpool = 0x52c4221Cb805479954CDE5accfF8C4DcaF96623B; // BLAST - else if (block.chainid == 34443) l2syncpool = 0x52c4221Cb805479954CDE5accfF8C4DcaF96623B; // MODE - else if (block.chainid == 8453) l2syncpool = 0xc38e046dFDAdf15f7F56853674242888301208a5; // BASE - else revert("Unsupported chain id"); - - vm.startBroadcast(deployerPrivateKey); - - BucketRateLimiter impl = new BucketRateLimiter(); - UUPSProxy proxy = new UUPSProxy(address(impl), ""); - BucketRateLimiter limiter = BucketRateLimiter(address(proxy)); - limiter.initialize(); - - limiter.updateConsumer(l2syncpool); - IL2SyncPool(l2syncpool).setRateLimiter(address(limiter)); - - vm.stopBroadcast(); - - // TEST - - vm.startPrank(deployer); - limiter.setCapacity(0.0002 ether); - limiter.setRefillRatePerSecond(0.0002 ether); - vm.stopPrank(); - - vm.prank(l2syncpool); - vm.expectRevert("BucketRateLimiter: rate limit exceeded"); - limiter.updateRateLimit(address(0), address(0), 0.0001 ether, 0.0001 ether); - - vm.prank(l2syncpool); - vm.warp(block.timestamp + 1); - limiter.updateRateLimit(address(0), address(0), 0.0001 ether, 0.0001 ether); - } - -} \ No newline at end of file diff --git a/script/deploys/DeployAndPopulateAddressProviderScript.s.sol b/script/deploys/DeployAndPopulateAddressProviderScript.s.sol deleted file mode 100644 index efa0c42e4..000000000 --- a/script/deploys/DeployAndPopulateAddressProviderScript.s.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract DeployAndPopulateAddressProvider is Script { - - /*---- Storage variables ----*/ - - struct PhaseOneAddresses { - address auctionManagerAddress; - address stakingManagerAddress; - address etherFiNodesManagerAddress; - address protocolRevenueManager; - address tnft; - address bnft; - address treasury; - address nodeOperatorManager; - address etherFiNode; - address earlyAdopterPool; - } - - struct PhaseOnePointFiveAddress { - address eETH; - address liquidityPool; - address membershipManager; - address membershipNFT; - address nftExchange; - address regulationsManager; - address weETH; - } - - AddressProvider public addressProvider; - PhaseOneAddresses public phaseOneAddresses; - PhaseOnePointFiveAddress public phaseOnePointFiveAddress; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address owner = vm.envAddress("DEPLOYER"); - vm.startBroadcast(deployerPrivateKey); - - addressProvider = new AddressProvider(owner); - console.log(address(addressProvider)); - - /*---- Populate Registry ----*/ - - phaseOneAddresses.auctionManagerAddress = vm.envAddress("AUCTION_MANAGER_PROXY_ADDRESS"); - phaseOneAddresses.stakingManagerAddress = vm.envAddress("STAKING_MANAGER_PROXY_ADDRESS"); - phaseOneAddresses.etherFiNodesManagerAddress = vm.envAddress("ETHERFI_NODES_MANAGER_PROXY_ADDRESS"); - phaseOneAddresses.protocolRevenueManager = vm.envAddress("PROTOCOL_REVENUE_MANAGER_PROXY_ADDRESS"); - phaseOneAddresses.tnft = vm.envAddress("TNFT_PROXY_ADDRESS"); - phaseOneAddresses.bnft = vm.envAddress("BNFT_PROXY_ADDRESS"); - phaseOneAddresses.treasury = vm.envAddress("TREASURY_ADDRESS"); - phaseOneAddresses.nodeOperatorManager = vm.envAddress("NODE_OPERATOR_MANAGER_ADDRESS"); - phaseOneAddresses.etherFiNode = vm.envAddress("ETHERFI_NODE"); - phaseOneAddresses.earlyAdopterPool = vm.envAddress("EARLY_ADOPTER_POOL"); - phaseOnePointFiveAddress.eETH = vm.envAddress("EETH_PROXY_ADDRESS"); - phaseOnePointFiveAddress.liquidityPool = vm.envAddress("LIQUIDITY_POOL_PROXY_ADDRESS"); - phaseOnePointFiveAddress.membershipManager = vm.envAddress("MEMBERSHIP_MANAGER_PROXY_ADDRESS"); - phaseOnePointFiveAddress.membershipNFT = vm.envAddress("MEMBERSHIP_NFT_PROXY_ADDRESS"); - phaseOnePointFiveAddress.nftExchange = vm.envAddress("NFT_EXCHANGE"); - phaseOnePointFiveAddress.regulationsManager = vm.envAddress("REGULATIONS_MANAGER_PROXY_ADDRESS"); - phaseOnePointFiveAddress.weETH = vm.envAddress("WEETH_PROXY_ADDRESS"); - - addressProvider.addContract(phaseOneAddresses.auctionManagerAddress, "AuctionManager"); - addressProvider.addContract(phaseOneAddresses.stakingManagerAddress, "StakingManager"); - addressProvider.addContract(phaseOneAddresses.etherFiNodesManagerAddress, "EtherFiNodesManager"); - addressProvider.addContract(phaseOneAddresses.protocolRevenueManager, "ProtocolRevenueManager"); - addressProvider.addContract(phaseOneAddresses.tnft, "TNFT"); - addressProvider.addContract(phaseOneAddresses.bnft, "BNFT"); - addressProvider.addContract(phaseOneAddresses.treasury, "Treasury"); - addressProvider.addContract(phaseOneAddresses.nodeOperatorManager, "NodeOperatorManager"); - addressProvider.addContract(phaseOneAddresses.etherFiNode, "EtherFiNode"); - addressProvider.addContract(phaseOneAddresses.earlyAdopterPool, "EarlyAdopterPool"); - addressProvider.addContract(phaseOnePointFiveAddress.eETH, "EETH"); - addressProvider.addContract(phaseOnePointFiveAddress.liquidityPool, "LiquidityPool"); - addressProvider.addContract(phaseOnePointFiveAddress.membershipManager, "MembershipManager"); - addressProvider.addContract(phaseOnePointFiveAddress.membershipNFT, "MembershipNFT"); - addressProvider.addContract(phaseOnePointFiveAddress.nftExchange, "NFTExchange"); - addressProvider.addContract(phaseOnePointFiveAddress.regulationsManager, "RegulationsManager"); - addressProvider.addContract(phaseOnePointFiveAddress.weETH, "WeETH"); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/deploys/DeployBucketLimiter.s.sol b/script/deploys/DeployBucketLimiter.s.sol deleted file mode 100644 index 54d1b7490..000000000 --- a/script/deploys/DeployBucketLimiter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "src/BucketRateLimiter.sol"; - -contract Deploy is Script { - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - BucketRateLimiter limiter = new BucketRateLimiter(); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployEarlyAdopterPool.s.sol b/script/deploys/DeployEarlyAdopterPool.s.sol deleted file mode 100644 index 735af5d87..000000000 --- a/script/deploys/DeployEarlyAdopterPool.s.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; -import "../../src/EarlyAdopterPool.sol"; -import "../../test/TestERC20.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract DeployEarlyAdopterPoolScript is Script { - using Strings for string; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - - EarlyAdopterPool earlyAdopterPool = new EarlyAdopterPool( - 0xae78736Cd615f374D3085123A210448E74Fc6393, - 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0, - 0xac3E018457B222d93114458476f3E3416Abbe38F, - 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704 - ); - - vm.stopBroadcast(); - - // Sets the variables to be written to contract addresses.txt - string memory earlyAdopterPoolAddress = Strings.toHexString( - address(earlyAdopterPool) - ); - - // Declare version Var - uint256 version; - - // Set path to version file where current version is recorded - /// @dev Initial version.txt and X.release files should be created manually - string memory versionPath = "release/logs/earlyAdopterPool/version.txt"; - - // Read Current version - string memory versionString = vm.readLine(versionPath); - - // Cast string to uint256 - version = _stringToUint(versionString); - - version++; - - // Declares the incremented version to be written to version.txt file - string memory versionData = string( - abi.encodePacked(Strings.toString(version)) - ); - - // Overwrites the version.txt file with incremented version - vm.writeFile(versionPath, versionData); - - // Sets the path for the release file using the incremented version var - string memory releasePath = string( - abi.encodePacked( - "release/logs/earlyAdopterPool/", - Strings.toString(version), - ".release" - ) - ); - - // Concatenates data to be written to X.release file - string memory writeData = string( - abi.encodePacked( - "Version: ", - Strings.toString(version), - "\n", - "Early Adopter Pool Contract Address: ", - earlyAdopterPoolAddress - ) - ); - - // Writes the data to .release file - vm.writeFile(releasePath, writeData); - } - - function _stringToUint(string memory numString) - internal - pure - returns (uint256) - { - uint256 val = 0; - bytes memory stringBytes = bytes(numString); - for (uint256 i = 0; i < stringBytes.length; i++) { - uint256 exp = stringBytes.length - i; - bytes1 ival = stringBytes[i]; - uint8 uval = uint8(ival); - uint256 jval = uval - uint256(0x30); - - val += (uint256(jval) * (10**(exp - 1))); - } - return val; - } -} diff --git a/script/deploys/DeployEtherFISuite.s.sol b/script/deploys/DeployEtherFISuite.s.sol deleted file mode 100644 index 644138f30..000000000 --- a/script/deploys/DeployEtherFISuite.s.sol +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/Treasury.sol"; -import "../../src/NodeOperatorManager.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/archive/ProtocolRevenueManager.sol"; -import "../../src/StakingManager.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/EETH.sol"; -import "../../src/WeETH.sol"; -import "../../src/archive/RegulationsManager.sol"; -import "../../src/UUPSProxy.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -import "../../test/TestERC20.sol"; - -contract DeployEtherFiSuiteScript is Script { - using Strings for string; - - bytes32 initialHash = vm.envBytes32("INITIAL_HASH"); - - - /*---- Storage variables ----*/ - - TestERC20 public rETH; - TestERC20 public wstETH; - TestERC20 public sfrxEth; - TestERC20 public cbEth; - - UUPSProxy public auctionManagerProxy; - UUPSProxy public stakingManagerProxy; - UUPSProxy public etherFiNodeManagerProxy; - UUPSProxy public protocolRevenueManagerProxy; - UUPSProxy public TNFTProxy; - UUPSProxy public BNFTProxy; - UUPSProxy public liquidityPoolProxy; - UUPSProxy public eETHProxy; - UUPSProxy public claimReceiverPoolProxy; - UUPSProxy public regulationsManagerProxy; - UUPSProxy public weETHProxy; - - BNFT public BNFTImplementation; - BNFT public BNFTInstance; - - TNFT public TNFTImplementation; - TNFT public TNFTInstance; - - WeETH public weEthImplementation; - WeETH public weEthInstance; - - AuctionManager public auctionManagerImplementation; - AuctionManager public auctionManager; - - StakingManager public stakingManagerImplementation; - StakingManager public stakingManager; - - ProtocolRevenueManager public protocolRevenueManagerImplementation; - ProtocolRevenueManager public protocolRevenueManager; - - EtherFiNodesManager public etherFiNodesManagerImplementation; - EtherFiNodesManager public etherFiNodesManager; - - LiquidityPool public liquidityPoolImplementation; - LiquidityPool public liquidityPool; - - EETH public eETHImplementation; - EETH public eETHInstance; - - RegulationsManager public regulationsManagerInstance; - RegulationsManager public regulationsManagerImplementation; - - struct suiteAddresses { - address treasury; - address nodeOperatorManager; - address auctionManager; - address stakingManager; - address TNFT; - address BNFT; - address etherFiNodesManager; - address protocolRevenueManager; - address etherFiNode; - address regulationsManager; - address liquidityPool; - address eETH; - address weEth; - } - - suiteAddresses suiteAddressesStruct; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address ethDepositContractAddress; - if (block.chainid == 5) { - // goerli - ethDepositContractAddress = 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b; - } else if (block.chainid == 1) { - ethDepositContractAddress = 0x00000000219ab540356cBB839Cbe05303d7705Fa; - } else { - assert(false); - } - - vm.startBroadcast(deployerPrivateKey); - - // Deploy contracts - Treasury treasury = new Treasury(); - NodeOperatorManager nodeOperatorManager = new NodeOperatorManager(); - - auctionManagerImplementation = new AuctionManager(); - auctionManagerProxy = new UUPSProxy(address(auctionManagerImplementation),""); - auctionManager = AuctionManager(address(auctionManagerProxy)); - auctionManager.initialize(address(nodeOperatorManager)); - - stakingManagerImplementation = new StakingManager(); - stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation),""); - stakingManager = StakingManager(address(stakingManagerProxy)); - //stakingManager.initialize(address(auctionManager), ethDepositContractAddress); - - BNFTImplementation = new BNFT(); - BNFTProxy = new UUPSProxy(address(BNFTImplementation),""); - BNFTInstance = BNFT(address(BNFTProxy)); - BNFTInstance.initialize(address(stakingManager)); - - TNFTImplementation = new TNFT(); - TNFTProxy = new UUPSProxy(address(TNFTImplementation),""); - TNFTInstance = TNFT(address(TNFTProxy)); - TNFTInstance.initialize(address(stakingManager)); - - protocolRevenueManagerImplementation = new ProtocolRevenueManager(); - protocolRevenueManagerProxy = new UUPSProxy(address(protocolRevenueManagerImplementation),""); - protocolRevenueManager = ProtocolRevenueManager(payable(address(protocolRevenueManagerProxy))); - protocolRevenueManager.initialize(); - - etherFiNodesManagerImplementation = new EtherFiNodesManager(); - etherFiNodeManagerProxy = new UUPSProxy(address(etherFiNodesManagerImplementation),""); - etherFiNodesManager = EtherFiNodesManager(payable(address(etherFiNodeManagerProxy))); - /* - etherFiNodesManager.initialize( - address(treasury), - address(auctionManager), - address(stakingManager), - address(TNFTInstance), - address(BNFTInstance), - address(0), // TODO - address(0), - address(0) - ); - */ - - regulationsManagerImplementation = new RegulationsManager(); - regulationsManagerProxy = new UUPSProxy(address(regulationsManagerImplementation), ""); - regulationsManagerInstance = RegulationsManager(address(regulationsManagerProxy)); - regulationsManagerInstance.initialize(); - - // TODO - EtherFiNode etherFiNode = new EtherFiNode(address(0), address(0), address(0), address(0)); - - // Mainnet Addresses - // address private immutable rETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; - // address private immutable wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; - // address private immutable sfrxETH = 0xac3E018457B222d93114458476f3E3416Abbe38F; - // address private immutable cbETH = 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704; - rETH = new TestERC20("Rocket Pool ETH", "rETH"); - cbEth = new TestERC20("Staked ETH", "wstETH"); - wstETH = new TestERC20("Coinbase ETH", "cbEth"); - sfrxEth = new TestERC20("Frax ETH", "sfrxEth"); - - liquidityPoolImplementation = new LiquidityPool(); - liquidityPoolProxy = new UUPSProxy( - address(liquidityPoolImplementation), - "" - ); - liquidityPool = LiquidityPool( - payable(address(liquidityPoolProxy)) - ); - - eETHImplementation = new EETH(); - eETHProxy = new UUPSProxy(address(eETHImplementation), ""); - eETHInstance = EETH(address(eETHProxy)); - eETHInstance.initialize(payable(address(liquidityPool))); - - // Setup dependencies - nodeOperatorManager.setAuctionContractAddress(address(auctionManager)); - - auctionManager.setStakingManagerContractAddress(address(stakingManager)); - - protocolRevenueManager.setAuctionManagerAddress(address(auctionManager)); - protocolRevenueManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); - - /* - stakingManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); - stakingManager.setLiquidityPoolAddress(address(liquidityPool)); - stakingManager.registerEtherFiNodeImplementationContract(address(etherFiNode)); - stakingManager.registerTNFTContract(address(TNFTInstance)); - stakingManager.registerBNFTContract(address(BNFTInstance)); - */ - - liquidityPool.initialize(address(eETHInstance), address(stakingManager), address(etherFiNodesManager), address(0), address(0), address(0), address(0)); - - weEthImplementation = new WeETH(); - weETHProxy = new UUPSProxy(address(weEthImplementation), ""); - weEthInstance = WeETH(address(weETHProxy)); - weEthInstance.initialize(payable(address(liquidityPool)), address(eETHInstance)); - - regulationsManagerInstance.initializeNewWhitelist(initialHash); - - vm.stopBroadcast(); - - suiteAddressesStruct = suiteAddresses({ - treasury: address(treasury), - nodeOperatorManager: address(nodeOperatorManager), - auctionManager: address(auctionManager), - stakingManager: address(stakingManager), - TNFT: address(TNFTInstance), - BNFT: address(BNFTInstance), - etherFiNodesManager: address(etherFiNodesManager), - protocolRevenueManager: address(protocolRevenueManager), - etherFiNode: address(etherFiNode), - regulationsManager: address(regulationsManagerInstance), - liquidityPool: address(liquidityPool), - eETH: address(eETHInstance), - weEth: address(weEthInstance) - }); - - writeSuiteVersionFile(); - writeLpVersionFile(); - } - - function _stringToUint( - string memory numString - ) internal pure returns (uint256) { - uint256 val = 0; - bytes memory stringBytes = bytes(numString); - for (uint256 i = 0; i < stringBytes.length; i++) { - uint256 exp = stringBytes.length - i; - bytes1 ival = stringBytes[i]; - uint8 uval = uint8(ival); - uint256 jval = uval - uint256(0x30); - - val += (uint256(jval) * (10 ** (exp - 1))); - } - return val; - } - - function writeSuiteVersionFile() internal { - // Read Current version - string memory versionString = vm.readLine("release/logs/EtherFiSuite/version.txt"); - - // Cast string to uint256 - uint256 version = _stringToUint(versionString); - - version++; - - // Overwrites the version.txt file with incremented version - vm.writeFile( - "release/logs/EtherFiSuite/version.txt", - string(abi.encodePacked(Strings.toString(version))) - ); - - // Writes the data to .release file - vm.writeFile( - string( - abi.encodePacked( - "release/logs/EtherFiSuite/", - Strings.toString(version), - ".release" - ) - ), - string( - abi.encodePacked( - Strings.toString(version), - "\nTreasury: ", - Strings.toHexString(suiteAddressesStruct.treasury), - "\nNode Operator Key Manager: ", - Strings.toHexString(suiteAddressesStruct.nodeOperatorManager), - "\nAuctionManager: ", - Strings.toHexString(suiteAddressesStruct.auctionManager), - "\nStakingManager: ", - Strings.toHexString(suiteAddressesStruct.stakingManager), - "\nEtherFi Node Manager: ", - Strings.toHexString(suiteAddressesStruct.etherFiNodesManager), - "\nProtocol Revenue Manager: ", - Strings.toHexString(suiteAddressesStruct.protocolRevenueManager), - "\nTNFT: ", - Strings.toHexString(suiteAddressesStruct.TNFT), - "\nBNFT: ", - Strings.toHexString(suiteAddressesStruct.BNFT) - ) - ) - ); - } - - function writeLpVersionFile() internal { - // Read Current version - string memory versionString = vm.readLine("release/logs/LiquidityPool/version.txt"); - - // Cast string to uint256 - uint256 version = _stringToUint(versionString); - - version++; - - // Overwrites the version.txt file with incremented version - vm.writeFile( - "release/logs/LiquidityPool/version.txt", - string(abi.encodePacked(Strings.toString(version))) - ); - - // Writes the data to .release file - vm.writeFile( - string( - abi.encodePacked( - "release/logs/LiquidityPool/", - Strings.toString(version), - ".release" - ) - ), - string( - abi.encodePacked( - Strings.toString(version), - "\nRegulations Manager: ", - Strings.toHexString(suiteAddressesStruct.regulationsManager), - "\nLiquidity Pool: ", - Strings.toHexString(suiteAddressesStruct.liquidityPool), - "\neETH: ", - Strings.toHexString(suiteAddressesStruct.eETH), - "\nweETH: ", - Strings.toHexString(suiteAddressesStruct.weEth) - ) - ) - ); - } -} diff --git a/script/deploys/DeployEtherFiOperationParameters.s.sol b/script/deploys/DeployEtherFiOperationParameters.s.sol deleted file mode 100644 index 15080939a..000000000 --- a/script/deploys/DeployEtherFiOperationParameters.s.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "@openzeppelin/contracts/utils/Strings.sol"; - -import "../../src/Liquifier.sol"; -import "../../src/EtherFiRestaker.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/helpers/EtherFiOperationParameters.sol"; - - -contract Deploy is Script { - using Strings for string; - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - bool to_upgrade = true; - - if (to_upgrade) { - EtherFiOperationParameters instance = EtherFiOperationParameters(0xD0Ff8996DB4bDB46870b7E833b7532f484fEad1A); - EtherFiOperationParameters impl = new EtherFiOperationParameters(); - instance.upgradeTo(address(impl)); - } else { - EtherFiOperationParameters impl = new EtherFiOperationParameters(); - UUPSProxy proxy = new UUPSProxy(address(impl), ""); - - EtherFiOperationParameters instance = EtherFiOperationParameters(payable(proxy)); - - instance.updateTagAdmin("ORACLE", 0x566E58ac0F2c4BCaF6De63760C56cC3f825C48f5, true); - instance.updateTagAdmin("EIGENPOD", 0x566E58ac0F2c4BCaF6De63760C56cC3f825C48f5, true); - // instance.updateTagKeyValue("ORACLE", "WITHDRAWAL_TARGET_GAS_PRICE", "12.5"); // 12.5 gwei - // instance.updateTagKeyValue("ORACLE", "TARGET_LIQUIDITY_IN_PERCENT_OF_TVL", "2.0"); // 2.0 % of TVL - // instance.updateTagKeyValue("EIGENPOD", "PROOF_SUBMIT_TARGET_GAS_PRICE", "12.5"); // 12.5 gwei - } - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/deploys/DeployEtherFiRestaker.s.sol b/script/deploys/DeployEtherFiRestaker.s.sol deleted file mode 100644 index eda1ba4fb..000000000 --- a/script/deploys/DeployEtherFiRestaker.s.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "../../src/Liquifier.sol"; -import "../../src/EtherFiRestaker.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract Deploy is Script { - using Strings for string; - - UUPSProxy public liquifierProxy; - - Liquifier public liquifierInstance; - - AddressProvider public addressProvider; - address eigenlayerRewardsCoordinator; - - address admin; - - function run() external { - require(eigenlayerRewardsCoordinator != address(0), "must set rewardsCoordinator"); - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiRestaker restaker = EtherFiRestaker(payable(new UUPSProxy(payable(new EtherFiRestaker(eigenlayerRewardsCoordinator)), ""))); - restaker.initialize( - addressProvider.getContractAddress("LiquidityPool"), - addressProvider.getContractAddress("Liquifier") - ); - - new Liquifier(); - - // addressProvider.addContract(address(liquifierInstance), "Liquifier"); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployEtherFiRewardsRouter.s.sol b/script/deploys/DeployEtherFiRewardsRouter.s.sol deleted file mode 100644 index ac7cd18ea..000000000 --- a/script/deploys/DeployEtherFiRewardsRouter.s.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "src/EtherFiRewardsRouter.sol"; -import "src/UUPSProxy.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "forge-std/console.sol"; - -/* Deploy Command - * source .env && forge script ./script/deploys/DeployEtherFiRewardsRouter.s.sol:DeployEtherFiRewardsRouter --rpc-url MAINNET_RPC_URL --broadcast --etherscan-api-key $ETHERSCAN_API_KEY --verify --slow -vvvv -*/ - -contract DeployEtherFiRewardsRouter is Script { - - AddressProvider public addressProvider; - /////////////////////////////////////// - address roleRegistryProxyAddress = address(0x1d3Af47C1607A2EF33033693A9989D1d1013BB50); //replace with deployed RoleRegistryProxy address - address treasuryGnosisSafeAddress = address(0x0c83EAe1FE72c390A02E426572854931EefF93BA); - address etherfiRouterAdmin = address(0xc13C06899a9BbEbB3E2b38dBe86e4Ea8852AFC9b); - ////////////////////////////////////// - - function run() external { - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - - vm.startBroadcast(); - - RoleRegistry roleRegistryInstance = RoleRegistry(roleRegistryProxyAddress); - roleRegistryInstance.grantRole(keccak256("ETHERFI_REWARDS_ROUTER_ADMIN_ROLE"), etherfiRouterAdmin); - - addressProvider = AddressProvider(addressProviderAddress); - - address liquidityPoolProxyAddress = addressProvider.getContractAddress("LiquidityPool"); - bytes memory initializerData = abi.encodeWithSelector(EtherFiRewardsRouter.initialize.selector); - EtherFiRewardsRouter etherFiRewardsRouterImplementation = new EtherFiRewardsRouter(liquidityPoolProxyAddress, treasuryGnosisSafeAddress, roleRegistryProxyAddress); - UUPSProxy etherFiRewardsRouterProxy = new UUPSProxy(address(etherFiRewardsRouterImplementation), initializerData); - } -} diff --git a/script/deploys/DeployEtherFiViewer.s.sol b/script/deploys/DeployEtherFiViewer.s.sol deleted file mode 100644 index 9ed28953c..000000000 --- a/script/deploys/DeployEtherFiViewer.s.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "src/helpers/EtherFiViewer.sol"; -import "src/UUPSProxy.sol"; - -contract DeployEtherFiViewer is Script { - - function run() external { - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiViewer impl = new EtherFiViewer(); - - // UUPSProxy proxy = new UUPSProxy(address(impl), ""); - // EtherFiViewer viewer = EtherFiViewer(address(proxy)); - // viewer.initialize(addressProviderAddress); - - EtherFiViewer viewer = EtherFiViewer(address(0x2ecd155405cA52a5ca0e552981fF44A8252FAb81)); - viewer.upgradeTo(address(impl)); - vm.stopBroadcast(); - - uint256[] memory validatorIds = new uint256[](2); - validatorIds[0] = 25678; - validatorIds[1] = 29208; - - address[] memory etherFiNodeAddresses = viewer.EtherFiNodesManager_etherFiNodeAddress(validatorIds); - assert(etherFiNodeAddresses[0] == 0x31db9021ec8E1065e1f55553c69e1B1ea9d20533); - assert(etherFiNodeAddresses[1] == 0xC3D3662A44c0d80080D3AF0eea752369c504724e); - - } -} diff --git a/script/deploys/DeployEtherFiWithdrawalBuffer.s.sol b/script/deploys/DeployEtherFiWithdrawalBuffer.s.sol deleted file mode 100644 index ebc4050cf..000000000 --- a/script/deploys/DeployEtherFiWithdrawalBuffer.s.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "@openzeppelin/contracts/utils/Strings.sol"; - -import "../../src/Liquifier.sol"; -import "../../src/EtherFiRestaker.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/EtherFiRedemptionManager.sol"; - - -contract Deploy is Script { - using Strings for string; - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiRedemptionManager impl = new EtherFiRedemptionManager( - addressProvider.getContractAddress("LiquidityPool"), - addressProvider.getContractAddress("EETH"), - addressProvider.getContractAddress("WeETH"), - 0x0c83EAe1FE72c390A02E426572854931EefF93BA, // protocol safe - 0x1d3Af47C1607A2EF33033693A9989D1d1013BB50 // role registry - ); - UUPSProxy proxy = new UUPSProxy(payable(impl), ""); - - EtherFiRedemptionManager instance = EtherFiRedemptionManager(payable(proxy)); - instance.initialize(10_00, 1_00, 1_00, 5 ether, 0.001 ether); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployImplementationContract.s.sol b/script/deploys/DeployImplementationContract.s.sol deleted file mode 100644 index d1698a17e..000000000 --- a/script/deploys/DeployImplementationContract.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Script.sol"; -// import "../../src/eBtcRateProvider.sol"; -// import "../../src/helpers/EtherFiViewer.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/EtherFiAdmin.sol"; -import "../../src/EtherFiOracle.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/Liquifier.sol"; - -import "../Create2Factory.sol"; - - -contract Deploy is Script { - bytes32 immutable salt = keccak256("ETHER_FI"); - Create2Factory immutable factory = Create2Factory(0x6521991A0BC180a5df7F42b27F4eE8f3B192BA62); - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); - - vm.startBroadcast(deployerPrivateKey); - bytes memory code = abi.encodePacked(type(Liquifier).creationCode); - factory.deploy(code, salt); - } -} \ No newline at end of file diff --git a/script/deploys/DeployLiquifier.s.sol b/script/deploys/DeployLiquifier.s.sol deleted file mode 100644 index 23f6012df..000000000 --- a/script/deploys/DeployLiquifier.s.sol +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "../../src/Liquifier.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract DeployLiquifierScript is Script { - using Strings for string; - - UUPSProxy public liquifierProxy; - - Liquifier public liquifierImplementation; - Liquifier public liquifierInstance; - - AddressProvider public addressProvider; - - address cbEth_Eth_Pool; - address wbEth_Eth_Pool; - address stEth_Eth_Pool; - address cbEth; - address wbEth; - address stEth; - address cbEthStrategy; - address wbEthStrategy; - address stEthStrategy; - address eigenLayerStrategyManager; - address lidoWithdrawalQueue; - uint32 depositCapRefreshInterval; - - address admin; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - liquifierImplementation = new Liquifier(); - liquifierProxy = new UUPSProxy(payable(liquifierImplementation), ""); - liquifierInstance = Liquifier(payable(liquifierProxy)); - if(block.chainid == 1) { - cbEth_Eth_Pool = 0x5FAE7E604FC3e24fd43A72867ceBaC94c65b404A; - wbEth_Eth_Pool = 0xBfAb6FA95E0091ed66058ad493189D2cB29385E6; - stEth_Eth_Pool = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022; - cbEth = 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704; - wbEth = 0xa2E3356610840701BDf5611a53974510Ae27E2e1; - stEth = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; - cbEthStrategy = 0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc; - wbEthStrategy = 0x7CA911E83dabf90C90dD3De5411a10F1A6112184; - stEthStrategy = 0x93c4b944D05dfe6df7645A86cd2206016c51564D; - eigenLayerStrategyManager = 0x858646372CC42E1A627fcE94aa7A7033e7CF075A; - lidoWithdrawalQueue = 0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1; - - depositCapRefreshInterval = 3600; // 3600 seconds = 1 hour - - admin = 0x2aCA71020De61bb532008049e1Bd41E451aE8AdC; - } else if(block.chainid == 5) { - // liquifierInstance.initialize(); - } - - - // function initialize(address _treasury, address _liquidityPool, address _eigenLayerStrategyManager, address _lidoWithdrawalQueue, - // address _stEth, address _cbEth, address _wbEth, address _cbEth_Eth_Pool, address _wbEth_Eth_Pool, address _stEth_Eth_Pool, - // uint32 _depositCapRefreshInterval) - // liquifierInstance.initialize( - // addressProvider.getContractAddress("Treasury"), - // addressProvider.getContractAddress("LiquidityPool"), - // eigenLayerStrategyManager, - // lidoWithdrawalQueue, - // stEth, - // cbEth, - // wbEth, - // cbEth_Eth_Pool, - // wbEth_Eth_Pool, - // stEth_Eth_Pool, - // depositCapRefreshInterval // deposit cap refresh interval in seconds - // ); - - liquifierInstance.updateAdmin(admin, true); - - address oracleWallet = 0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F; - liquifierInstance.updateAdmin(oracleWallet, true); - - liquifierInstance.registerToken(stEth, stEthStrategy, true, 0, 1, 10, false); // 1 ether timebound cap, 10 ether max cap - liquifierInstance.registerToken(cbEth, cbEthStrategy, true, 0, 1, 10, false); - liquifierInstance.registerToken(wbEth, wbEthStrategy, true, 0, 1, 10, false); - - addressProvider.addContract(address(liquifierInstance), "Liquifier"); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployLoyaltyPointsMarketSafe.sol b/script/deploys/DeployLoyaltyPointsMarketSafe.sol deleted file mode 100644 index 6441a6ab3..000000000 --- a/script/deploys/DeployLoyaltyPointsMarketSafe.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/LoyaltyPointsMarketSafe.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract DeployLoyaltyPointsMarketSafeScript is Script { - - LoyaltyPointsMarketSafe public lpaMarketSafe; - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - lpaMarketSafe = new LoyaltyPointsMarketSafe(1500000000000); - - addressProvider.addContract(address(lpaMarketSafe), "LoyaltyPointsMarketSafeV2"); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployMultiCall.s.sol b/script/deploys/DeployMultiCall.s.sol deleted file mode 100644 index a3d0499ef..000000000 --- a/script/deploys/DeployMultiCall.s.sol +++ /dev/null @@ -1,62 +0,0 @@ -/** - *Submitted for verification at Etherscan.io on 2019-06-10 -*/ -import "forge-std/Script.sol"; - -pragma solidity >=0.5.0; -pragma experimental ABIEncoderV2; - -/// @title Multicall - Aggregate results from multiple read-only function calls -/// @author Michael Elliot -/// @author Joshua Levine -/// @author Nick Johnson - -contract Multicall { - struct Call { - address target; - bytes callData; - } - function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { - blockNumber = block.number; - returnData = new bytes[](calls.length); - for(uint256 i = 0; i < calls.length; i++) { - (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); - require(success); - returnData[i] = ret; - } - } - // Helper functions - function getEthBalance(address addr) public view returns (uint256 balance) { - balance = addr.balance; - } - function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { - blockHash = blockhash(blockNumber); - } - function getLastBlockHash() public view returns (bytes32 blockHash) { - blockHash = blockhash(block.number - 1); - } - function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { - timestamp = block.timestamp; - } - function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { - difficulty = block.difficulty; - } - function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { - gaslimit = block.gaslimit; - } - function getCurrentBlockCoinbase() public view returns (address coinbase) { - coinbase = block.coinbase; - } -} - -contract DeployMultiCall is Script { - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - - Multicall multicall = new Multicall(); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/deploys/DeployPhaseOne.s.sol b/script/deploys/DeployPhaseOne.s.sol deleted file mode 100644 index c648a22fe..000000000 --- a/script/deploys/DeployPhaseOne.s.sol +++ /dev/null @@ -1,250 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/Treasury.sol"; -import "../../src/NodeOperatorManager.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/BNFT.sol"; -import "../../src/TNFT.sol"; -import "../../src/archive/ProtocolRevenueManager.sol"; -import "../../src/StakingManager.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/UUPSProxy.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract DeployPhaseOne is Script { - using Strings for string; - - /*---- Storage variables ----*/ - - UUPSProxy public auctionManagerProxy; - UUPSProxy public stakingManagerProxy; - UUPSProxy public etherFiNodeManagerProxy; - UUPSProxy public protocolRevenueManagerProxy; - UUPSProxy public TNFTProxy; - UUPSProxy public BNFTProxy; - - BNFT public BNFTImplementation; - BNFT public BNFTInstance; - - TNFT public TNFTImplementation; - TNFT public TNFTInstance; - - AuctionManager public auctionManagerImplementation; - AuctionManager public auctionManager; - - StakingManager public stakingManagerImplementation; - StakingManager public stakingManager; - - ProtocolRevenueManager public protocolRevenueManagerImplementation; - ProtocolRevenueManager public protocolRevenueManager; - - EtherFiNodesManager public etherFiNodesManagerImplementation; - EtherFiNodesManager public etherFiNodesManager; - - struct suiteAddresses { - address treasury; - address nodeOperatorManager; - address auctionManager; - address stakingManager; - address TNFT; - address BNFT; - address etherFiNodesManager; - address protocolRevenueManager; - address etherFiNode; - } - - suiteAddresses suiteAddressesStruct; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address ethDepositContractAddress; - if (block.chainid == 5) { - // goerli - ethDepositContractAddress = 0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b; - } else if (block.chainid == 1) { - ethDepositContractAddress = 0x00000000219ab540356cBB839Cbe05303d7705Fa; - } else { - assert(false); - } - - vm.startBroadcast(deployerPrivateKey); - - // Deploy contracts - Treasury treasury = new Treasury(); - NodeOperatorManager nodeOperatorManager = new NodeOperatorManager(); - - auctionManagerImplementation = new AuctionManager(); - auctionManagerProxy = new UUPSProxy(address(auctionManagerImplementation),""); - auctionManager = AuctionManager(address(auctionManagerProxy)); - auctionManager.initialize(address(nodeOperatorManager)); - - stakingManagerImplementation = new StakingManager(); - stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation),""); - stakingManager = StakingManager(address(stakingManagerProxy)); - //stakingManager.initialize(address(auctionManager), ethDepositContractAddress); - - BNFTImplementation = new BNFT(); - BNFTProxy = new UUPSProxy(address(BNFTImplementation),""); - BNFTInstance = BNFT(address(BNFTProxy)); - BNFTInstance.initialize(address(stakingManager)); - - TNFTImplementation = new TNFT(); - TNFTProxy = new UUPSProxy(address(TNFTImplementation),""); - TNFTInstance = TNFT(address(TNFTProxy)); - TNFTInstance.initialize(address(stakingManager)); - - protocolRevenueManagerImplementation = new ProtocolRevenueManager(); - protocolRevenueManagerProxy = new UUPSProxy(address(protocolRevenueManagerImplementation),""); - protocolRevenueManager = ProtocolRevenueManager(payable(address(protocolRevenueManagerProxy))); - protocolRevenueManager.initialize(); - - etherFiNodesManagerImplementation = new EtherFiNodesManager(); - etherFiNodeManagerProxy = new UUPSProxy(address(etherFiNodesManagerImplementation),""); - etherFiNodesManager = EtherFiNodesManager(payable(address(etherFiNodeManagerProxy))); - /* - etherFiNodesManager.initialize( - address(treasury), - address(auctionManager), - address(stakingManager), - address(TNFTInstance), - address(BNFTInstance), - address(0), // TODO - address(0), - address(0) - ); - */ - - EtherFiNode etherFiNode = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); - - // Setup dependencies - nodeOperatorManager.setAuctionContractAddress(address(auctionManager)); - - auctionManager.setStakingManagerContractAddress(address(stakingManager)); - - protocolRevenueManager.setAuctionManagerAddress(address(auctionManager)); - protocolRevenueManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); - - /* - stakingManager.setEtherFiNodesManagerAddress(address(etherFiNodesManager)); - stakingManager.registerEtherFiNodeImplementationContract(address(etherFiNode)); - stakingManager.registerTNFTContract(address(TNFTInstance)); - stakingManager.registerBNFTContract(address(BNFTInstance)); - */ - - vm.stopBroadcast(); - - suiteAddressesStruct = suiteAddresses({ - treasury: address(treasury), - nodeOperatorManager: address(nodeOperatorManager), - auctionManager: address(auctionManager), - stakingManager: address(stakingManager), - TNFT: address(TNFTInstance), - BNFT: address(BNFTInstance), - etherFiNodesManager: address(etherFiNodesManager), - protocolRevenueManager: address(protocolRevenueManager), - etherFiNode: address(etherFiNode) - }); - - writeSuiteVersionFile(); - writeNFTVersionFile(); - } - - function _stringToUint( - string memory numString - ) internal pure returns (uint256) { - uint256 val = 0; - bytes memory stringBytes = bytes(numString); - for (uint256 i = 0; i < stringBytes.length; i++) { - uint256 exp = stringBytes.length - i; - bytes1 ival = stringBytes[i]; - uint8 uval = uint8(ival); - uint256 jval = uval - uint256(0x30); - - val += (uint256(jval) * (10 ** (exp - 1))); - } - return val; - } - - function writeSuiteVersionFile() internal { - // Read Current version - string memory versionString = vm.readLine("release/logs/PhaseOne/version.txt"); - - // Cast string to uint256 - uint256 version = _stringToUint(versionString); - - version++; - - // Overwrites the version.txt file with incremented version - vm.writeFile( - "release/logs/PhaseOne/version.txt", - string(abi.encodePacked(Strings.toString(version))) - ); - - // Writes the data to .release file - vm.writeFile( - string( - abi.encodePacked( - "release/logs/PhaseOne/", - Strings.toString(version), - ".release" - ) - ), - string( - abi.encodePacked( - Strings.toString(version), - "\nTreasury: ", - Strings.toHexString(suiteAddressesStruct.treasury), - "\nNode Operator Key Manager: ", - Strings.toHexString(suiteAddressesStruct.nodeOperatorManager), - "\nAuctionManager: ", - Strings.toHexString(suiteAddressesStruct.auctionManager), - "\nStakingManager: ", - Strings.toHexString(suiteAddressesStruct.stakingManager), - "\nEtherFi Node Manager: ", - Strings.toHexString(suiteAddressesStruct.etherFiNodesManager), - "\nProtocol Revenue Manager: ", - Strings.toHexString(suiteAddressesStruct.protocolRevenueManager) - ) - ) - ); - } - - function writeNFTVersionFile() internal { - // Read Current version - string memory versionString = vm.readLine("release/logs/PhaseOneNFTs/version.txt"); - - // Cast string to uint256 - uint256 version = _stringToUint(versionString); - - version++; - - // Overwrites the version.txt file with incremented version - vm.writeFile( - "release/logs/PhaseOneNFTs/version.txt", - string(abi.encodePacked(Strings.toString(version))) - ); - - // Writes the data to .release file - vm.writeFile( - string( - abi.encodePacked( - "release/logs/PhaseOneNFTs/", - Strings.toString(version), - ".release" - ) - ), - string( - abi.encodePacked( - Strings.toString(version), - "\nTNFT: ", - Strings.toHexString(suiteAddressesStruct.TNFT), - "\nBNFT: ", - Strings.toHexString(suiteAddressesStruct.BNFT) - ) - ) - ); - } -} diff --git a/script/deploys/DeployPhaseOnePointFive.s.sol b/script/deploys/DeployPhaseOnePointFive.s.sol deleted file mode 100644 index db69f9fe9..000000000 --- a/script/deploys/DeployPhaseOnePointFive.s.sol +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/MembershipManager.sol"; -import "../../src/MembershipNFT.sol"; -import "../../src/WeETH.sol"; -import "../../src/EETH.sol"; -import "../../src/NFTExchange.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/archive/RegulationsManager.sol"; -import "../../src/UUPSProxy.sol"; - -contract DeployPhaseOnePointFiveScript is Script { - - /*---- Storage variables ----*/ - - UUPSProxy public membershipManagerProxy; - UUPSProxy public membershipNFTProxy; - UUPSProxy public eETHProxy; - UUPSProxy public weETHProxy; - UUPSProxy public liquidityPoolProxy; - UUPSProxy public regulationsManagerProxy; - UUPSProxy public nftExchangeProxy; - - MembershipManager public membershipManagerImplementation; - MembershipManager public membershipManager; - - MembershipNFT public membershipNFTImplementation; - MembershipNFT public membershipNFT; - - WeETH public weETHImplementation; - WeETH public weETH; - - EETH public eETHImplementation; - EETH public eETH; - - LiquidityPool public liquidityPoolImplementation; - LiquidityPool public liquidityPool; - - RegulationsManager public regulationsManagerImplementation; - RegulationsManager public regulationsManager; - - NFTExchange public nftExchangeImplementation; - NFTExchange public nftExchange; - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - - bytes32[] memory emptyProof; - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address stakingManagerProxyAddress = addressProvider.getContractAddress("StakingManager"); - address etherFiNodesManagerProxyAddress = addressProvider.getContractAddress("EtherFiNodesManager"); - address treasury = addressProvider.getImplementationAddress("Treasury"); - address protocolRevenueManagerProxy = addressProvider.getContractAddress("ProtocolRevenueManager"); - address tnft = addressProvider.getContractAddress("TNFT"); - address admin = vm.envAddress("DEPLOYER"); - - bytes32 initialHash = vm.envBytes32("INITIAL_HASH"); - - string memory baseURI = vm.envString("BASE_URI"); - - // Deploy contracts - regulationsManagerImplementation = new RegulationsManager(); - regulationsManagerProxy = new UUPSProxy(address(regulationsManagerImplementation),""); - regulationsManager = RegulationsManager(address(regulationsManagerProxy)); - regulationsManager.initialize(); - addressProvider.addContract(address(regulationsManagerProxy), "RegulationsManager"); - - liquidityPoolImplementation = new LiquidityPool(); - liquidityPoolProxy = new UUPSProxy(address(liquidityPoolImplementation),""); - liquidityPool = LiquidityPool(payable(address(liquidityPoolProxy))); - addressProvider.addContract(address(liquidityPoolProxy), "LiquidityPool"); - - eETHImplementation = new EETH(); - eETHProxy = new UUPSProxy(address(eETHImplementation),""); - eETH = EETH(address(eETHProxy)); - eETH.initialize(address(liquidityPool)); - addressProvider.addContract(address(eETHProxy), "EETH"); - - membershipNFTImplementation = new MembershipNFT(); - membershipNFTProxy = new UUPSProxy(address(membershipNFTImplementation),""); - membershipNFT = MembershipNFT(payable(address(membershipNFTProxy))); - addressProvider.addContract(address(membershipNFTProxy), "MembershipNFT"); - - membershipManagerImplementation = new MembershipManager(); - membershipManagerProxy = new UUPSProxy(address(membershipManagerImplementation),""); - membershipManager = MembershipManager(payable(address(membershipManagerProxy))); - addressProvider.addContract(address(membershipManagerProxy), "MembershipManager"); - - liquidityPool.initialize(address(eETH), address(stakingManagerProxyAddress), address(etherFiNodesManagerProxyAddress), address(membershipManager), address(tnft), address(0), address(0)); - // membershipManager.initialize(address(eETH), address(liquidityPool), address(membershipNFT), treasury, protocolRevenueManagerProxy); - membershipNFT.initialize(baseURI, address(membershipManager)); - - weETHImplementation = new WeETH(); - weETHProxy = new UUPSProxy(address(weETHImplementation),""); - weETH = WeETH(address(weETHProxy)); - weETH.initialize(address(liquidityPool), address(eETH)); - addressProvider.addContract(address(weETHProxy), "WeETH"); - - nftExchangeImplementation = new NFTExchange(); - nftExchangeProxy = new UUPSProxy(address(nftExchangeImplementation),""); - nftExchange = NFTExchange(address(nftExchangeProxy)); - nftExchange.initialize(tnft, address(membershipNFT), address(etherFiNodesManagerProxyAddress)); - addressProvider.addContract(address(nftExchangeProxy), "NFTExchange"); - - setUpAdmins(admin); - - regulationsManager.initializeNewWhitelist(initialHash); - regulationsManager.confirmEligibility(initialHash); - membershipManager.setTopUpCooltimePeriod(28 days); - - initializeTiers(); - preMint(); - membershipManager.setFeeAmounts(0.05 ether, 0.05 ether, 0, 0); - membershipManager.pauseContract(); - - vm.stopBroadcast(); - } - - function setUpAdmins(address _admin) internal { - // liquidityPool.updateAdmin(_admin, true); - regulationsManager.updateAdmin(_admin, true); - membershipManager.updateAdmin(_admin, true); - membershipNFT.updateAdmin(_admin, true); - nftExchange.updateAdmin(_admin); - } - - function initializeTiers() internal { - membershipManager.addNewTier(0, 1); - membershipManager.addNewTier(672, 2); - membershipManager.addNewTier(2016, 3); - membershipManager.addNewTier(4704, 4); - } - - function preMint() internal { - bytes32[] memory emptyProof; - uint256 minAmount = membershipManager.minimumAmountForMint(); - // MembershipManager V1 does not have `wrapEthBatch` - // membershipManager.wrapEthBatch{value: 100 * minAmount}(100, minAmount, 0, emptyProof); - } -} diff --git a/script/deploys/DeployPhaseTwo.s.sol b/script/deploys/DeployPhaseTwo.s.sol deleted file mode 100644 index f45a7cecd..000000000 --- a/script/deploys/DeployPhaseTwo.s.sol +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/EtherFiOracle.sol"; -import "../../src/EtherFiAdmin.sol"; -import "../../src/WithdrawRequestNFT.sol"; -import "../../src/helpers/AddressProvider.sol"; - -import "../../src/interfaces/IAuctionManager.sol"; -import "../../src/interfaces/IStakingManager.sol"; -import "../../src/interfaces/ILiquidityPool.sol"; -import "../../src/interfaces/IMembershipManager.sol"; -import "../../src/interfaces/IMembershipNFT.sol"; -import "../../src/interfaces/IEtherFiNodesManager.sol"; -import "../../src/interfaces/IEtherFiOracle.sol"; -import "../../src/interfaces/IWithdrawRequestNFT.sol"; - -import "../../src/UUPSProxy.sol"; - -contract DeployPhaseTwoScript is Script { - UUPSProxy public etherFiOracleProxy; - EtherFiOracle public etherFiOracleInstance; - EtherFiOracle public etherFiOracleImplementation; - - UUPSProxy public etherFiAdminProxy; - EtherFiAdmin public etherFiAdminInstance; - EtherFiAdmin public etherFiAdminImplementation; - - UUPSProxy public withdrawRequestNftProxy; - WithdrawRequestNFT public withdrawRequestNftInstance; - WithdrawRequestNFT public withdrawRequestNftImplementation; - - AddressProvider public addressProvider; - - address addressProviderAddress; - address etherFiOracleAddress; - address stakingManagerAddress; - address auctionAddress; - address managerAddress; - address liquidityPoolAddress; - address eEthAddress; - address membershipManagerAddress; - address withdrawRequestNFTAddress; - - address oracleAdminAddress; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - oracleAdminAddress = vm.envAddress("ORACLE_ADMIN_ADDRESS"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - deploy_WithdrawRequestNFT(); - - deploy_EtherFiOracle(); - - deploy_EtherFiAdmin(); - - vm.stopBroadcast(); - } - - function retrieve_contract_addresses() internal { - // Retrieve the addresses of the contracts that have already deployed - etherFiOracleAddress = addressProvider.getContractAddress("EtherFiOracle"); - stakingManagerAddress = addressProvider.getContractAddress("StakingManager"); - auctionAddress = addressProvider.getContractAddress("AuctionManager"); - managerAddress = addressProvider.getContractAddress("EtherFiNodesManager"); - liquidityPoolAddress = addressProvider.getContractAddress("LiquidityPool"); - eEthAddress = addressProvider.getContractAddress("EETH"); - membershipManagerAddress = addressProvider.getContractAddress("MembershipManager"); - withdrawRequestNFTAddress = addressProvider.getContractAddress("WithdrawRequestNFT"); - } - - function deploy_WithdrawRequestNFT() internal { - if (addressProvider.getContractAddress("WithdrawRequestNFT") != address(0)) { - addressProvider.removeContract("WithdrawRequestNFT"); - } - retrieve_contract_addresses(); - - withdrawRequestNftImplementation = new WithdrawRequestNFT(address(0)); - withdrawRequestNftProxy = new UUPSProxy(address(withdrawRequestNftImplementation), ""); - withdrawRequestNftInstance = WithdrawRequestNFT(payable(withdrawRequestNftProxy)); - - withdrawRequestNftInstance.initialize(liquidityPoolAddress, eEthAddress, membershipManagerAddress); - - addressProvider.addContract(address(withdrawRequestNftProxy), "WithdrawRequestNFT"); - } - - function deploy_EtherFiOracle() internal { - if (addressProvider.getContractAddress("EtherFiOracle") != address(0)) { - addressProvider.removeContract("EtherFiOracle"); - } - retrieve_contract_addresses(); - - etherFiOracleImplementation = new EtherFiOracle(); - etherFiOracleProxy = new UUPSProxy(address(etherFiOracleImplementation), ""); - etherFiOracleInstance = EtherFiOracle(payable(etherFiOracleProxy)); - - if (block.chainid == 1) { - // Mainnet's slot 0 happened at 1606824023; https://beaconcha.in/slot/0 - etherFiOracleInstance.initialize(1, 7200, 0, 32, 12, 1606824023); - - // TODO - // address oracleNodeAddress = 0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39 - // etherFiOracleInstance.addCommitteeMember(oracleNodeAddress); - - } else if (block.chainid == 5) { - // Goerli's slot 0 happened at 1616508000; https://goerli.beaconcha.in/slot/0 - etherFiOracleInstance.initialize(1, 96, 0, 32, 12, 1616508000); - // 96 slots = 19.2 mins, 7200 slots = 225 epochs = 1day - - etherFiOracleInstance.addCommitteeMember(address(0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39)); - etherFiOracleInstance.addCommitteeMember(address(0x601B37004f2A6B535a6cfBace0f88D2d534aCcD8)); - } else { - require(false, "chain is wrong"); - } - - addressProvider.addContract(address(etherFiOracleProxy), "EtherFiOracle"); - } - - function deploy_EtherFiAdmin() internal { - if (addressProvider.getContractAddress("EtherFiAdmin") != address(0)) { - addressProvider.removeContract("EtherFiAdmin"); - } - retrieve_contract_addresses(); - - etherFiAdminImplementation = new EtherFiAdmin(); - etherFiAdminProxy = new UUPSProxy(address(etherFiAdminImplementation), ""); - etherFiAdminInstance = EtherFiAdmin(payable(etherFiAdminProxy)); - - int32 acceptableRebaseAprInBps; - uint16 postReportWaitTimeInSlots; - - if (block.chainid == 1) { - acceptableRebaseAprInBps = 500; // 5% - postReportWaitTimeInSlots = 7200 / 2; // 7200 slots = 225 epochs = 1 day - } else if (block.chainid == 5) { - acceptableRebaseAprInBps = 600; // 6% - postReportWaitTimeInSlots = 15 minutes / 12 seconds; // 15 minutes - } else { - require(false, "chain is wrong"); - } - - etherFiAdminInstance.initialize( - etherFiOracleAddress, - stakingManagerAddress, - auctionAddress, - managerAddress, - liquidityPoolAddress, - membershipManagerAddress, - withdrawRequestNFTAddress, - acceptableRebaseAprInBps, - postReportWaitTimeInSlots - ); - // etherFiAdminInstance.updateAdmin(oracleAdminAddress, true); - IEtherFiOracle(address(etherFiOracleAddress)).setEtherFiAdmin(address(etherFiAdminInstance)); - //IWithdrawRequestNFT(address(withdrawRequestNFTAddress)).updateAdmin(address(etherFiAdminInstance), true); - - // Used only for development - if (false) { - address admin = address(etherFiAdminInstance); - IAuctionManager(address(auctionAddress)).updateAdmin(admin, true); - //IStakingManager(address(stakingManagerAddress)).updateAdmin(admin, true); - // ILiquidityPool(address(liquidityPoolAddress)).updateAdmin(admin, true); - IMembershipManager(address(membershipManagerAddress)).updateAdmin(admin, true); - //IEtherFiNodesManager(address(managerAddress)).updateAdmin(admin, true); - } - - addressProvider.addContract(address(etherFiAdminProxy), "EtherFiAdmin"); - } -} diff --git a/script/deploys/DeployRoleRegistry.s.sol b/script/deploys/DeployRoleRegistry.s.sol deleted file mode 100644 index 20acf96ae..000000000 --- a/script/deploys/DeployRoleRegistry.s.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Script.sol"; - -import "src/RoleRegistry.sol"; -import "src/UUPSProxy.sol"; -import "../../src/helpers/AddressProvider.sol"; - - -/* deploy command - * source .env && forge script ./script/deploys/DeployRoleRegistry.s.sol:DeployRoleRegistry --rpc-url MAINNET_RPC_URL --broadcast --etherscan-api-key $ETHERSCAN_API_KEY --verify --slow -vvvv -*/ - -contract DeployRoleRegistry is Script { - - AddressProvider public addressProvider; - /////////////////////////////////////// - address superAdmin = address(0x8D5AAc5d3d5cda4c404fA7ee31B0822B648Bb150); //replace with actual super admin address - ////////////////////////////////////// - - function run() external { - vm.startBroadcast(); - - RoleRegistry roleRegistryImplementation = new RoleRegistry(); - bytes memory initializerData = abi.encodeWithSelector(RoleRegistry.initialize.selector, superAdmin); - UUPSProxy roleRegistryProxy = new UUPSProxy(address(roleRegistryImplementation), initializerData); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployTVLOracle.s.sol b/script/deploys/DeployTVLOracle.s.sol deleted file mode 100644 index ad6a0ebac..000000000 --- a/script/deploys/DeployTVLOracle.s.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/TVLOracle.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract DeployTVLOracleScript is Script { - - /*---- Storage variables ----*/ - - TVLOracle public tvlOracle; - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - address tvlAggregatorAddress = vm.envAddress("TVL_AGGREGATOR_ADDRESS"); - - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - // Deploy contract - tvlOracle = new TVLOracle(tvlAggregatorAddress); - addressProvider.addContract(address(tvlOracle), "TVLOracle"); - - vm.stopBroadcast(); - } -} diff --git a/script/deploys/DeployV2Dot49.s.sol b/script/deploys/DeployV2Dot49.s.sol deleted file mode 100644 index 3f939c867..000000000 --- a/script/deploys/DeployV2Dot49.s.sol +++ /dev/null @@ -1,289 +0,0 @@ - -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/EtherFiAdmin.sol"; -import "../../src/WeETH.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/EtherFiTimelock.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/EtherFiRewardsRouter.sol"; -import "../../src/EtherFiRedemptionManager.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/WithdrawRequestNFT.sol"; -import "forge-std/console.sol"; -import "forge-std/console2.sol"; - -import "../../src/EETH.sol"; - -contract DeployV2Dot49Script is Script { - - LiquidityPool liquidityPoolImplementation; - RoleRegistry roleRegistryImplementation; - EtherFiAdmin etherFiAdminImplementation; - EtherFiRewardsRouter etherFiRewardsRouterImplementation; - WeETH weEthImplementation; - EtherFiNodesManager managerImplementation; - WithdrawRequestNFT withdrawRequestNFTImplementation; - EtherFiRedemptionManager etherFiRedemptionManagerImplementation; - EtherFiTimelock operatingTimelockImplementation; - - LiquidityPool public liquidityPoolInstance; - RoleRegistry public roleRegistryInstance; - EtherFiAdmin public etherFiAdminInstance; - EtherFiRewardsRouter public etherFiRewardsRouterInstance; - WeETH public weETHInstance; - EtherFiNodesManager public managerInstance; - WithdrawRequestNFT public withdrawRequestNFTInstance; - EtherFiRedemptionManager public etherFiRedemptionManagerInstance; - EtherFiTimelock public operatingTimelockInstance; - EtherFiTimelock public oldOperatingTimelockInstance; - EtherFiTimelock public timelockInstance; - EETH public eETHInstance; - - bool public isProd = true; - - AddressProvider public addressProvider; - EtherFiTimelock public etherFiTimelock; - address public deployerKey = address(0x123); //get correct deployer key - address public treasuryGnosisSafeAddress = address(0x0c83EAe1FE72c390A02E426572854931EefF93BA); - - address public treasuryAddress = address(0x0c83EAe1FE72c390A02E426572854931EefF93BA); - - address public etherfiMultisig = address(0x2aCA71020De61bb532008049e1Bd41E451aE8AdC); - address public etherfiOracleAdmin = address(0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F); - address public liquidityPoolAdmin = address(0xFa238cB37E58556b23ea45643FFe4Da382162a53); - address public hypernativeEoa = address(0x9AF1298993DC1f397973C62A5D47a284CF76844D); - address public timelockAddress = address(0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761); - address public oldTreasury = address(0x6329004E903B7F420245E7aF3f355186f2432466); - address public etherOracleMember1 = address(0xDd777e5158Cb11DB71B4AF93C75A96eA11A2A615); - address public etherOracleMember2 = address(0x2c7cB7d5dC4aF9caEE654553a144C76F10D4b320); - - function init() internal { - addressProvider = AddressProvider(vm.envAddress("CONTRACT_REGISTRY")); - roleRegistryInstance = RoleRegistry(address(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9)); - operatingTimelockInstance = EtherFiTimelock(payable(address(0xcD425f44758a08BaAB3C4908f3e3dE5776e45d7a))); - timelockInstance = EtherFiTimelock(payable(address(0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761))); - - etherFiAdminInstance = EtherFiAdmin(addressProvider.getContractAddress("EtherFiAdmin")); - managerInstance = EtherFiNodesManager(payable(addressProvider.getContractAddress("EtherFiNodesManager"))); - etherFiRewardsRouterInstance = EtherFiRewardsRouter(payable(address(0x73f7b1184B5cD361cC0f7654998953E2a251dd58))); - liquidityPoolInstance = LiquidityPool(payable(addressProvider.getContractAddress("LiquidityPool"))); - weETHInstance = WeETH(addressProvider.getContractAddress("WeETH")); - withdrawRequestNFTInstance = WithdrawRequestNFT(addressProvider.getContractAddress("WithdrawRequestNFT")); - etherFiRedemptionManagerInstance = EtherFiRedemptionManager(payable(address(0xDadEf1fFBFeaAB4f68A9fD181395F68b4e4E7Ae0))); - eETHInstance = EETH(addressProvider.getContractAddress("EETH")); - - - //print all addresses of contracts in same order - console2.log('AddressProvider: ', address(addressProvider)); - console2.log('RoleRegistry: ', address(roleRegistryInstance)); - console2.log('OperatingTimelock: ', address(operatingTimelockInstance)); - console2.log('Timelock: ', address(timelockInstance)); - - console2.log('EtherFiAdmin: ', address(etherFiAdminInstance)); - console2.log('EtherFiNodesManager: ', address(managerInstance)); - console2.log('EtherFiRewardsRouter: ', address(etherFiRewardsRouterInstance)); - console2.log('LiquidityPool: ', address(liquidityPoolInstance)); - console2.log('WeETH: ', address(weETHInstance)); - console2.log('WithdrawRequestNFT: ', address(withdrawRequestNFTInstance)); - console2.log('EtherFiRedemptionManager: ', address(etherFiRedemptionManagerInstance)); - console2.log('EETH: ', address(eETHInstance)); - console2.log('Treasury: ', address(treasuryAddress)); - - } - -function writeDeployedContractsToFile() internal { - // Split the JSON generation to reduce stack variables - string memory jsonPart1 = string(abi.encodePacked( - "{\n", - ' "EtherFiRedemptionManagerImplementation": "', vm.toString(address(etherFiRedemptionManagerImplementation)), '",\n', - ' "EtherFiRedemptionManagerInstance": "', vm.toString(address(etherFiRedemptionManagerInstance)), '",\n', - ' "EtherFiAdminImplementation": "', vm.toString(address(etherFiAdminImplementation)), '",\n' - //' "EtherFiNodesManagerImplementation": "', vm.toString(address(managerImplementation)), '",\n' - )); - - string memory jsonPart2 = string(abi.encodePacked( - ' "EtherFiRewardsRouterImplementation": "', vm.toString(address(etherFiRewardsRouterImplementation)), '",\n', - ' "LiquidityPoolImplementation": "', vm.toString(address(liquidityPoolImplementation)), '",\n', - ' "WeETHImplementation": "', vm.toString(address(weEthImplementation)), '",\n', - ' "WithdrawRequestNFTImplementation": "', vm.toString(address(withdrawRequestNFTImplementation)), '",\n', - "}" - )); - - string memory jsonContent = string(abi.encodePacked(jsonPart1, jsonPart2)); - vm.writeFile("deployment/deployed-contracts.json", jsonContent); -} - - /////////////////////////// DEPLOYMENT //////////////////////////// - function deployImplementationContracts() internal { - // deploy key - vm.startBroadcast(); - etherFiRedemptionManagerImplementation = new EtherFiRedemptionManager(address(liquidityPoolInstance), address(eETHInstance), address(weETHInstance), address(treasuryAddress), address(roleRegistryInstance)); - UUPSProxy etherFiRedemptionManagerProxy = new UUPSProxy(address(etherFiRedemptionManagerImplementation), ""); - etherFiRedemptionManagerInstance = EtherFiRedemptionManager(payable(etherFiRedemptionManagerProxy)); - - etherFiAdminImplementation = new EtherFiAdmin(); - //managerImplementation = new EtherFiNodesManager(); - etherFiRewardsRouterImplementation = new EtherFiRewardsRouter(address(liquidityPoolInstance), treasuryAddress, address(roleRegistryInstance)); - liquidityPoolImplementation = new LiquidityPool(); - weEthImplementation = new WeETH(); - withdrawRequestNFTImplementation = new WithdrawRequestNFT(treasuryAddress); - vm.stopBroadcast(); - } - - function deployNodesManager() internal { - vm.startBroadcast(); - managerImplementation = new EtherFiNodesManager(); - console2.log('EtherFiNodesManager: ', address(managerImplementation)); - vm.stopBroadcast(); - } - - - - //////////////////////////// ROLE REGISTRY SETUP //////////////////////////// - - function grantRoles() internal { - // tenderly address - address etherfiMultisig = address(0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39); - address etherfiOracleAdmin = address(0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39); - address liquidityPoolAdmin = address(0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39); - address hypernativeEoa = address(0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39); - if(isProd) { - // prod address - etherfiMultisig = address(0x2aCA71020De61bb532008049e1Bd41E451aE8AdC); - etherfiOracleAdmin = address(0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F); - liquidityPoolAdmin = address(0xFa238cB37E58556b23ea45643FFe4Da382162a53); - hypernativeEoa = address(0x9AF1298993DC1f397973C62A5D47a284CF76844D); - } - - vm.startBroadcast(vm.envUint("PRIVATE_KEY")); - roleRegistryInstance.grantRole(liquidityPoolInstance.LIQUIDITY_POOL_ADMIN_ROLE(), liquidityPoolAdmin); - roleRegistryInstance.grantRole(etherFiAdminInstance.ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE(), etherfiOracleAdmin); - roleRegistryInstance.grantRole(etherFiAdminInstance.ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE(), etherfiOracleAdmin); - roleRegistryInstance.grantRole(roleRegistryInstance.PROTOCOL_UNPAUSER(), etherfiMultisig); - roleRegistryInstance.grantRole(roleRegistryInstance.PROTOCOL_PAUSER(), address(etherFiAdminInstance)); - roleRegistryInstance.grantRole(roleRegistryInstance.PROTOCOL_PAUSER(), address(hypernativeEoa)); - roleRegistryInstance.grantRole(roleRegistryInstance.PROTOCOL_PAUSER(), address(etherfiMultisig)); - roleRegistryInstance.grantRole(roleRegistryInstance.PROTOCOL_UNPAUSER(), address(etherFiAdminInstance)); - roleRegistryInstance.grantRole(etherFiRewardsRouterInstance.ETHERFI_REWARDS_ROUTER_ADMIN_ROLE(), address(etherfiMultisig)); - roleRegistryInstance.transferOwnership(address(timelockInstance)); - vm.stopBroadcast(); - } - //transfer ownership and initalize role registry - function completeRoleRegistrySetup() internal { - vm.startBroadcast(address(timelockInstance)); - roleRegistryInstance.acceptOwnership(); - //liquidityPoolInstance.initializeRoleRegistry(address(roleRegistryInstance)); - etherFiAdminInstance.initializeRoleRegistry(address(roleRegistryInstance)); - vm.stopBroadcast(); - - } - - function renamingEtherAdminRoles() internal { - vm.startBroadcast(); - address etherfiMultisig = address(0x2aCA71020De61bb532008049e1Bd41E451aE8AdC); - address oracleEOA = address(0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F); - bytes32 taskManagerRole = keccak256("ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE"); - bytes32 adminRole = keccak256("ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE"); - bytes32 validatorManagerRole = keccak256("ETHERFI_ORACLE_EXECUTOR_VALIDATOR_MANAGER_ROLE"); - RoleRegistry roleRegistry = RoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); - - roleRegistry.revokeRole(adminRole, oracleEOA); - roleRegistry.revokeRole(validatorManagerRole, oracleEOA); - - roleRegistry.grantRole(taskManagerRole, oracleEOA); - roleRegistry.grantRole(taskManagerRole, etherfiMultisig); - roleRegistry.grantRole(adminRole, etherfiMultisig); - vm.stopBroadcast(); - } - - function updateRoleRegistry() internal { - vm.startBroadcast(); - bytes32 taskManagerRole = keccak256("ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE"); - - bytes32 adminRole = keccak256("ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE"); - bytes32 liquidityPoolAdminRole = keccak256("LIQUIDITY_POOL_ADMIN_ROLE"); - bytes32 redemptionManagerAdminRole = keccak256("ETHERFI_REDEMPTION_MANAGER_ADMIN_ROLE"); - bytes32 withdrawRequestNFTAdminRole = keccak256("WITHDRAW_REQUEST_NFT_ADMIN_ROLE"); - - - roleRegistryInstance.revokeRole(liquidityPoolAdminRole, address(oldOperatingTimelockInstance)); - roleRegistryInstance.revokeRole(redemptionManagerAdminRole, address(oldOperatingTimelockInstance)); - roleRegistryInstance.revokeRole(withdrawRequestNFTAdminRole, address(oldOperatingTimelockInstance)); - roleRegistryInstance.revokeRole(adminRole, address(oldOperatingTimelockInstance)); - - roleRegistryInstance.grantRole(liquidityPoolAdminRole, address(operatingTimelockInstance)); - roleRegistryInstance.grantRole(redemptionManagerAdminRole, address(operatingTimelockInstance)); - roleRegistryInstance.grantRole(withdrawRequestNFTAdminRole, address(operatingTimelockInstance)); - roleRegistryInstance.grantRole(adminRole, address(operatingTimelockInstance)); - vm.stopBroadcast(); - } - - //////////////////////////// ADDRESS PROVIDER SETUP //////////////////////////// - - function updateAddressProvider() internal { - address etherfiMultisig = address(0x2aCA71020De61bb532008049e1Bd41E451aE8AdC); - vm.startBroadcast(address(timelockInstance)); - addressProvider.addContract(address(roleRegistryInstance), "RoleRegistry"); - addressProvider.addContract(address(etherFiRewardsRouterInstance), "EtherFiRewardsRouter"); - addressProvider.setOwner(etherfiMultisig); - vm.stopBroadcast(); - } - - //////////////////////////// CONTRACT UPGRADES //////////////////////////// - - function upgradeContracts() internal { - //behind timelock - vm.startBroadcast(address(timelockInstance)); - liquidityPoolInstance.upgradeTo(address(liquidityPoolImplementation)); - etherFiAdminInstance.upgradeTo(address(etherFiAdminImplementation)); - weETHInstance.upgradeTo(address(weEthImplementation)); - etherFiRewardsRouterInstance.upgradeTo(address(etherFiRewardsRouterImplementation)); - vm.stopBroadcast(); - } - - function initContracts() internal { - vm.startBroadcast(); - etherFiRedemptionManagerInstance.initialize(10_00, 30, 1_00, 1000 ether, 0.01157407407 ether); - vm.stopBroadcast(); - } - - //////////////////////////// OPERATING TIMELOCK SETUP //////////////////////////// - - function deployOperatingTimelock() internal { - vm.startBroadcast(); - uint256 minDelay = 60 * 60 * 8; // 8 hours - address[] memory proposers = new address[](1); - proposers[0] = address(0x2aCA71020De61bb532008049e1Bd41E451aE8AdC); - address admin = address(0); - EtherFiTimelock operatingTimelock = new EtherFiTimelock(minDelay, proposers, proposers, admin); - vm.stopBroadcast(); - } - - - function run() public { - init(); - //completeRoleRegistrySetup(); - //grantRoles(); - //renamingEtherAdminRoles(); - - - //deployOperatingTimelock(); - //uncomment after operating timelock is deployed - //oldOperatingTimelockInstance = EtherFiTimelock(payable(address(0x82215f1274356E94543a4D6baC6F7170D8A59F2A))); - //updateRoleRegistry(); - - //uncomment after deploy is done - //EtherFiRedemptionManager etherFiRedemptionManagerInstance = EtherFiRedemptionManager(address(0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761)); - //Timelock operatingTimelock = EtherFiTimelock(); - - //deployImplementationContracts(); - //writeDeployedContractsToFile(); - initContracts(); - //deployNodesManager(); - } - -} diff --git a/script/deploys/EtherFiTimelock.sol b/script/deploys/EtherFiTimelock.sol deleted file mode 100644 index 1500434a0..000000000 --- a/script/deploys/EtherFiTimelock.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/EtherFiTimelock.sol"; - -contract DeployLoyaltyPointsMarketSafeScript is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - /* - // minimum wait time for a timelock tx - uint256 minWaitTime = 2 days; - - // who can propose transactions for the timelock - // TODO: Fill out the addresses we want - address[] memory proposers = new address[](2); - proposers[0] = ; - proposers[1] = admin; - - // who can execute transactions for the timelock - // TODO: Fill out the addresses we want - address[] memory executors = new address[](1); - executors[0] = owner; - - vm.startBroadcast(deployerPrivateKey); - - // Last param is left blank as recommended by OZ documentation - Timelock tl = new EtherFiTimelock(minWaitTime, proposers, executors, address(0x0)); - - addressProvider.addContract(address(tl), "Timelock"); - */ - - vm.stopBroadcast(); - } -} diff --git a/script/specialized/DeployNewNodeOperatorManager.s.sol b/script/specialized/DeployNewNodeOperatorManager.s.sol deleted file mode 100644 index 594e29767..000000000 --- a/script/specialized/DeployNewNodeOperatorManager.s.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/NodeOperatorManager.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -contract DeployNewNodeOperatorManagerScript is Script { - using Strings for string; - - UUPSProxy public nodeOperatorManagerProxy; - - NodeOperatorManager public nodeOperatorManagerImplementation; - NodeOperatorManager public nodeOperatorManagerInstance; - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address phaseOneNodeOperator = addressProvider.getContractAddress("NodeOperatorManager"); - address AuctionManagerProxyAddress = addressProvider.getContractAddress("AuctionManager"); - - address[] memory operators; - bytes[] memory hashes; - uint64[] memory totalKeys; - uint64[] memory keysUsed; - - if(block.chainid == 1) { - // MAINNET - // normal wallets - // 0x83B55dF61cD1181F019DF8e93D46bAFd31806d50 - // 0x78cA32Ac90D7F99225a3B9288D561E0cB3744899 - // 0x7C0576343975A1360CEb91238e7B7985B8d71BF4 - // 0x6916487F0c4553B9EE2f401847B6C58341B76991 - // 0xd624FEfF4b4E77486B544c93A30794CA4B3f10A2 - // 0xB8db44e12eacc48F7C2224a248c8990289556fAe - // 0x00a16D2572573DC9E26e2d267f2270cddAC9218B - // 0x3f95F8f6222F6D97b47122372D60117ab386C48F - - // gnosis - // 0xadf8ca6368227db9ee3a4d03e463bcd90d31de53 - // 0xAfBD66706F90BC56D29c39A260930b34B2757ed8 - // 0xB6C9125584A1A28cCafd31056D4aF29014862536 - - operators = new address[](11); - hashes = new bytes[](11); - totalKeys = new uint64[](11); - keysUsed = new uint64[](11); - - operators[0] = 0x83B55dF61cD1181F019DF8e93D46bAFd31806d50; - operators[1] = 0x78cA32Ac90D7F99225a3B9288D561E0cB3744899; - operators[2] = 0x7C0576343975A1360CEb91238e7B7985B8d71BF4; - operators[3] = 0x6916487F0c4553B9EE2f401847B6C58341B76991; - operators[4] = 0xd624FEfF4b4E77486B544c93A30794CA4B3f10A2; - operators[5] = 0xB8db44e12eacc48F7C2224a248c8990289556fAe; - operators[6] = 0x00a16D2572573DC9E26e2d267f2270cddAC9218B; - operators[7] = 0x3f95F8f6222F6D97b47122372D60117ab386C48F; - operators[8] = 0xaDf8Ca6368227DB9Ee3a4d03e463Bcd90D31DE53; - operators[9] = 0xAfBD66706F90BC56D29c39A260930b34B2757ed8; - operators[10] = 0xB6C9125584A1A28cCafd31056D4aF29014862536; - }else if(block.chainid == 5) { - // GOERLI - operators = new address[](1); - // operators = new address[](2); - operators[0] = 0x99635FA608caaAC2b04720Cb875EA7075A4622d6; - // operators[1] = 0xD0d7F8a5a86d8271ff87ff24145Cf40CEa9F7A39; - - hashes = new bytes[](1); - totalKeys = new uint64[](1); - keysUsed = new uint64[](1); - } - - vm.startBroadcast(deployerPrivateKey); - - for(uint256 i = 0; i < operators.length; i++) { - (uint64 totalKeysLocal, uint64 keysUsedLocal, bytes memory ipfsHash) = NodeOperatorManager(phaseOneNodeOperator).addressToOperatorData(operators[i]); - hashes[i] = ipfsHash; - totalKeys[i] = totalKeysLocal; - keysUsed[i] = keysUsedLocal; - } - - nodeOperatorManagerImplementation = new NodeOperatorManager(); - nodeOperatorManagerProxy = new UUPSProxy(address(nodeOperatorManagerImplementation), ""); - nodeOperatorManagerInstance = NodeOperatorManager(address(nodeOperatorManagerProxy)); - nodeOperatorManagerInstance.initialize(); - - NodeOperatorManager(nodeOperatorManagerInstance).initializeOnUpgrade(operators, hashes, totalKeys, keysUsed); - NodeOperatorManager(nodeOperatorManagerInstance).setAuctionContractAddress(AuctionManagerProxyAddress); - - if (addressProvider.getContractAddress("NodeOperatorManager") != address(nodeOperatorManagerInstance)) { - addressProvider.removeContract("NodeOperatorManager"); - } - addressProvider.addContract(address(nodeOperatorManagerInstance), "NodeOperatorManager"); - - vm.stopBroadcast(); - } -} diff --git a/script/specialized/DeployProxyForMock.s.sol b/script/specialized/DeployProxyForMock.s.sol deleted file mode 100644 index 5ffbdcd94..000000000 --- a/script/specialized/DeployProxyForMock.s.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/UUPSProxy.sol"; - -import "../../src/Liquifier.sol"; - - -contract Deploy is Script { - - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - address impl = address(new Liquifier()); - // address impl = 0x61e2cA79cA3d90Fd1440976a6C9641431b3F296a; - UUPSProxy proxy = new UUPSProxy(impl, ""); - - // Liquifier(payable(address(proxy))).initialize(); - - // mgr.upgradeEtherFiAvsOperator(address(new EtherFiAvsOperator())); - // mgr.upgradeTo(mgr_impl); - // mgr.initializeAvsDirectory(avsDirectory); - - // mgr.instantiateEtherFiAvsOperator(1); - - // EtherFiAvsOperatorsManager mgr = EtherFiAvsOperatorsManager(address(new UUPSProxy(address(new EtherFiAvsOperatorsManager()), ""))); - // mgr.initialize(delegationManager, address(new EtherFiAvsOperator())); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/specialized/RegisterValidator.s.sol b/script/specialized/RegisterValidator.s.sol deleted file mode 100644 index e5f11f027..000000000 --- a/script/specialized/RegisterValidator.s.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; -import "../../src/interfaces/IStakingManager.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; - -import "../../test/common/DepositDataGeneration.sol"; - -contract RegisterValidator is Script { - using Strings for string; - - AddressProvider public addressProvider; - LiquidityPool public liquidityPool; - EtherFiNodesManager public managerInstance; - - bytes32 zeroRoot = 0x0000000000000000000000000000000000000000000000000000000000000000; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - liquidityPool = LiquidityPool(payable(addressProvider.getContractAddress("LiquidityPool"))); - managerInstance = EtherFiNodesManager(payable(addressProvider.getContractAddress("EtherFiNodesManager"))); - - DepositDataGeneration depGen = new DepositDataGeneration(); - - uint256[] memory _validatorIds = new uint256[](1); - IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](_validatorIds.length); - bytes32[] memory depositDataRootsForApproval = new bytes32[](_validatorIds.length); - bytes[] memory sig = new bytes[](_validatorIds.length); - bytes[] memory pubKey = new bytes[](_validatorIds.length); - - // 1 ETH - // [{"pubkey": "ad85894db60881bcee956116beae6bc6934d7eca8317dc3084adf665be426a21a1855b5196a7515fd791bf0b6e3727c5", "withdrawal_credentials": "010000000000000000000000b2de3e0380d7229dc9e717342ed042e54eaaa620", "amount": 1000000000, "signature": "848169dd090590d31b4eee23f031b3f0df4bbe097e1ea3f65e4c40b5da412f326adfed4b1c45b83831eb692e906ba1ec0f0cacfc714f1c89ad74eca71be43247965e71ead2dc7625410d69c434fa88895ef2c9e135bb187b04eb62bc87539dcf", "deposit_message_root": "12f106b1c020239e8c2857c5bd355ca4c9fec33b2140367064a7df5a11861f44", "deposit_data_root": "aa08a4fe6761812fb0ae067b98e30239a2aefb609c271edf4efe84f7a5742ff3", "fork_version": "01017000", "network_name": "holesky", "deposit_cli_version": "2.7.0"}] - - // 31 ETH - // [{"pubkey": "ad85894db60881bcee956116beae6bc6934d7eca8317dc3084adf665be426a21a1855b5196a7515fd791bf0b6e3727c5", "withdrawal_credentials": "010000000000000000000000b2de3e0380d7229dc9e717342ed042e54eaaa620", "amount": 31000000000, "signature": "accff50c4fde87119058770c8fff833b6e052e6a689bc639e3a20f57ebff15315ff7cb15e7c0de2856794e459023862d0539bb1b22d3938730f7ec0e0504dcf12b63ba9f9ca2cceef4914f997d90df3f604c0c3a99706a038ef35ed9b3d166d2", "deposit_message_root": "81566688bde7909767507f4daa38f48aba6858d0f786fa4fb4082129ec86fbe0", "deposit_data_root": "262b9668947937a8135e8f6bd9cf27b07891a4c5d5d2dd9d01ada07eff77ccf1", "fork_version": "01017000", "network_name": "holesky", "deposit_cli_version": "2.7.0"}] - - _validatorIds[0] = 1; - - address etherFiNode = managerInstance.etherFiNodeFromId(_validatorIds[0]); - - depositDataArray[0] = IStakingManager.DepositData({ - publicKey: hex"ad85894db60881bcee956116beae6bc6934d7eca8317dc3084adf665be426a21a1855b5196a7515fd791bf0b6e3727c5", - signature: hex"848169dd090590d31b4eee23f031b3f0df4bbe097e1ea3f65e4c40b5da412f326adfed4b1c45b83831eb692e906ba1ec0f0cacfc714f1c89ad74eca71be43247965e71ead2dc7625410d69c434fa88895ef2c9e135bb187b04eb62bc87539dcf", - depositDataRoot: hex"aa08a4fe6761812fb0ae067b98e30239a2aefb609c271edf4efe84f7a5742ff3", - ipfsHashForEncryptedValidatorKey: "SYKO_TEST" - }); - - depositDataRootsForApproval[0] = 0x262b9668947937a8135e8f6bd9cf27b07891a4c5d5d2dd9d01ada07eff77ccf1; - - sig[0] = hex"accff50c4fde87119058770c8fff833b6e052e6a689bc639e3a20f57ebff15315ff7cb15e7c0de2856794e459023862d0539bb1b22d3938730f7ec0e0504dcf12b63ba9f9ca2cceef4914f997d90df3f604c0c3a99706a038ef35ed9b3d166d2"; - - - vm.startBroadcast(deployerPrivateKey); - - // bytes32 _depositRoot, - // uint256[] calldata _validatorIds, - // IStakingManager.DepositData[] calldata _registerValidatorDepositData, - // bytes32[] calldata _depositDataRootApproval, - // bytes[] calldata _signaturesForApprovalDeposit - liquidityPool.batchRegister(zeroRoot, _validatorIds, depositDataArray, depositDataRootsForApproval, sig); - - vm.stopBroadcast(); - } -} diff --git a/script/specialized/TransferOwnership.s.sol b/script/specialized/TransferOwnership.s.sol deleted file mode 100644 index 43a976003..000000000 --- a/script/specialized/TransferOwnership.s.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/MembershipManager.sol"; -import "../../src/MembershipNFT.sol"; -import "../../src/WeETH.sol"; -import "../../src/EETH.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/NFTExchange.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/archive/RegulationsManager.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/archive/ProtocolRevenueManager.sol"; -import "../../src/TNFT.sol"; -import "../../src/BNFT.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/StakingManager.sol"; - -contract TransferOwnership is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - address auctionManager = addressProvider.getContractAddress("AuctionManager"); - address stakingManager = addressProvider.getContractAddress("StakingManager"); - address protocolRevenueManager = addressProvider.getContractAddress("ProtocolRevenueManager"); - address tnft = addressProvider.getContractAddress("TNFT"); - address bnft = addressProvider.getContractAddress("BNFT"); - address etherfiNodesManager = addressProvider.getContractAddress("EtherFiNodesManager"); - address membershipManager = addressProvider.getContractAddress("MembershipManager"); - address membershipNFT = addressProvider.getContractAddress("MembershipNFT"); - address weETH = addressProvider.getContractAddress("WeETH"); - address eETH = addressProvider.getContractAddress("EETH"); - address nftExchange = addressProvider.getContractAddress("NFTExchange"); - address liquidityPool = addressProvider.getContractAddress("LiquidityPool"); - address regulationsManager = addressProvider.getContractAddress("RegulationsManager"); - - address owner = vm.envAddress("GNOSIS"); - - MembershipManager(payable(membershipManager)).transferOwnership(owner); - MembershipNFT(membershipNFT).transferOwnership(owner); - WeETH(weETH).transferOwnership(owner); - EETH(eETH).transferOwnership(owner); - NFTExchange(nftExchange).transferOwnership(owner); - LiquidityPool(payable(liquidityPool)).transferOwnership(owner); - RegulationsManager(regulationsManager).transferOwnership(owner); - AuctionManager(auctionManager).transferOwnership(owner); - StakingManager(stakingManager).transferOwnership(owner); - ProtocolRevenueManager(payable(protocolRevenueManager)).transferOwnership(owner); - TNFT(tnft).transferOwnership(owner); - BNFT(bnft).transferOwnership(owner); - EtherFiNodesManager(payable(etherfiNodesManager)).transferOwnership(owner); - addressProvider.setOwner(owner); - - vm.stopBroadcast(); - } -} diff --git a/script/specialized/UpdateAdminScripts.s.sol b/script/specialized/UpdateAdminScripts.s.sol deleted file mode 100644 index d8b6e92b0..000000000 --- a/script/specialized/UpdateAdminScripts.s.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/archive/ProtocolRevenueManager.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/TNFT.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/MembershipManager.sol"; -import "../../src/MembershipNFT.sol"; -import "../../src/StakingManager.sol"; -import "../../src/NFTExchange.sol"; -import "../../src/archive/RegulationsManager.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/WithdrawRequestNFT.sol"; - -contract UpdateAdmins is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - vm.startBroadcast(deployerPrivateKey); - - address stakingManager = addressProvider.getContractAddress("StakingManager"); - address etherFiNodesManager = addressProvider.getContractAddress("EtherFiNodesManager"); - address protocolRevenueManager = addressProvider.getContractAddress("ProtocolRevenueManager"); - address auctionManager = addressProvider.getContractAddress("AuctionManager"); - address liquidityPool = addressProvider.getContractAddress("LiquidityPool"); - address regulationsManager = addressProvider.getContractAddress("RegulationsManager"); - address membershipNFT = addressProvider.getContractAddress("MembershipNFT"); - address membershipManager = addressProvider.getContractAddress("MembershipManager"); - address nftExchange = addressProvider.getContractAddress("NFTExchange"); - address withdrawRequestNFTAddress = addressProvider.getContractAddress("WithdrawRequestNFT"); - - address admin = vm.envAddress("ADMIN"); - - //EtherFiNodesManager(payable(etherFiNodesManager)).updateAdmin(admin, true); - // ProtocolRevenueManager(payable(protocolRevenueManager)).updateAdmin(admin); // DEPRECATED - AuctionManager(auctionManager).updateAdmin(admin, true); - //StakingManager(stakingManager).updateAdmin(admin, true); - // LiquidityPool(payable(liquidityPool)).updateAdmin(admin, true); - // RegulationsManager(regulationsManager).updateAdmin(admin, true); - MembershipManager(payable(membershipManager)).updateAdmin(admin, true); - MembershipNFT(membershipNFT).updateAdmin(admin, true); - // NFTExchange(nftExchange).updateAdmin(admin); // Not in the scope of Phase 2 upgrade - //WithdrawRequestNFT(payable(withdrawRequestNFTAddress)).updateAdmin(admin, true); - - vm.stopBroadcast(); - } -} diff --git a/script/specialized/weEth_withdrawal_v2.s.sol b/script/specialized/weEth_withdrawal_v2.s.sol deleted file mode 100644 index cc856cfd6..000000000 --- a/script/specialized/weEth_withdrawal_v2.s.sol +++ /dev/null @@ -1,131 +0,0 @@ -// // SPDX-License-Identifier: UNLICENSED -// pragma solidity ^0.8.13; - -// import "forge-std/Script.sol"; -// import "test/TestSetup.sol"; - -// import "src/helpers/AddressProvider.sol"; -// import "../GnosisHelpers.sol"; - -// contract Upgrade is Script, GnosisHelpers { - -// address public etherFiTimelock = 0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761; -// address public addressProviderAddress = 0x8487c5F8550E3C3e7734Fe7DCF77DB2B72E4A848; -// AddressProvider public addressProvider = AddressProvider(addressProviderAddress); -// address public roleRegistry = 0x1d3Af47C1607A2EF33033693A9989D1d1013BB50; -// address public treasury = 0x0c83EAe1FE72c390A02E426572854931EefF93BA; -// address public pauser = 0x9AF1298993DC1f397973C62A5D47a284CF76844D; - -// WithdrawRequestNFT withdrawRequestNFTInstance; -// LiquidityPool liquidityPoolInstance; -// EtherFiRedemptionManager etherFiRedemptionManagerInstance; - -// function run() external { -// // uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - -// withdrawRequestNFTInstance = WithdrawRequestNFT(payable(addressProvider.getContractAddress("WithdrawRequestNFT"))); -// liquidityPoolInstance = LiquidityPool(payable(addressProvider.getContractAddress("LiquidityPool"))); - -// vm.startBroadcast(); - -// deploy_upgrade(); -// // agg(); -// // handle_remainder(); - -// vm.stopBroadcast(); -// } - -// function deploy_upgrade() internal { -// UUPSProxy etherFiRedemptionManagerProxy = new UUPSProxy(address(new EtherFiRedemptionManager( -// addressProvider.getContractAddress("LiquidityPool"), -// addressProvider.getContractAddress("EETH"), -// addressProvider.getContractAddress("WeETH"), -// treasury, -// roleRegistry)), -// "" -// ); -// etherFiRedemptionManagerInstance = EtherFiRedemptionManager(payable(etherFiRedemptionManagerProxy)); -// etherFiRedemptionManagerInstance.initialize(10_00, 1_00, 2_30, 500 ether, 0.005787037037 ether); -// // abi.encodeWithSelector( -// // EtherFiRedemptionManager.initialize.selector, -// // 10_00, 1_00, 2_30, 500 ether, 0.005787037037 ether // 10% fee split to treasury, 1% exit fee, 1% low watermark -// // ) - -// address withdrawRequestNFTImpl = address(new WithdrawRequestNFT(treasury)); -// address liquidityPoolImpl = address(new LiquidityPool()); - -// predecessor = 0x0000000000000000000000000000000000000000000000000000000000000000; -// salt = keccak256(abi.encodePacked(address(withdrawRequestNFTInstance), address(liquidityPoolInstance), block.number)); -// delay = 3 days; - -// address[] memory targets = new address[](2); -// targets[0] = address(withdrawRequestNFTInstance); -// targets[1] = address(liquidityPoolInstance); - -// uint256[] memory values = new uint256[](2); - -// bytes[] memory payloads = new bytes[](2); -// bytes memory upgradeWithdrawRequestNFTUpgradeData = abi.encodeWithSignature( -// "upgradeToAndCall(address,bytes)", -// withdrawRequestNFTImpl, -// abi.encodeWithSelector(WithdrawRequestNFT.initializeOnUpgrade.selector, pauser, 50_00) // 50% fee split to treasury -// ); -// bytes memory upgradeLiquidityPoolUpgradeData = abi.encodeWithSignature("upgradeToAndCall(address,bytes)", liquidityPoolImpl, abi.encodeWithSelector(LiquidityPool.initializeOnUpgradeWithRedemptionManager.selector, address(etherFiRedemptionManagerInstance))); -// payloads[0] = upgradeWithdrawRequestNFTUpgradeData; -// payloads[1] = upgradeLiquidityPoolUpgradeData; - -// string memory scheduleGnosisTx = _getGnosisHeader("1"); -// string memory scheduleUpgrade = iToHex(abi.encodeWithSignature("scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256)", targets, values, payloads, predecessor, salt, delay)); -// scheduleGnosisTx = string(abi.encodePacked(scheduleGnosisTx, _getGnosisTransaction(addressToHex(timelock), scheduleUpgrade, true))); - -// string memory path = "./operations/20250128_upgrade_instant_withdrawal_schedule.json"; -// vm.writeFile(path, scheduleGnosisTx); - -// string memory executeGnosisTx = _getGnosisHeader("1"); -// string memory executeUpgrade = iToHex(abi.encodeWithSignature("executeBatch(address[],uint256[],bytes[],bytes32,bytes32)", targets, values, payloads, predecessor, salt)); -// executeGnosisTx = string(abi.encodePacked(executeGnosisTx, _getGnosisTransaction(addressToHex(timelock), executeUpgrade, true))); - -// path = "./operations/20250128_upgrade_instant_withdrawal_execute.json"; -// vm.writeFile(path, executeGnosisTx); -// } - -// function agg() internal { -// uint256 numToScanPerTx = 1024; -// uint256 cnt = (withdrawRequestNFTInstance.nextRequestId() / numToScanPerTx) + 1; -// console.log(cnt); -// for (uint256 i = 0; i < cnt; i++) { -// withdrawRequestNFTInstance.aggregateSumEEthShareAmount(numToScanPerTx); -// } -// } - -// function handle_remainder() internal { -// withdrawRequestNFTInstance.updateAdmin(msg.sender, true); -// withdrawRequestNFTInstance.unPauseContract(); -// uint256 remainder = withdrawRequestNFTInstance.getEEthRemainderAmount(); -// console.log(remainder); -// withdrawRequestNFTInstance.handleRemainder(remainder); -// } -// } - -// contract TestUpgrade is Test, Upgrade { -// function setUp() public { -// vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); - -// withdrawRequestNFTInstance = WithdrawRequestNFT(payable(addressProvider.getContractAddress("WithdrawRequestNFT"))); -// liquidityPoolInstance = LiquidityPool(payable(addressProvider.getContractAddress("LiquidityPool"))); -// etherFiRedemptionManagerInstance = EtherFiRedemptionManager(payable(0xD5fd46F4df70a63d60a8563CaD0444Fcc25dcE7f)); -// } - -// function test_UpgradeWeETHInstantWithdrawal() public { -// string memory path = "./operations/20250128_upgrade_instant_withdrawal_schedule.json"; -// executeGnosisTransactionBundle(path, 0xcdd57D11476c22d265722F68390b036f3DA48c21); - -// vm.warp(block.timestamp + 3 days + 1); - -// path = "./operations/20250128_upgrade_instant_withdrawal_execute.json"; -// executeGnosisTransactionBundle(path, 0xcdd57D11476c22d265722F68390b036f3DA48c21); - -// assert(withdrawRequestNFTInstance.pauser() == pauser); -// assert(address(liquidityPoolInstance.etherFiRedemptionManager()) == address(etherFiRedemptionManagerInstance)); -// } -// } diff --git a/script/upgrade_v1.5.sh b/script/upgrade_v1.5.sh deleted file mode 100644 index 5e4d8312d..000000000 --- a/script/upgrade_v1.5.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -e # Exit immediately if any command fails - -network=$1 - -targets=( - upgrade-${network}-staking-manager - upgrade-${network}-auction-manager - upgrade-${network}-etherfi-node - upgrade-${network}-bnft - upgrade-${network}-tnft - upgrade-${network}-eeth - upgrade-${network}-etherfi_nodes_manager - upgrade-${network}-liquidity-pool - upgrade-${network}-membership-manager - upgrade-${network}-membership-nft - upgrade-${network}-weeth -) - -for target in "${targets[@]}"; do - make "$target" -done - diff --git a/script/upgrades/AuctionManagerUpgradeScript.s.sol b/script/upgrades/AuctionManagerUpgradeScript.s.sol deleted file mode 100644 index 30e71d67f..000000000 --- a/script/upgrades/AuctionManagerUpgradeScript.s.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/AuctionManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract AuctionManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address AuctionManagerProxyAddress = addressProvider.getContractAddress("AuctionManager"); - address membershipManagerProxyAddress = addressProvider.getContractAddress("MembershipManager"); - address etherFiAdminAddress = addressProvider.getContractAddress("EtherFiAdmin"); - address nodeOperatorManagerAddress = addressProvider.getContractAddress("NodeOperatorManager"); - - vm.startBroadcast(deployerPrivateKey); - - AuctionManager AuctionManagerInstance = AuctionManager(AuctionManagerProxyAddress); - AuctionManager AuctionManagerImplementation = new AuctionManager(); - - AuctionManagerInstance.upgradeTo(address(AuctionManagerImplementation)); - AuctionManagerInstance.initializeOnUpgrade(membershipManagerProxyAddress, 0.15 ether, etherFiAdminAddress, nodeOperatorManagerAddress); - - require(AuctionManagerInstance.admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/BNFTUpgradeScript.s.sol b/script/upgrades/BNFTUpgradeScript.s.sol deleted file mode 100644 index 8ef29e25d..000000000 --- a/script/upgrades/BNFTUpgradeScript.s.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/BNFT.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract BNFTUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address BNFTProxyAddress = addressProvider.getContractAddress("BNFT"); - - vm.startBroadcast(deployerPrivateKey); - - BNFT BNFTInstance = BNFT(BNFTProxyAddress); - BNFT BNFTV2Implementation = new BNFT(); - - BNFTInstance.upgradeTo(address(BNFTV2Implementation)); - - // phase 2 upgrade initialization - address etherFiNodesManagerAddress = addressProvider.getContractAddress("EtherFiNodesManager"); - assert(etherFiNodesManagerAddress != address(0)); - BNFTInstance.initializeOnUpgrade(etherFiNodesManagerAddress); - - vm.stopBroadcast(); - } -} diff --git a/script/upgrades/BucketRateLimiter.s.sol b/script/upgrades/BucketRateLimiter.s.sol deleted file mode 100644 index d5f1821dd..000000000 --- a/script/upgrades/BucketRateLimiter.s.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -import "../../src/BucketRateLimiter.sol"; -import "../../src/UUPSProxy.sol"; - -interface IL2SyncPool { - function setRateLimiter(address rateLimiter) external; -} - -contract Upgrade is Script { - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address deployer = vm.addr(deployerPrivateKey); - address limiter; - - if (block.chainid == 59144) limiter = 0x3A19866D5E0fAE0Ce19Adda617f9d2B9fD5a3975; // LINEA - else if (block.chainid == 81457) limiter = 0x6f257089bF046a02751b60767871953F3899652e; // BLAST - else if (block.chainid == 34443) limiter = 0x95F1138837F1158726003251B32ecd8732c76781; // MODE - else revert("Unsupported chain id"); - - vm.startBroadcast(deployerPrivateKey); - - BucketRateLimiter impl = new BucketRateLimiter(); - - BucketRateLimiter(limiter).upgradeTo(address(impl)); - - vm.stopBroadcast(); - } - -} \ No newline at end of file diff --git a/script/upgrades/EETHUpgradeScript.s.sol b/script/upgrades/EETHUpgradeScript.s.sol deleted file mode 100644 index a0e939ad5..000000000 --- a/script/upgrades/EETHUpgradeScript.s.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/EETH.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract EETHUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address EETHProxyAddress = addressProvider.getContractAddress("EETH"); - - vm.startBroadcast(deployerPrivateKey); - - EETH EETHInstance = EETH(EETHProxyAddress); - EETH EETHV2Implementation = new EETH(); - - uint256 totalShares = EETHInstance.totalShares(); - - EETHInstance.upgradeTo(address(EETHV2Implementation)); - - require(totalShares == EETHInstance.totalShares(), "EETHUpgrade: totalShares mismatch"); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/EtherFiAdminUpgradeScript.s.sol b/script/upgrades/EtherFiAdminUpgradeScript.s.sol deleted file mode 100644 index 635ed41ea..000000000 --- a/script/upgrades/EtherFiAdminUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/EtherFiAdmin.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract EtherFiAdminUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address proxyAddress = addressProvider.getContractAddress("EtherFiAdmin"); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiAdmin instance = EtherFiAdmin(proxyAddress); - EtherFiAdmin v2Implementation = new EtherFiAdmin(); - - //instance.upgradeTo(address(v2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/EtherFiNodeScript.s.sol b/script/upgrades/EtherFiNodeScript.s.sol deleted file mode 100644 index 5b8fb9315..000000000 --- a/script/upgrades/EtherFiNodeScript.s.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/StakingManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract EtherFiNodeUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address stakingManagerProxyAddress = addressProvider.getContractAddress("StakingManager"); - - revert("FILL IN ADDRESSES"); - address eigenPodManager; - address delegationManager; - address liquidityPool; - address etherFiNodesManager; - - StakingManager stakingManager = StakingManager(stakingManagerProxyAddress); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiNode etherFiNode = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager); - stakingManager.upgradeEtherFiNode(address(etherFiNode)); - - vm.stopBroadcast(); - } -} diff --git a/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol b/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol deleted file mode 100644 index e4a451d7c..000000000 --- a/script/upgrades/EtherFiNodesManagerUpgradeScript.s.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract EtherFiNodesManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address EtherFiNodesManagerProxyAddress = addressProvider.getContractAddress("EtherFiNodesManager"); - address etherFiAdminAddress = addressProvider.getContractAddress("EtherFiAdmin"); - - address eigenPodManager; - address delayedWithdrawalRouter; - address delegationManager; - uint8 maxEigenlayerWithdrawals = 5; - - if (block.chainid == 1) { - eigenPodManager = 0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338; - delayedWithdrawalRouter = 0x7Fe7E9CC0F274d2435AD5d56D5fa73E47F6A23D8; - delegationManager = 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A; - } else if (block.chainid == 5) { - eigenPodManager = 0xa286b84C96aF280a49Fe1F40B9627C2A2827df41; - delayedWithdrawalRouter = 0x89581561f1F98584F88b0d57c2180fb89225388f; - delegationManager = 0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8; - } else if (block.chainid == 17000) { - } else { - require(false); - } - - //uint64 numberOfValidators = IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).numberOfValidators(); - //address treasury = IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).treasuryContract(); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiNodesManager EtherFiNodesManagerInstance = EtherFiNodesManager(payable(EtherFiNodesManagerProxyAddress)); - EtherFiNodesManagerInstance.upgradeTo(address(new EtherFiNodesManager())); - - // EtherFiNodesManagerInstance.initializeOnUpgrade(etherFiAdminAddress, eigenPodManager, delayedWithdrawalRouter, maxEigenlayerWithdrawals); - // EtherFiNodesManagerInstance.initializeOnUpgrade2(delegationManager); - - //require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).numberOfValidators() == numberOfValidators); - //require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).treasuryContract() == treasury); - // require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).maxEigenlayerWithdrawals() == maxEigenlayerWithdrawals); - // require(IEtherFiNodesManager(EtherFiNodesManagerProxyAddress).admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); - - vm.stopBroadcast(); - } -} diff --git a/script/upgrades/EtherFiOracleUpgradeScript.s.sol b/script/upgrades/EtherFiOracleUpgradeScript.s.sol deleted file mode 100644 index 8edf33e8b..000000000 --- a/script/upgrades/EtherFiOracleUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/EtherFiOracle.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract EtherFiOracleUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address proxyAddress = addressProvider.getContractAddress("EtherFiOracle"); - - vm.startBroadcast(deployerPrivateKey); - - EtherFiOracle oracleInstance = EtherFiOracle(proxyAddress); - EtherFiOracle v2Implementation = new EtherFiOracle(); - - //oracleInstance.upgradeTo(address(v2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/LiquidityPoolUpgradeScript.s.sol b/script/upgrades/LiquidityPoolUpgradeScript.s.sol deleted file mode 100644 index 7420c1509..000000000 --- a/script/upgrades/LiquidityPoolUpgradeScript.s.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/LiquidityPool.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract LiquidityPoolUpgrade is Script { - - AddressProvider public addressProvider; - - address public stakingManager; - uint256 public getTotalPooledEther; - uint32 public numPendingDeposits; - uint128 public totalValueOutOfLp; - uint128 public totalValueInLp; - - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address LiquidityPoolProxyAddress = addressProvider.getContractAddress("LiquidityPool"); - address etherFiAdminAddress = addressProvider.getContractAddress("EtherFiAdmin"); - address withdrawRequestNFTAddress = addressProvider.getContractAddress("WithdrawRequestNFT"); - address auctionManager = addressProvider.getContractAddress("AuctionManager"); - address liquifier = addressProvider.getContractAddress("Liquifier"); - - vm.startBroadcast(deployerPrivateKey); - - LiquidityPool LiquidityPoolInstance = LiquidityPool(payable(LiquidityPoolProxyAddress)); - - // Copy the states - stakingManager = address(LiquidityPoolInstance.stakingManager()); - getTotalPooledEther = LiquidityPoolInstance.getTotalPooledEther(); - numPendingDeposits = LiquidityPoolInstance.numPendingDeposits(); - totalValueOutOfLp = LiquidityPoolInstance.totalValueOutOfLp(); - totalValueInLp = LiquidityPoolInstance.totalValueInLp(); - - LiquidityPool LiquidityPoolV2Implementation = new LiquidityPool(); - - //require(LiquidityPoolInstance.numPendingDeposits() == 0, "numPendingDeposits should be 0"); - - //LiquidityPoolInstance.upgradeTo(address(LiquidityPoolV2Implementation)); - - // // Phase 2 - // //Ensure these inputs are correct - // //First parameter = the scheduling period in seconds we want to set - // //Second parameter = the number of validators ETH source of funds currently has spun up - // //Third parameter = the number of validators ETHER_FAN source of funds currently has spun up - // if (block.chainid == 1) { - // LiquidityPoolInstance.initializeOnUpgrade(auctionManager, liquifier); - // } else if (block.chainid == 5) { - // LiquidityPoolInstance.initializeOnUpgrade(auctionManager, liquifier); - // } else { - // require(false, "chain is wrong"); - // } - - // // Validate the states - // require(stakingManager == address(LiquidityPoolInstance.stakingManager()), "stakingManager"); - // require(getTotalPooledEther == LiquidityPoolInstance.getTotalPooledEther(), "getTotalPooledEther"); - // require(numPendingDeposits == LiquidityPoolInstance.numPendingDeposits(), "numPendingDeposits"); - // require(totalValueOutOfLp == LiquidityPoolInstance.totalValueOutOfLp(), "totalValueOutOfLp"); - // require(totalValueInLp == LiquidityPoolInstance.totalValueInLp(), "totalValueInLp"); - // require(LiquidityPoolInstance.admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); - - vm.stopBroadcast(); - } -} diff --git a/script/upgrades/Liquifier.s.sol b/script/upgrades/Liquifier.s.sol deleted file mode 100644 index 64e2961b9..000000000 --- a/script/upgrades/Liquifier.s.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "@openzeppelin/contracts/utils/Strings.sol"; -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import "../../src/Liquifier.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/EtherFiTimelock.sol"; -import "../../src/StakingManager.sol"; - -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; - - -contract Upgrade is Script { - using Strings for string; - - AddressProvider public addressProvider; - - address addressProviderAddress; - - Liquifier liquifier; - - EtherFiTimelock timelock; - - event TimelockTransaction(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay); - - function getSelector(bytes memory _f) public pure returns (bytes4) { - return bytes4(keccak256(_f)); - } - - function genUpgradeTo(address _target, address _newImplementation) public pure returns (bytes memory) { - bytes4 functionSelector = getSelector("upgradeTo(address)"); - return abi.encodeWithSelector(functionSelector, _newImplementation); - } - - function run() external { - address deployer = vm.envAddress("DEPLOYER"); - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - retrieve_contract_addresses(); - - vm.startBroadcast(deployerPrivateKey); - - Liquifier LiquifierNewImpl = new Liquifier(); - - address l1syncpool; - if (block.chainid == 1) { - l1syncpool = 0xD789870beA40D056A4d26055d0bEFcC8755DA146; - - uint256 minDelay = timelock.getMinDelay(); - - emit TimelockTransaction(address(liquifier), 0, genUpgradeTo(address(liquifier), address(LiquifierNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(liquifier), 0, abi.encodeWithSelector(getSelector("initializeL1SyncPool(address)"), l1syncpool), bytes32(0), bytes32(0), minDelay); - - // Dummy ETH Blast Dummy ETH : 0x83998e169026136760bE6AF93e776C2F352D4b28 - // Dummy ETH Linea Dummy ETH : 0x61Ff310aC15a517A846DA08ac9f9abf2A0f9A2bf - // Dummy ETH Optimism Dummy ETH : 0xABC12e7C1137961B75551b26932E6F0FA9F04Ae8 - // Dummy ETH Base Dummy ETH : 0x0295E0CE709723FB25A28b8f67C54a488BA5aE46 - // Dummy ETH Mode Dummy ETH : 0xDc400f3da3ea5Df0B7B6C127aE2e54CE55644CF3 - address[] memory dummyETHs = new address[](5); - dummyETHs[0] = 0x83998e169026136760bE6AF93e776C2F352D4b28; - dummyETHs[1] = 0x61Ff310aC15a517A846DA08ac9f9abf2A0f9A2bf; - dummyETHs[2] = 0xABC12e7C1137961B75551b26932E6F0FA9F04Ae8; - dummyETHs[3] = 0x0295E0CE709723FB25A28b8f67C54a488BA5aE46; - dummyETHs[4] = 0xDc400f3da3ea5Df0B7B6C127aE2e54CE55644CF3; - - for (uint i = 0; i < dummyETHs.length; i++) { - emit TimelockTransaction(address(liquifier), 0, abi.encodeWithSelector(getSelector("registerToken(address _token, address _target, bool _isWhitelisted, uint16 _discountInBasisPoints, uint32 _timeBoundCapInEther, uint32 _totalCapInEther, bool _isL2Eth)"), dummyETHs[i], address(0), true, 0, 1, 1, true), bytes32(0), bytes32(0), minDelay); - } - - for (uint i = 0; i < dummyETHs.length; i++) { - emit TimelockTransaction(address(liquifier), 0, abi.encodeWithSelector(getSelector("registerToken(address _token, address _target, bool _isWhitelisted, uint16 _discountInBasisPoints, uint32 _timeBoundCapInEther, uint32 _totalCapInEther, bool _isL2Eth)"), dummyETHs[i], address(0), true, 0, 30_000, 30_000, true), bytes32(0), bytes32(0), minDelay); - } - - } else if (block.chainid == 17000) { - // l1syncpool = 0x0; - - require(deployer == liquifier.owner(), "Only the owner can upgrade the contract"); - - liquifier.upgradeTo(address(LiquifierNewImpl)); - // liquifier.initializeL1SyncPool(l1syncpool); - } - - vm.stopBroadcast(); - } - - function retrieve_contract_addresses() internal { - liquifier = Liquifier(payable(addressProvider.getContractAddress("Liquifier"))); - timelock = EtherFiTimelock(payable(addressProvider.getContractAddress("EtherFiTimelock"))); - } - -} diff --git a/script/upgrades/MembershipManagerUpgradeScript.s.sol b/script/upgrades/MembershipManagerUpgradeScript.s.sol deleted file mode 100644 index 34615b5f6..000000000 --- a/script/upgrades/MembershipManagerUpgradeScript.s.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/MembershipManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract MembershipManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address membershipManagerProxy = addressProvider.getContractAddress("MembershipManager"); - address etherFiAdminAddress = addressProvider.getContractAddress("EtherFiAdmin"); - assert(membershipManagerProxy != address(0)); - assert(etherFiAdminAddress != address(0)); - - - vm.startBroadcast(deployerPrivateKey); - - MembershipManager membershipManagerInstance = MembershipManager(payable(membershipManagerProxy)); - MembershipManager membershipManagerV2Implementation = new MembershipManager(); - - address treasury = membershipManagerInstance.treasury(); - uint16 pointsGrowthRate = membershipManagerInstance.pointsGrowthRate(); - (uint256 mintFeeAmount, uint256 burnFeeAmount, uint256 upgradeFeeAmount) = membershipManagerInstance.getFees(); - uint32 topUpCooltimePeriod = membershipManagerInstance.topUpCooltimePeriod(); - - membershipManagerInstance.upgradeTo(address(membershipManagerV2Implementation)); - - // 0.3 ether is the treshold for ether.fan rewards distribution - // 183 days (6 months) is required for burn fee waiver - membershipManagerInstance.initializeOnUpgrade(etherFiAdminAddress, 0.3 ether, 183); - - (uint256 _mintFeeAmount, uint256 _burnFeeAmount, uint256 _upgradeFeeAmount) = membershipManagerInstance.getFees(); - - require(membershipManagerInstance.treasury() == treasury, "Treasury address should not change"); - require(membershipManagerInstance.pointsGrowthRate() == pointsGrowthRate, "Points growth rate should not change"); - require(membershipManagerInstance.topUpCooltimePeriod() == topUpCooltimePeriod, "Top up cooltime period should not change"); - require(membershipManagerInstance.admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); - require(_mintFeeAmount == mintFeeAmount, "Mint fee amount should not change"); - require(_burnFeeAmount == burnFeeAmount, "Burn fee amount should not change"); - require(_upgradeFeeAmount == upgradeFeeAmount, "Upgrade fee amount should not change"); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/MembershipNFTUpgradeScript.s.sol b/script/upgrades/MembershipNFTUpgradeScript.s.sol deleted file mode 100644 index fa8a027bc..000000000 --- a/script/upgrades/MembershipNFTUpgradeScript.s.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/MembershipNFT.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract MembershipNFTUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address membershipNFTProxy = addressProvider.getContractAddress("MembershipNFT"); - address liquidityPool = addressProvider.getContractAddress("LiquidityPool"); - - require(membershipNFTProxy != address(0), "MembershipNFTUpgrade: membershipNFTProxy is zero address"); - require(liquidityPool != address(0), "MembershipNFTUpgrade: liquidityPool is zero address"); - - vm.startBroadcast(deployerPrivateKey); - - MembershipNFT membershipNFTInstance = MembershipNFT(payable(membershipNFTProxy)); - MembershipNFT membershipNFTV2Implementation = new MembershipNFT(); - - uint32 nextMintTokenId = membershipNFTInstance.nextMintTokenId(); - uint32 maxTokenId = membershipNFTInstance.maxTokenId(); - bytes32 eapMerkleRoot = membershipNFTInstance.eapMerkleRoot(); - - membershipNFTInstance.upgradeTo(address(membershipNFTV2Implementation)); - membershipNFTInstance.initializeOnUpgrade(liquidityPool); - - require(membershipNFTInstance.nextMintTokenId() == nextMintTokenId, "MembershipNFTUpgrade: nextMintTokenId mismatch"); - require(membershipNFTInstance.maxTokenId() == maxTokenId, "MembershipNFTUpgrade: maxTokenId mismatch"); - require(membershipNFTInstance.eapMerkleRoot() == eapMerkleRoot, "MembershipNFTUpgrade: eapMerkleRoot mismatch"); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/MultipleValidatorsPerSafe.s.sol b/script/upgrades/MultipleValidatorsPerSafe.s.sol deleted file mode 100644 index 031f441f0..000000000 --- a/script/upgrades/MultipleValidatorsPerSafe.s.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "@openzeppelin/contracts/utils/Strings.sol"; -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import "../../src/LiquidityPool.sol"; -import "../../src/StakingManager.sol"; -import "../../src/TNFT.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/EtherFiTimelock.sol"; - -import "../../src/AuctionManager.sol"; -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; - -contract MultipleValidatorsPerSafe is Script { - using Strings for string; - - AddressProvider public addressProvider; - - address addressProviderAddress; - - EtherFiNodesManager nodesManager; - LiquidityPool liquidityPool; - StakingManager stakingManager; - TNFT tnft; - - EtherFiTimelock timelock; - - event TimelockTransaction(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay); - - function getSelector(bytes memory _f) public pure returns (bytes4) { - return bytes4(keccak256(_f)); - } - - function genUpgradeTo(address _target, address _newImplementation) public pure returns (bytes memory) { - bytes4 functionSelector = getSelector("upgradeTo(address)"); - return abi.encodeWithSelector(functionSelector, _newImplementation); - } - - function run() external { - address deployer = vm.envAddress("DEPLOYER"); - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - retrieve_contract_addresses(); - - vm.startBroadcast(deployerPrivateKey); - - LiquidityPool LiquidityPoolNewImpl = new LiquidityPool(); - StakingManager StakingManagerNewImpl = new StakingManager(); - TNFT TNFTNewImpl = new TNFT(); - EtherFiNodesManager EtherFiNodesManagerNewImpl = new EtherFiNodesManager(); - EtherFiNode EtherFiNodeNewImpl = new EtherFiNode(address(0x0),address(0x0),address(0x0),address(0x0)); - - address el_delegationManager; - if (block.chainid == 1) { - el_delegationManager = 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A; - - uint256 minDelay = timelock.getMinDelay(); - - emit TimelockTransaction(address(liquidityPool), 0, genUpgradeTo(address(liquidityPool), address(LiquidityPoolNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(stakingManager), 0, genUpgradeTo(address(stakingManager), address(StakingManagerNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(tnft), 0, genUpgradeTo(address(tnft), address(TNFTNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(nodesManager), 0, genUpgradeTo(address(nodesManager), address(EtherFiNodesManagerNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(stakingManager), 0, abi.encodeWithSelector(getSelector("upgradeEtherFiNode(address)"), address(EtherFiNodeNewImpl)), bytes32(0), bytes32(0), minDelay); - - // Perform the upgrades manually by the timelock - } else if (block.chainid == 5) { - el_delegationManager = 0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8; - - require(deployer == liquidityPool.owner(), "Only the owner can upgrade the contract"); - require(deployer == stakingManager.owner(), "Only the owner can upgrade the contract"); - require(deployer == tnft.owner(), "Only the owner can upgrade the contract"); - require(deployer == nodesManager.owner(), "Only the owner can upgrade the contract"); - - liquidityPool.upgradeTo(address(LiquidityPoolNewImpl)); - stakingManager.upgradeTo(address(StakingManagerNewImpl)); - tnft.upgradeTo(address(TNFTNewImpl)); - nodesManager.upgradeTo(address(EtherFiNodesManagerNewImpl)); - - stakingManager.upgradeEtherFiNode(address(EtherFiNodeNewImpl)); - } - - vm.stopBroadcast(); - } - - function retrieve_contract_addresses() internal { - stakingManager = StakingManager(addressProvider.getContractAddress("StakingManager")); - nodesManager = EtherFiNodesManager(payable(addressProvider.getContractAddress("EtherFiNodesManager"))); - tnft = TNFT(addressProvider.getContractAddress("TNFT")); - liquidityPool = LiquidityPool(payable(addressProvider.getContractAddress("LiquidityPool"))); - timelock = EtherFiTimelock(payable(addressProvider.getContractAddress("EtherFiTimelock"))); - } - -} diff --git a/script/upgrades/NFTExchangeUpgradeScript.s.sol b/script/upgrades/NFTExchangeUpgradeScript.s.sol deleted file mode 100644 index b10655e79..000000000 --- a/script/upgrades/NFTExchangeUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/NFTExchange.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract NFTExchangeUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address NFTExchangeProxyAddress = addressProvider.getContractAddress("NFTExchange"); - - vm.startBroadcast(deployerPrivateKey); - - NFTExchange NFTExchangeInstance = NFTExchange(NFTExchangeProxyAddress); - NFTExchange NFTExchangeV2Implementation = new NFTExchange(); - - // NFTExchangeInstance.upgradeTo(address(NFTExchangeV2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/NodeOperatorManagerUpgradeScript.s.sol b/script/upgrades/NodeOperatorManagerUpgradeScript.s.sol deleted file mode 100644 index 6b172cfa6..000000000 --- a/script/upgrades/NodeOperatorManagerUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/NodeOperatorManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract NodeOperatorManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address NodeOperatorManagerProxyAddress = addressProvider.getContractAddress("NodeOperatorManager"); - - vm.startBroadcast(deployerPrivateKey); - - NodeOperatorManager NodeOperatorManagerInstance = NodeOperatorManager(NodeOperatorManagerProxyAddress); - NodeOperatorManager NodeOperatorManagerV2Implementation = new NodeOperatorManager(); - - NodeOperatorManagerInstance.upgradeTo(address(NodeOperatorManagerV2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/ProtocolRevenueManagerUpgradeScript.s.sol b/script/upgrades/ProtocolRevenueManagerUpgradeScript.s.sol deleted file mode 100644 index 5e629a723..000000000 --- a/script/upgrades/ProtocolRevenueManagerUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/archive/ProtocolRevenueManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract ProtocolRevenueManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address ProtocolRevenueManagerProxyAddress = addressProvider.getContractAddress("ProtocolRevenueManager"); - - vm.startBroadcast(deployerPrivateKey); - - ProtocolRevenueManager ProtocolRevenueManagerInstance = ProtocolRevenueManager(payable(ProtocolRevenueManagerProxyAddress)); - ProtocolRevenueManager ProtocolRevenueManagerV2Implementation = new ProtocolRevenueManager(); - - ProtocolRevenueManagerInstance.upgradeTo(address(ProtocolRevenueManagerV2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/RegulationsManagerUpgradeScript.s.sol b/script/upgrades/RegulationsManagerUpgradeScript.s.sol deleted file mode 100644 index 1741dd26d..000000000 --- a/script/upgrades/RegulationsManagerUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/archive/RegulationsManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract RegulationsManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address RegulationsManagerProxyAddress = addressProvider.getContractAddress("RegulationsManager"); - - vm.startBroadcast(deployerPrivateKey); - - RegulationsManager RegulationsManagerInstance = RegulationsManager(RegulationsManagerProxyAddress); - RegulationsManager RegulationsManagerV2Implementation = new RegulationsManager(); - - RegulationsManagerInstance.upgradeTo(address(RegulationsManagerV2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/StakingManagerUpgradeScript.s.sol b/script/upgrades/StakingManagerUpgradeScript.s.sol deleted file mode 100644 index 26ba9cf02..000000000 --- a/script/upgrades/StakingManagerUpgradeScript.s.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/StakingManager.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract StakingManagerUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address stakingManagerProxyAddress = addressProvider.getContractAddress("StakingManager"); - address etherFiAdminAddress = addressProvider.getContractAddress("EtherFiAdmin"); - address nodeOperatorManagerAddress = addressProvider.getContractAddress("NodeOperatorManager"); - - require(stakingManagerProxyAddress != address(0), "StakingManager address not set"); - require(etherFiAdminAddress != address(0), "EtherFiAdmin address not set"); - require(nodeOperatorManagerAddress != address(0), "NodeOperatorManager address not set"); - - vm.startBroadcast(deployerPrivateKey); - - StakingManager stakingManagerInstance = StakingManager(stakingManagerProxyAddress); - StakingManager stakingManagerV2Implementation = new StakingManager(); - - stakingManagerInstance.upgradeTo(address(stakingManagerV2Implementation)); - //stakingManagerInstance.initializeOnUpgrade(nodeOperatorManagerAddress, etherFiAdminAddress); - - //require(stakingManagerInstance.admins(etherFiAdminAddress), "EtherFiAdmin should be an admin"); - - vm.stopBroadcast(); - } -} diff --git a/script/upgrades/TNFTUpgradeScript.s.sol b/script/upgrades/TNFTUpgradeScript.s.sol deleted file mode 100644 index 2c5973522..000000000 --- a/script/upgrades/TNFTUpgradeScript.s.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/TNFT.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract TNFTUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address TNFTProxyAddress = addressProvider.getContractAddress("TNFT"); - - vm.startBroadcast(deployerPrivateKey); - - TNFT TNFTInstance = TNFT(TNFTProxyAddress); - TNFT TNFTV2Implementation = new TNFT(); - - TNFTInstance.upgradeTo(address(TNFTV2Implementation)); - - // phase 2 upgrade initialization - address etherFiNodesManagerAddress = addressProvider.getContractAddress("EtherFiNodesManager"); - assert(etherFiNodesManagerAddress != address(0)); - TNFTInstance.initializeOnUpgrade(etherFiNodesManagerAddress); - - vm.stopBroadcast(); - } -} diff --git a/script/upgrades/UpgradeForEigenLayerM2.s.sol b/script/upgrades/UpgradeForEigenLayerM2.s.sol deleted file mode 100644 index 139aceaf5..000000000 --- a/script/upgrades/UpgradeForEigenLayerM2.s.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "@openzeppelin/contracts/utils/Strings.sol"; -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import "../../src/Liquifier.sol"; -import "../../src/EtherFiNodesManager.sol"; -import "../../src/EtherFiNode.sol"; -import "../../src/EtherFiTimelock.sol"; -import "../../src/StakingManager.sol"; - -import "../../src/helpers/AddressProvider.sol"; -import "../../src/UUPSProxy.sol"; - - -contract UpgradeForEigenLayerM2 is Script { - using Strings for string; - - AddressProvider public addressProvider; - - address addressProviderAddress; - - StakingManager stakingManager; - EtherFiNodesManager nodesManager; - Liquifier liquifier; - - EtherFiTimelock timelock; - - event TimelockTransaction(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay); - - function getSelector(bytes memory _f) public pure returns (bytes4) { - return bytes4(keccak256(_f)); - } - - function genUpgradeTo(address _target, address _newImplementation) public pure returns (bytes memory) { - bytes4 functionSelector = getSelector("upgradeTo(address)"); - return abi.encodeWithSelector(functionSelector, _newImplementation); - } - - function run() external { - address deployer = vm.envAddress("DEPLOYER"); - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - retrieve_contract_addresses(); - - vm.startBroadcast(deployerPrivateKey); - - // Liquifier LiquifierNewImpl = new Liquifier(); - EtherFiNodesManager EtherFiNodesManagerNewImpl = new EtherFiNodesManager(); - EtherFiNode EtherFiNodeNewImpl = new EtherFiNode(address(0x0),address(0x0),address(0x0),address(0x0)); - - address el_delegationManager; - address pancakeRouter; - address el_admin; - if (block.chainid == 1) { - el_delegationManager = 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A; - pancakeRouter = 0x1b81D678ffb9C0263b24A97847620C99d213eB14; - el_admin = 0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F; - - uint256 minDelay = timelock.getMinDelay(); - - // emit TimelockTransaction(address(liquifier), 0, genUpgradeTo(address(liquifier), address(LiquifierNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(stakingManager), 0, abi.encodeWithSelector(getSelector("upgradeEtherFiNode(address)"), address(EtherFiNodeNewImpl)), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(nodesManager), 0, genUpgradeTo(address(nodesManager), address(EtherFiNodesManagerNewImpl)), bytes32(0), bytes32(0), minDelay); - - // emit TimelockTransaction(address(liquifier), 0, abi.encodeWithSelector(getSelector("initializeOnUpgrade(address,address)"), el_delegationManager, pancakeRouter), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(nodesManager), 0, abi.encodeWithSelector(getSelector("initializeOnUpgrade2(address)"), el_delegationManager), bytes32(0), bytes32(0), minDelay); - emit TimelockTransaction(address(nodesManager), 0, abi.encodeWithSelector(getSelector("updateEigenLayerOperatingAdmin(address,bool)"), el_admin, true), bytes32(0), bytes32(0), minDelay); - - // Perform the upgrades manually by the timelock - } else if (block.chainid == 17000) { - el_delegationManager = 0x1b7b8F6b258f95Cf9596EabB9aa18B62940Eb0a8; - pancakeRouter = address(0); // not live in Holesky - el_admin = deployer; - - // require(deployer == liquifier.owner(), "Only the owner can upgrade the contract"); - require(deployer == stakingManager.owner(), "Only the owner can upgrade the contract"); - require(deployer == nodesManager.owner(), "Only the owner can upgrade the contract"); - - // liquifier.upgradeTo(address(LiquifierNewImpl)); - nodesManager.upgradeTo(address(EtherFiNodesManagerNewImpl)); - stakingManager.upgradeEtherFiNode(address(EtherFiNodeNewImpl)); - - // liquifier.initializeOnUpgrade(el_delegationManager, pancakeRouter); - // nodesManager.initializeOnUpgrade2(el_delegationManager); - // nodesManager.updateEigenLayerOperatingAdmin(el_admin, true); - } - - vm.stopBroadcast(); - } - - function retrieve_contract_addresses() internal { - stakingManager = StakingManager(addressProvider.getContractAddress("StakingManager")); - nodesManager = EtherFiNodesManager(payable(addressProvider.getContractAddress("EtherFiNodesManager"))); - liquifier = Liquifier(payable(addressProvider.getContractAddress("Liquifier"))); - timelock = EtherFiTimelock(payable(addressProvider.getContractAddress("EtherFiTimelock"))); - } - -} diff --git a/script/upgrades/WeETHUpgradeScript.s.sol b/script/upgrades/WeETHUpgradeScript.s.sol deleted file mode 100644 index c9c82c85f..000000000 --- a/script/upgrades/WeETHUpgradeScript.s.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/WeETH.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract WeEthUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address weEthProxyAddress = addressProvider.getContractAddress("WeETH"); - - vm.startBroadcast(deployerPrivateKey); - - WeETH weEthInstance = WeETH(weEthProxyAddress); - WeETH weEthV2Implementation = new WeETH(); - - uint256 totalSupply = weEthInstance.totalSupply(); - - weEthInstance.upgradeTo(address(weEthV2Implementation)); - - require(totalSupply == weEthInstance.totalSupply(), "WeEthUpgrade: totalSupply mismatch"); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/script/upgrades/WithdrawRequestNFTUpgradeScript.s.sol b/script/upgrades/WithdrawRequestNFTUpgradeScript.s.sol deleted file mode 100644 index 115251475..000000000 --- a/script/upgrades/WithdrawRequestNFTUpgradeScript.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import "../../src/WithdrawRequestNFT.sol"; -import "../../src/helpers/AddressProvider.sol"; - -contract WithdrawRequestNFTUpgrade is Script { - - AddressProvider public addressProvider; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - - address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY"); - addressProvider = AddressProvider(addressProviderAddress); - - address proxyAddress = addressProvider.getContractAddress("WithdrawRequestNFT"); - - vm.startBroadcast(deployerPrivateKey); - - WithdrawRequestNFT oracleInstance = WithdrawRequestNFT(proxyAddress); - WithdrawRequestNFT v2Implementation = new WithdrawRequestNFT(address(0)); - - oracleInstance.upgradeTo(address(v2Implementation)); - - vm.stopBroadcast(); - } -} \ No newline at end of file diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index 5bf4ed81e..f5f88a840 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -46,7 +46,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { uint32 public lastHandledReportRefSlot; uint32 public lastHandledReportRefBlock; - uint32 public numValidatorsToSpinUp; + uint32 public __gap_0; int32 public acceptableRebaseAprInBps; @@ -193,8 +193,6 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { require(blockForNextReportToProcess() == _report.refBlockFrom, "EtherFiAdmin: report has wrong `refBlockFrom`"); require(current_slot >= postReportWaitTimeInSlots + etherFiOracle.getConsensusSlot(reportHash), "EtherFiAdmin: report is too fresh"); - numValidatorsToSpinUp = _report.numValidatorsToSpinUp; - _handleAccruedRewards(_report); _handleProtocolFees(_report); _handleValidators(reportHash, _report); @@ -310,9 +308,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { function _handleValidators(bytes32 _reportHash, IEtherFiOracle.OracleReport calldata _report) internal { uint32[] memory emptyTimestamps = new uint32[](0); _enqueueValidatorManagementTask(_reportHash, _report.validatorsToApprove, emptyTimestamps, TaskType.ValidatorApproval); - _enqueueValidatorManagementTask(_reportHash, _report.liquidityPoolValidatorsToExit, emptyTimestamps, TaskType.SendExitRequests); _enqueueValidatorManagementTask(_reportHash, _report.exitedValidators, _report.exitedValidatorsExitTimestamps, TaskType.ProcessNodeExit); - _enqueueValidatorManagementTask(_reportHash, _report.slashedValidators, emptyTimestamps, TaskType.MarkBeingSlashed); } function _handleWithdrawals(IEtherFiOracle.OracleReport calldata _report) internal { diff --git a/src/EtherFiOracle.sol b/src/EtherFiOracle.sol index 6057f10d5..827bee799 100644 --- a/src/EtherFiOracle.sol +++ b/src/EtherFiOracle.sol @@ -211,10 +211,8 @@ contract EtherFiOracle is Initializable, OwnableUpgradeable, PausableUpgradeable bytes32 chunk2 = keccak256( abi.encode( _report.validatorsToApprove, - _report.liquidityPoolValidatorsToExit, _report.exitedValidators, - _report.exitedValidatorsExitTimestamps, - _report.slashedValidators + _report.exitedValidatorsExitTimestamps ) ); @@ -222,10 +220,7 @@ contract EtherFiOracle is Initializable, OwnableUpgradeable, PausableUpgradeable abi.encode( _report.withdrawalRequestsToInvalidate, _report.lastFinalizedWithdrawalRequestId, - _report.eEthTargetAllocationWeight, - _report.etherFanTargetAllocationWeight, - _report.finalizedWithdrawalAmount, - _report.numValidatorsToSpinUp + _report.finalizedWithdrawalAmount ) ); return keccak256(abi.encode(chunk1, chunk2, chunk3)); diff --git a/src/LoyaltyPointsMarketSafe.sol b/src/archive/LoyaltyPointsMarketSafe.sol similarity index 100% rename from src/LoyaltyPointsMarketSafe.sol rename to src/archive/LoyaltyPointsMarketSafe.sol diff --git a/src/NFTExchange.sol b/src/archive/NFTExchange.sol similarity index 97% rename from src/NFTExchange.sol rename to src/archive/NFTExchange.sol index ffecb370b..e89d08e1c 100644 --- a/src/NFTExchange.sol +++ b/src/archive/NFTExchange.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import "./interfaces/IMembershipNFT.sol"; -import "./interfaces/IEtherFiNodesManager.sol"; -import "./interfaces/IEtherFiNode.sol"; +import "../interfaces/IMembershipNFT.sol"; +import "../interfaces/IEtherFiNodesManager.sol"; +import "../interfaces/IEtherFiNode.sol"; import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; diff --git a/src/Treasury.sol b/src/archive/Treasury.sol similarity index 96% rename from src/Treasury.sol rename to src/archive/Treasury.sol index f53dec657..97c32d574 100644 --- a/src/Treasury.sol +++ b/src/archive/Treasury.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import "@openzeppelin/contracts/access/Ownable.sol"; -import "./interfaces/ITreasury.sol"; +import "../interfaces/ITreasury.sol"; contract Treasury is ITreasury, Ownable { diff --git a/src/interfaces/IEtherFiAdmin.sol b/src/interfaces/IEtherFiAdmin.sol index 5af6683ac..d7720d68a 100644 --- a/src/interfaces/IEtherFiAdmin.sol +++ b/src/interfaces/IEtherFiAdmin.sol @@ -5,5 +5,4 @@ interface IEtherFiAdmin { function lastHandledReportRefSlot() external view returns (uint32); function lastHandledReportRefBlock() external view returns (uint32); function lastAdminExecutionBlock() external view returns (uint32); - function numValidatorsToSpinUp() external view returns (uint32); } diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index f98ea3b6a..ccef5cb2a 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import "./IEtherFiNodesManager.sol"; import "../eigenlayer-interfaces/IDelegationManager.sol"; +import "../eigenlayer-interfaces/IEigenPod.sol"; interface IEtherFiNode { diff --git a/src/interfaces/IEtherFiOracle.sol b/src/interfaces/IEtherFiOracle.sol index 98367e599..0dc6e6d85 100644 --- a/src/interfaces/IEtherFiOracle.sol +++ b/src/interfaces/IEtherFiOracle.sol @@ -11,16 +11,11 @@ interface IEtherFiOracle { int128 accruedRewards; int128 protocolFees; uint256[] validatorsToApprove; - uint256[] liquidityPoolValidatorsToExit; uint256[] exitedValidators; uint32[] exitedValidatorsExitTimestamps; - uint256[] slashedValidators; uint256[] withdrawalRequestsToInvalidate; uint32 lastFinalizedWithdrawalRequestId; - uint32 eEthTargetAllocationWeight; - uint32 etherFanTargetAllocationWeight; uint128 finalizedWithdrawalAmount; - uint32 numValidatorsToSpinUp; } struct CommitteeMemberState { diff --git a/test/EtherFiAdminUpgrade.t.sol b/test/EtherFiAdminUpgrade.t.sol index ac8d1585b..44697a232 100644 --- a/test/EtherFiAdminUpgrade.t.sol +++ b/test/EtherFiAdminUpgrade.t.sol @@ -72,16 +72,11 @@ contract EtherFiAdminUpgradeTest is TestSetup { accruedRewards: 12865299762487754752, protocolFees: 1438401262268165688, validatorsToApprove: new uint256[](10), - liquidityPoolValidatorsToExit: new uint256[](0), exitedValidators: new uint256[](0), exitedValidatorsExitTimestamps: new uint32[](0), - slashedValidators: new uint256[](0), withdrawalRequestsToInvalidate: new uint256[](0), lastFinalizedWithdrawalRequestId: 57907, - eEthTargetAllocationWeight: 0, - etherFanTargetAllocationWeight: 0, - finalizedWithdrawalAmount: 608440514420670619423, - numValidatorsToSpinUp: 100 + finalizedWithdrawalAmount: 608440514420670619423 }); uint256 startId = 80920; for (uint i = 0; i < 10; i++) { diff --git a/test/EtherFiOracle.t.sol b/test/EtherFiOracle.t.sol index 4aded73d2..468851897 100644 --- a/test/EtherFiOracle.t.sol +++ b/test/EtherFiOracle.t.sol @@ -425,13 +425,6 @@ contract EtherFiOracleTest is TestSetup { function test_admin_task() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - // When we want Oracle to allow to spin up one validator - report.numValidatorsToSpinUp = 1; - _executeAdminTasks(report); - assertEq(etherFiAdminInstance.numValidatorsToSpinUp(), 1); - - report.eEthTargetAllocationWeight = 80; - report.etherFanTargetAllocationWeight = 20; _executeAdminTasks(report); } diff --git a/test/L2Constants.sol b/test/L2Constants.sol deleted file mode 100644 index cc2706c24..000000000 --- a/test/L2Constants.sol +++ /dev/null @@ -1,262 +0,0 @@ - -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - - -contract L2Constants { - struct ConfigPerL2 { - string name; - string rpc_url; - - // https://docs.layerzero.network/v2/developers/evm/technical-reference/endpoints - uint32 l2Eid; - address l2Endpoint; - - address l1Messenger; - address l2Messenger; - - address l2Oft; - address l2SyncPool; - address l2SyncPoolRateLimiter; - address l2ExchagneRateProvider; - - address l2PriceOracle; - uint32 l2PriceOracleHeartBeat; - - address l2ContractControllerSafe; - - address l1dummyToken; - address l1Receiver; - - // ProxyAdmin - address l2Oft_ProxyAdmin; - address l2SyncPool_ProxyAdmin; - address l2ExchagneRateProvider_ProxyAdmin; - address l1dummyToken_ProxyAdmin; - address l1Receiver_ProxyAdmin; - - // DVN - // - https://docs.layerzero.network/v2/developers/evm/technical-reference/executor-addresses - // - https://docs.layerzero.network/v2/developers/evm/technical-reference/messagelibs - // - https://docs.layerzero.network/v2/developers/evm/technical-reference/dvn-addresses - address send302; - address receive302; - address lzExecutor; - address[2] lzDvn; - } - - function getL2Config() public returns (ConfigPerL2 memory) { - if (block.chainid == 59144) return LINEA; - else if (block.chainid == 81457) return BLAST; - else if (block.chainid == 34443) return MODE; - else if (block.chainid == 56) return BNB; - else if (block.chainid == 8453) return BASE; - else revert("Unsupported chain id"); - } - - - string l1RpcUrl = "https://mainnet.gateway.tenderly.co"; - uint32 l1Eid = 30101; - address l1Endpoint = 0x1a44076050125825900e736c501f859c50fE728c; - address ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - address l1ContractController = 0x2aCA71020De61bb532008049e1Bd41E451aE8AdC; - - address l1SyncPoolAddress = 0xD789870beA40D056A4d26055d0bEFcC8755DA146; - address l1OftAdapter = 0xFE7fe01F8B9A76803aF3750144C2715D9bcf7D0D; - address l1Send302 = 0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1; - address l1Receive302 = 0xc02Ab410f0734EFa3F14628780e6e695156024C2; - address[2] l1Dvn = [0x589dEDbD617e0CBcB916A9223F4d1300c294236b, 0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5]; - - address l1SyncPool_ProxyAdmin = 0xDBf6bE120D4dc72f01534673a1223182D9F6261D; - - - ConfigPerL2 BLAST = ConfigPerL2({ - name: "BLAST", - rpc_url: "https://rpc.blast.io", - - l2Eid: 30243, - l2Endpoint: 0x1a44076050125825900e736c501f859c50fE728c, - - l1Messenger: 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0, - l2Messenger: 0x4200000000000000000000000000000000000007, - - l2Oft: 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A, - l2SyncPool: 0x52c4221Cb805479954CDE5accfF8C4DcaF96623B, - l2SyncPoolRateLimiter: 0x6f257089bF046a02751b60767871953F3899652e, - l2ExchagneRateProvider: 0xc42853c0C6624F42fcB8219aCeb67Ad188087DCB, - l2PriceOracle: 0xcD96262Df56127f298b452FA40759632868A472a, - l2PriceOracleHeartBeat: 24 hours, - l2ContractControllerSafe: 0xa4822d7d24747e6A1BAA171944585bad4434f2D5, - l1dummyToken: 0x83998e169026136760bE6AF93e776C2F352D4b28, - l1Receiver: 0x27e120C518a339c3d8b665E56c4503DF785985c2, - - l2Oft_ProxyAdmin: 0x2F6f3cc4a275C7951FB79199F01eD82421eDFb68, - l2SyncPool_ProxyAdmin: 0x8f732e00d6CF2302775Df16d4110f0f7ad3780f9, - l2ExchagneRateProvider_ProxyAdmin: 0xb4224E552016ba5D35b44608Cd4578fF7FCB6e82, - l1dummyToken_ProxyAdmin: 0x96a226ad7c14870502f9794fB481EE102E595fFa, - l1Receiver_ProxyAdmin: 0x70F38913d95987829577788dF9a6A0741dA16543, - - send302: 0xc1B621b18187F74c8F6D52a6F709Dd2780C09821, - receive302: 0x377530cdA84DFb2673bF4d145DCF0C4D7fdcB5b6, - lzExecutor: 0x4208D6E27538189bB48E603D6123A94b8Abe0A0b, - lzDvn: [0xc097ab8CD7b053326DFe9fB3E3a31a0CCe3B526f, 0xDd7B5E1dB4AaFd5C8EC3b764eFB8ed265Aa5445B] - }); - - ConfigPerL2 MODE = ConfigPerL2({ - name: "MODE", - rpc_url: "https://mainnet.mode.network", - - l2Eid: 30260, - l2Endpoint: 0x1a44076050125825900e736c501f859c50fE728c, - - l1Messenger: 0x95bDCA6c8EdEB69C98Bd5bd17660BaCef1298A6f, - l2Messenger: 0xC0d3c0d3c0D3c0D3C0d3C0D3C0D3c0d3c0d30007, - - l2Oft: 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A, - l2SyncPool: 0x52c4221Cb805479954CDE5accfF8C4DcaF96623B, - l2SyncPoolRateLimiter: 0x95F1138837F1158726003251B32ecd8732c76781, - l2ExchagneRateProvider: 0xc42853c0C6624F42fcB8219aCeb67Ad188087DCB, - l2ContractControllerSafe: 0xa4822d7d24747e6A1BAA171944585bad4434f2D5, - l2PriceOracle: 0x7C1DAAE7BB0688C9bfE3A918A4224041c7177256, - l2PriceOracleHeartBeat: 6 hours, - l1dummyToken: 0xDc400f3da3ea5Df0B7B6C127aE2e54CE55644CF3, - l1Receiver: 0xC8Ad0949f33F02730cFf3b96E7F067E83De1696f, - - l2Oft_ProxyAdmin: 0x2F6f3cc4a275C7951FB79199F01eD82421eDFb68, - l2SyncPool_ProxyAdmin: 0x8f732e00d6CF2302775Df16d4110f0f7ad3780f9, - l2ExchagneRateProvider_ProxyAdmin: 0xb4224E552016ba5D35b44608Cd4578fF7FCB6e82, - l1dummyToken_ProxyAdmin: 0x59a5518aCE8e3d60C740503639B94bD86F7CEDF0, - l1Receiver_ProxyAdmin: 0xe85e493d78a4444bf5fC4A2E415AF530aEad6dd5, - - send302: 0x2367325334447C5E1E0f1b3a6fB947b262F58312, - receive302: 0xc1B621b18187F74c8F6D52a6F709Dd2780C09821, - lzExecutor: 0x4208D6E27538189bB48E603D6123A94b8Abe0A0b, - lzDvn: [0xcd37CA043f8479064e10635020c65FfC005d36f6, 0xce8358bc28dd8296Ce8cAF1CD2b44787abd65887] - }); - - ConfigPerL2 LINEA = ConfigPerL2({ - name: "LINEA", - rpc_url: "https://1rpc.io/linea", - - l2Eid: 30183, - l2Endpoint: 0x1a44076050125825900e736c501f859c50fE728c, - - l1Messenger: 0xd19d4B5d358258f05D7B411E21A1460D11B0876F, - l2Messenger: 0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec, - - l2Oft: 0x1Bf74C010E6320bab11e2e5A532b5AC15e0b8aA6, - l2SyncPool: 0x823106E745A62D0C2FC4d27644c62aDE946D9CCa, - l2SyncPoolRateLimiter: 0x3A19866D5E0fAE0Ce19Adda617f9d2B9fD5a3975, - l2ExchagneRateProvider: 0x241a91F095B2020890Bc8518bea168C195518344, - l2PriceOracle: 0x100c8e61aB3BeA812A42976199Fc3daFbcDD7272, - l2PriceOracleHeartBeat: 6 hours, - l2ContractControllerSafe: 0xe4ff196Cd755566845D3dEBB1e2bD34123807eBc, - l1dummyToken: 0x61Ff310aC15a517A846DA08ac9f9abf2A0f9A2bf, - l1Receiver: 0x6F149F8bf1CB0245e70171c9972059C22294aa35, - - l2Oft_ProxyAdmin: 0xE21B7A5e4c15156180a76F4747313a3485fC4163, - l2SyncPool_ProxyAdmin: 0x0F88DB75B9011B909b67c498cdcc1C0FD2308444, - l2ExchagneRateProvider_ProxyAdmin: 0x40B6a79A93f9596Fe6155c9a56f79482d831178f, - l1dummyToken_ProxyAdmin: 0xaa249a01a3D73611a27B735130Ab77fd6b0f5a3e, - l1Receiver_ProxyAdmin: 0x7c6261c2eD0Bd5e532B45C4E553e633cBF34063f, - - send302: 0x32042142DD551b4EbE17B6FEd53131dd4b4eEa06, - receive302: 0xE22ED54177CE1148C557de74E4873619e6c6b205, - lzExecutor: 0x0408804C5dcD9796F22558464E6fE5bDdF16A7c7, - lzDvn: [0x129Ee430Cb2Ff2708CCADDBDb408a88Fe4FFd480, 0xDd7B5E1dB4AaFd5C8EC3b764eFB8ed265Aa5445B] - }); - - ConfigPerL2 BNB = ConfigPerL2({ - name: "BNB", - rpc_url: "https://bsc-dataseed1.binance.org/", - - l2Eid: 30102, - l2Endpoint: 0x1a44076050125825900e736c501f859c50fE728c, - - l1Messenger: address(0), - l2Messenger: address(0), - - l2Oft: 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A, - l2SyncPool: address(0), - l2SyncPoolRateLimiter: address(0), - l2ExchagneRateProvider: address(0), - l2ContractControllerSafe: 0xD568c4D42147224a701A14468bEC9E9bccF571F5, - l2PriceOracle: address(0), - l2PriceOracleHeartBeat: 0, - l1dummyToken: address(0), - l1Receiver: address(0), - - l2Oft_ProxyAdmin: 0x2F6f3cc4a275C7951FB79199F01eD82421eDFb68, - l2SyncPool_ProxyAdmin: address(0), - l2ExchagneRateProvider_ProxyAdmin: address(0), - l1dummyToken_ProxyAdmin: address(0), - l1Receiver_ProxyAdmin: address(0), - - send302: 0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE, - receive302: 0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1, - lzExecutor: 0x3ebD570ed38B1b3b4BC886999fcF507e9D584859, - lzDvn: [0x31F748a368a893Bdb5aBB67ec95F232507601A73, 0xfD6865c841c2d64565562fCc7e05e619A30615f0] - }); - - ConfigPerL2 BASE = ConfigPerL2({ - name: "Base", - rpc_url: "https://base.drpc.org/", - - l2Eid: 30184, - l2Endpoint: 0x1a44076050125825900e736c501f859c50fE728c, - - l1Messenger: 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa, - l2Messenger: 0x4200000000000000000000000000000000000007, - - l2Oft: 0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A, - l2SyncPool: 0xc38e046dFDAdf15f7F56853674242888301208a5, - l2SyncPoolRateLimiter: 0xe6e0fe0C3Ac45d1FE71AF7853007467eE89e1e67, - l2ExchagneRateProvider: 0xF2c5519c634796B73dE90c7Dc27B4fEd560fC3ca, - l2ContractControllerSafe: 0x7a00657a45420044bc526B90Ad667aFfaee0A868, - l2PriceOracle: 0x35e9D7001819Ea3B39Da906aE6b06A62cfe2c181, // CL - l2PriceOracleHeartBeat: 24 hours, - l1dummyToken: 0x0295E0CE709723FB25A28b8f67C54a488BA5aE46, - l1Receiver: 0x8963C96186bd05995AdaA9E1fda25B7181CCBc37, - - l2Oft_ProxyAdmin: 0x2F6f3cc4a275C7951FB79199F01eD82421eDFb68, - l2SyncPool_ProxyAdmin: 0x9055c6EF7Cb895D550368fE7B38Be346E7eA9eE6, - l2ExchagneRateProvider_ProxyAdmin: 0x7Ce9B21e86778Bb6D06CF107f1C154cB5635598f, - l1dummyToken_ProxyAdmin: 0x915B16B555872A084B3512169b1F1dC089C3ca9A, - l1Receiver_ProxyAdmin: 0x0df531532cf25156b1fe91232F41B4c9AA514125, - - send302: 0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2, - receive302: 0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf, - lzExecutor: 0x2CCA08ae69E0C44b18a57Ab2A87644234dAebaE4, - lzDvn: [0x9e059a54699a285714207b43B055483E78FAac25, 0xcd37CA043f8479064e10635020c65FfC005d36f6] - }); - - - // ConfigPerL2 ExampleL2 = ConfigPerL2({ - // name: "BNB", - // rpc_url: "https://bsc-dataseed1.binance.org/", - - // l2Eid: 0, - // l2Endpoint: address(0), - - // l2Oft: address(0), - // l2SyncPool: address(0), - // l2SyncPoolRateLimiter: address(0), - // l2ExchagneRateProvider: address(0), - // l2ContractControllerSafe: address(0), - // l2PriceOracle: address(0), - // l2PriceOracleHeartBeat: 0, - // l1dummyToken: address(0), - // l1Receiver: address(0), - - // l2Oft_ProxyAdmin: 0x2F6f3cc4a275C7951FB79199F01eD82421eDFb68, - // l2SyncPool_ProxyAdmin: address(0), - // l2ExchagneRateProvider_ProxyAdmin: address(0), - // l1dummyToken_ProxyAdmin: address(0), - // l1Receiver_ProxyAdmin: address(0), - - // send302: address(0), - // receive302: address(0), - // lzExecutor: address(0), - // lzDvn: [address(0), address(0)] - // }); -} \ No newline at end of file diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index e143f4bfa..0dba434da 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -265,7 +265,6 @@ contract LiquidityPoolTest is TestSetup { vm.deal(owner, 100 ether); IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 4; _executeAdminTasks(report); setUpBnftHolders(); @@ -401,7 +400,6 @@ contract LiquidityPoolTest is TestSetup { vm.deal(owner, 100 ether); IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 4; _executeAdminTasks(report); setUpBnftHolders(); @@ -604,7 +602,6 @@ contract LiquidityPoolTest is TestSetup { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 4; _initReportBlockStamp(report); _executeAdminTasks(report); @@ -640,7 +637,6 @@ contract LiquidityPoolTest is TestSetup { IEtherFiOracle.OracleReport memory report2 = _emptyOracleReport(); - report2.numValidatorsToSpinUp = 14; _initReportBlockStamp(report2); _executeAdminTasks(report2); @@ -703,7 +699,6 @@ contract LiquidityPoolTest is TestSetup { function test_DepositWhenUserDeRegisters() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 21; _executeAdminTasks(report); //Sets up the list of BNFT holders @@ -740,7 +735,6 @@ contract LiquidityPoolTest is TestSetup { function test_DepositFromBNFTHolder() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 4; _executeAdminTasks(report); vm.startPrank(alice); @@ -883,7 +877,6 @@ contract LiquidityPoolTest is TestSetup { function test_DepositFromBNFTHolderTwice() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 8; _executeAdminTasks(report); vm.startPrank(alice); @@ -922,53 +915,6 @@ contract LiquidityPoolTest is TestSetup { } */ - function test_SD_17() public { - vm.deal(owner, 100 ether); - - IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 4; - _executeAdminTasks(report); - - setUpBnftHolders(); - - vm.warp(976348625856); - - hoax(alice); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.2 ether}(2, 0.1 ether); - assertEq(bidIds.length, 2); - - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 0); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 0); - - startHoax(bob); - liquidityPoolInstance.deposit{value: 128 ether}(); - vm.stopPrank(); - - assertEq(address(liquidityPoolInstance).balance, 128 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 128 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 128 ether); - - uint256 aliceBalance = address(alice).balance; - uint256[] memory bidIdsWithDuplicates = new uint256[](4); - bidIdsWithDuplicates[0] = bidIds[0]; - bidIdsWithDuplicates[1] = bidIds[0]; - bidIdsWithDuplicates[2] = bidIds[1]; - bidIdsWithDuplicates[3] = bidIds[1]; - vm.prank(alice); - uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIdsWithDuplicates, 4); - - assertEq(newValidators.length, 2); - assertEq(address(alice).balance, aliceBalance); - assertEq(address(liquidityPoolInstance).balance, 128 ether); - assertEq(address(stakingManagerInstance).balance, 0); - assertEq(liquidityPoolInstance.numPendingDeposits(), 2); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 128 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 128 ether); - } - /* function test_goerli_test() internal { initializeRealisticFork(TESTNET_FORK); diff --git a/test/StakingManager.t.sol b/test/StakingManager.t.sol index 135010724..4e4d98d7c 100644 --- a/test/StakingManager.t.sol +++ b/test/StakingManager.t.sol @@ -52,7 +52,6 @@ contract StakingManagerTest is TestSetup { function test_ApproveRegistration() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - report.numValidatorsToSpinUp = 4; _executeAdminTasks(report); vm.startPrank(alice); diff --git a/test/TestSetup.sol b/test/TestSetup.sol index ce404fea0..0d2a315ea 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -25,7 +25,6 @@ import "../src/AuctionManager.sol"; import "../src/archive/ProtocolRevenueManager.sol"; import "../src/BNFT.sol"; import "../src/TNFT.sol"; -import "../src/Treasury.sol"; import "../src/EtherFiNode.sol"; import "../src/LiquidityPool.sol"; import "../src/Liquifier.sol"; @@ -38,7 +37,6 @@ import "../src/EarlyAdopterPool.sol"; import "../src/TVLOracle.sol"; import "../src/UUPSProxy.sol"; import "../src/WithdrawRequestNFT.sol"; -import "../src/NFTExchange.sol"; import "../src/helpers/AddressProvider.sol"; import "./common/DepositDataGeneration.sol"; import "./common/DepositContract.sol"; @@ -67,7 +65,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen uint256 public constant kwei = 10 ** 3; uint256 public slippageLimit = 50; - Create2Factory immutable create2factory = Create2Factory(0x6521991A0BC180a5df7F42b27F4eE8f3B192BA62); + Create2Factory immutable create2factory = Create2Factory(0x356d1B83970CeF2018F2c9337cDdb67dff5AEF99); TestERC20 public rETH; TestERC20 public wstETH; @@ -111,7 +109,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen UUPSProxy public nodeOperatorManagerProxy; UUPSProxy public membershipManagerProxy; UUPSProxy public membershipNftProxy; - UUPSProxy public nftExchangeProxy; UUPSProxy public withdrawRequestNFTProxy; UUPSProxy public etherFiRedemptionManagerProxy; UUPSProxy public etherFiOracleProxy; @@ -178,9 +175,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen EtherFiRedemptionManager public etherFiRedemptionManagerInstance; - NFTExchange public nftExchangeImplementation; - NFTExchange public nftExchangeInstance; - NodeOperatorManager public nodeOperatorManagerImplementation; NodeOperatorManager public nodeOperatorManagerInstance; @@ -193,7 +187,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen EtherFiRewardsRouter public etherFiRewardsRouterInstance = EtherFiRewardsRouter(payable(0x73f7b1184B5cD361cC0f7654998953E2a251dd58)); EtherFiNode public node; - Treasury public treasuryInstance; + address public treasuryInstance; TVLOracle tvlOracle; @@ -395,12 +389,10 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen weEthInstance = WeETH(addressProviderInstance.getContractAddress("WeETH")); membershipManagerV1Instance = MembershipManager(payable(addressProviderInstance.getContractAddress("MembershipManager"))); membershipNftInstance = MembershipNFT(addressProviderInstance.getContractAddress("MembershipNFT")); - nftExchangeInstance = NFTExchange(addressProviderInstance.getContractAddress("NFTExchange")); auctionInstance = AuctionManager(addressProviderInstance.getContractAddress("AuctionManager")); stakingManagerInstance = StakingManager(addressProviderInstance.getContractAddress("StakingManager")); TNFTInstance = TNFT(addressProviderInstance.getContractAddress("TNFT")); BNFTInstance = BNFT(addressProviderInstance.getContractAddress("BNFT")); - treasuryInstance = Treasury(payable(addressProviderInstance.getContractAddress("Treasury"))); nodeOperatorManagerInstance = NodeOperatorManager(addressProviderInstance.getContractAddress("NodeOperatorManager")); node = EtherFiNode(payable(addressProviderInstance.getContractAddress("EtherFiNode"))); earlyAdopterPoolInstance = EarlyAdopterPool(payable(addressProviderInstance.getContractAddress("EarlyAdopterPool"))); @@ -462,7 +454,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen depositContractEth2 = IDepositContract(address(mockDepositContractEth2)); // Deploy Contracts and Proxies - treasuryInstance = new Treasury(); + treasuryInstance = vm.addr(999999999); roleRegistryImplementation = new RoleRegistry(); roleRegistryProxy = new UUPSProxy(address(roleRegistryImplementation), abi.encodeWithSelector(RoleRegistry.initialize.selector, owner)); @@ -541,13 +533,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen sfrxEth.mint(alice, 10e18); sfrxEth.mint(bob, 10e18); - earlyAdopterPoolInstance = new EarlyAdopterPool( - address(rETH), - address(wstETH), - address(sfrxEth), - address(cbEthTestERC) - ); - addressProviderInstance = new AddressProvider(address(owner)); liquidityPoolImplementation = new LiquidityPool(); @@ -709,12 +694,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen tvlOracle = new TVLOracle(alice); - nftExchangeImplementation = new NFTExchange(); - nftExchangeProxy = new UUPSProxy(address(nftExchangeImplementation), ""); - nftExchangeInstance = NFTExchange(payable(nftExchangeProxy)); - nftExchangeInstance.initialize(address(TNFTInstance), address(membershipNftInstance), address(managerInstance)); - nftExchangeInstance.updateAdmin(alice); - etherFiAdminInstance.initialize( address(etherFiOracleInstance), address(stakingManagerInstance), @@ -870,17 +849,16 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen uint256[] memory validatorsToExit = new uint256[](0); uint256[] memory exitedValidators = new uint256[](0); uint32[] memory exitTimestamps = new uint32[](0); - uint256[] memory slashedValidators = new uint256[](0); uint256[] memory withdrawalRequestsToInvalidate = new uint256[](0); - reportAtPeriod2A = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); - reportAtPeriod2B = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 81, 19, 0, 0); - reportAtPeriod2C = IEtherFiOracle.OracleReport(2, 0, 1024 - 1, 0, 1024 - 1, 1, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 79, 21, 0, 0); - reportAtPeriod3 = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 2048 - 1, 1, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); - reportAtPeriod3A = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 3 * 1024 - 1, 1, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); - reportAtPeriod3B = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 1, 2 * 1024 - 1, 1, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); - reportAtPeriod4 = IEtherFiOracle.OracleReport(1, 2 * 1024, 1024 * 3 - 1, 2 * 1024, 3 * 1024 - 1, 0, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); - reportAtSlot3071 = IEtherFiOracle.OracleReport(1, 2048, 3072 - 1, 2048, 3072 - 1, 1, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); - reportAtSlot4287 = IEtherFiOracle.OracleReport(1, 3264, 4288 - 1, 3264, 4288 - 1, 1, 0, validatorsToApprove, validatorsToExit, exitedValidators, exitTimestamps, slashedValidators, withdrawalRequestsToInvalidate, 1, 80, 20, 0, 0); + reportAtPeriod2A = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod2B = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod2C = IEtherFiOracle.OracleReport(2, 0, 1024 - 1, 0, 1024 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod3 = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 2048 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod3A = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 3 * 1024 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod3B = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 1, 2 * 1024 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod4 = IEtherFiOracle.OracleReport(1, 2 * 1024, 1024 * 3 - 1, 2 * 1024, 3 * 1024 - 1, 0, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtSlot3071 = IEtherFiOracle.OracleReport(1, 2048, 3072 - 1, 2048, 3072 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtSlot4287 = IEtherFiOracle.OracleReport(1, 3264, 4288 - 1, 3264, 4288 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); } function _merkleSetup() internal { @@ -1111,7 +1089,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen uint256[] memory emptyVals = new uint256[](0); uint32[] memory emptyVals32 = new uint32[](0); uint32 consensusVersion = etherFiOracleInstance.consensusVersion(); - report = IEtherFiOracle.OracleReport(consensusVersion, 0, 0, 0, 0, 0, 0, emptyVals, emptyVals, emptyVals, emptyVals32, emptyVals, emptyVals, 0, 0, 0, 0, 0); + report = IEtherFiOracle.OracleReport(consensusVersion, 0, 0, 0, 0, 0, 0, emptyVals, emptyVals, emptyVals32, emptyVals, 0, 0); } function calculatePermitDigest(address _owner, address spender, uint256 value, uint256 nonce, uint256 deadline, bytes32 domainSeparator) public pure returns (bytes32) { @@ -1262,10 +1240,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen function launch_validator(uint256 _numValidators, uint256 _validatorIdToCoUseWithdrawalSafe, bool _isLpBnftHolder, address _bnftStaker, address _nodeOperator) internal returns (uint256[] memory) { bytes32 rootForApproval; - // IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - // report.numValidatorsToSpinUp = uint32(_numValidators); - // _executeAdminTasks(report); - vm.deal(owner, 10000 ether); vm.deal(alice, 10000 ether); vm.deal(bob, 10000 ether); diff --git a/test/Treasury.t.sol b/test/Treasury.t.sol deleted file mode 100644 index ea1bb22bc..000000000 --- a/test/Treasury.t.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "./TestSetup.sol"; - -contract TreasuryTest is TestSetup { - - function setUp() public { - setUpTests(); - } - - function test_TreasuryCanReceiveFunds() public { - assertEq(address(treasuryInstance).balance, 0); - startHoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - (bool sent, ) = address(treasuryInstance).call{value: 0.5 ether}(""); - require(sent, "Failed to send Ether"); - - assertEq(address(treasuryInstance).balance, 0.5 ether); - } - - function test_WithdrawFailsIfNotOwner() public { - hoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - (bool sent, ) = address(treasuryInstance).call{value: 0.5 ether}(""); - require(sent, "Failed to send Ether"); - - vm.prank(alice); - vm.expectRevert("Ownable: caller is not the owner"); - treasuryInstance.withdraw(0, alice); - } - - function test_WithdrawWorks() public { - assertEq(address(treasuryInstance).balance, 0); - uint256 ownerBalanceBeforeWithdrawal = address(owner).balance; - - hoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - (bool sent, ) = address(treasuryInstance).call{value: 0.5 ether}(""); - require(sent, "Failed to send Ether"); - assertEq(address(treasuryInstance).balance, 0.5 ether); - vm.prank(owner); - vm.expectRevert("the balance is lower than the requested amount"); - treasuryInstance.withdraw(0.5 ether + 1, owner); - - vm.prank(owner); - treasuryInstance.withdraw(0.5 ether, owner); - - assertEq(address(owner).balance, ownerBalanceBeforeWithdrawal + 0.5 ether); - - assertEq(address(treasuryInstance).balance, 0); - } - - function test_WithdrawPartialWorks() public { - assertEq(address(treasuryInstance).balance, 0); - uint256 ownerBalanceBeforeWithdrawal = address(owner).balance; - - hoax(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); - (bool sent, ) = address(treasuryInstance).call{value: 5 ether}(""); - require(sent, "Failed to send Ether"); - - assertEq(address(treasuryInstance).balance, 5 ether); - - vm.prank(owner); - treasuryInstance.withdraw(0.5 ether, owner); - - assertEq(address(owner).balance, ownerBalanceBeforeWithdrawal + 0.5 ether); - assertEq(address(treasuryInstance).balance, 4.5 ether); - } -} diff --git a/test/eethPayoutUpgrade.t.sol b/test/eethPayoutUpgrade.t.sol deleted file mode 100644 index 74e3e7abb..000000000 --- a/test/eethPayoutUpgrade.t.sol +++ /dev/null @@ -1,224 +0,0 @@ -pragma solidity ^0.8.13; - -import "./TestSetup.sol"; - - struct OldOracleReport { - uint32 consensusVersion; - uint32 refSlotFrom; - uint32 refSlotTo; - uint32 refBlockFrom; - uint32 refBlockTo; - int128 accruedRewards; - uint256[] validatorsToApprove; - uint256[] liquidityPoolValidatorsToExit; - uint256[] exitedValidators; - uint32[] exitedValidatorsExitTimestamps; - uint256[] slashedValidators; - uint256[] withdrawalRequestsToInvalidate; - uint32 lastFinalizedWithdrawalRequestId; - uint32 eEthTargetAllocationWeight; - uint32 etherFanTargetAllocationWeight; - uint128 finalizedWithdrawalAmount; - uint32 numValidatorsToSpinUp; - } - -contract eethPayoutUpgradeTest is TestSetup { - address treasury; - address lpAdmin; - address oracleAdmin; - uint256 setupSnapshot; - - function setUp() public { - setUpTests(); - oracleAdmin = address(0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F); - initializeRealisticForkWithBlock(MAINNET_FORK, 20370905); - treasury = address(alice); - upgradeContract(); - vm.startPrank(managerInstance.owner()); -// managerInstance.setStakingRewardsSplit(0, 0, 1000000, 0); - vm.stopPrank(); - } - - function upgradeContract() public { - setupSnapshot = vm.snapshot(); - LiquidityPool newLiquidityImplementation = new LiquidityPool(); - EtherFiAdmin newEtherFiAdminImplementation = new EtherFiAdmin(); - EtherFiOracle newEtherFiOracleImplementation = new EtherFiOracle(); - vm.startPrank(admin); - liquidityPoolInstance.upgradeTo(address(newLiquidityImplementation)); - etherFiAdminInstance.upgradeTo(address(newEtherFiAdminImplementation)); - liquidityPoolInstance.setFeeRecipient(alice); - vm.stopPrank(); - vm.startPrank(owner); - etherFiOracleInstance.upgradeTo(address(newEtherFiOracleImplementation)); - } - - function generateReport() public returns (IEtherFiOracle.OracleReport memory) { - IEtherFiOracle.OracleReport memory report = IEtherFiOracle.OracleReport({ - consensusVersion: 1, - refSlotFrom: 9574144, - refSlotTo: 9577503, - refBlockFrom: 20367245, - refBlockTo: 20370590, - accruedRewards: 64625161825710190377, - protocolFees: 8 ether, - validatorsToApprove: new uint256[](0), - liquidityPoolValidatorsToExit: new uint256[](0), - exitedValidators: new uint256[](0), - exitedValidatorsExitTimestamps: new uint32[](0), - slashedValidators: new uint256[](0), - withdrawalRequestsToInvalidate: new uint256[](0), - lastFinalizedWithdrawalRequestId: 30403, - eEthTargetAllocationWeight: 0, - etherFanTargetAllocationWeight: 0, - finalizedWithdrawalAmount: 2298792938059759651463, - numValidatorsToSpinUp: 100 - }); - return report; - } - - function generateOldReport() public returns (OldOracleReport memory) { - OldOracleReport memory report = OldOracleReport({ - consensusVersion: 1, - refSlotFrom: 9574144, - refSlotTo: 9577503, - refBlockFrom: 20367245, - refBlockTo: 20370590, - accruedRewards: 64625161825710190377, - validatorsToApprove: new uint256[](0), - liquidityPoolValidatorsToExit: new uint256[](0), - exitedValidators: new uint256[](0), - exitedValidatorsExitTimestamps: new uint32[](0), - slashedValidators: new uint256[](0), - withdrawalRequestsToInvalidate: new uint256[](0), - lastFinalizedWithdrawalRequestId: 30403, - eEthTargetAllocationWeight: 0, - etherFanTargetAllocationWeight: 0, - finalizedWithdrawalAmount: 2298792938059759651463, - numValidatorsToSpinUp: 100 - }); - return report; - } - - function helperSubmitReport(uint128 _protocolFees) public { - IEtherFiOracle.OracleReport memory report = generateReport(); - report.protocolFees = int128(_protocolFees); - vm.startPrank(oracleAdmin); - etherFiOracleInstance.submitReport(report); - skip(1000); - vm.startPrank(alice); - bool isAdmin = roleRegistryInstance.hasRole(etherFiAdminInstance.ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE(), alice); - etherFiAdminInstance.executeTasks(report); - uint256 balOfTreasury = eETHInstance.balanceOf(treasury); - assertApproxEqAbs(balOfTreasury, _protocolFees, 10); - vm.stopPrank(); - } - - function test_noProtocolFees() public { - helperSubmitReport(0); - } - - function test_noProtocolFeesEquivalentCurrentContract() public { - IEtherFiOracle.OracleReport memory new_report = generateReport(); - new_report.protocolFees = 0; - uint256 total_pooled_eth_upgraded_before = liquidityPoolInstance.getTotalPooledEther(); - vm.startPrank(oracleAdmin); - etherFiOracleInstance.submitReport(new_report); - skip(1000); - etherFiAdminInstance.executeTasks(new_report); - uint256 total_pooled_eth_upgraded_after = liquidityPoolInstance.getTotalPooledEther(); - vm.stopPrank(); - - vm.revertTo(setupSnapshot); - OldOracleReport memory old_report = generateOldReport(); - UUPSProxy oldEtherFiAdmin = UUPSProxy(payable(addressProviderInstance.getContractAddress("EtherFiAdmin"))); - UUPSProxy oldEtherFiOracle = UUPSProxy(payable(addressProviderInstance.getContractAddress("EtherFiOracle"))); - vm.startPrank(oracleAdmin); - uint256 total_pooled_eth_not_upgraded_before = liquidityPoolInstance.getTotalPooledEther(); - (bool success1, bytes memory res1) = address(oldEtherFiOracle).call(abi.encodeWithSignature("submitReport((uint32,uint32,uint32,uint32,uint32,int128,uint256[],uint256[],uint256[],uint32[],uint256[],uint256[],uint32,uint32,uint32,uint128,uint32))", old_report)); - skip(1000); - (bool success2, bytes memory res2) = address(oldEtherFiAdmin).call(abi.encodeWithSignature("executeTasks((uint32,uint32,uint32,uint32,uint32,int128,uint256[],uint256[],uint256[],uint32[],uint256[],uint256[],uint32,uint32,uint32,uint128,uint32),bytes[],bytes[])", old_report, new bytes[](0), new bytes[](0))); - uint256 total_pooled_eth_not_upgraded_after = liquidityPoolInstance.getTotalPooledEther(); - vm.stopPrank(); - assertTrue(success1); - assertTrue(success2); - assertEq(total_pooled_eth_not_upgraded_before, total_pooled_eth_upgraded_before); - assertEq(total_pooled_eth_not_upgraded_after, total_pooled_eth_upgraded_after); - } - - function test_sharePriceSame() public { - IEtherFiOracle.OracleReport memory new_report = generateReport(); - OldOracleReport memory old_report = generateOldReport(); - - // share price after executing adminTask upgraded contract - uint256 share_price_upgraded_before = weEthInstance.getWeETHByeETH(1 ether); - vm.prank(oracleAdmin); - etherFiOracleInstance.submitReport(new_report); - skip(1000); - vm.prank(alice); - etherFiAdminInstance.executeTasks(new_report); - uint256 share_price_upgraded_after = weEthInstance.getWeETHByeETH(1 ether); - - //go back in time before executing adminTask - vm.revertTo(setupSnapshot); - - // share price after executing adminTask in old contract - - UUPSProxy oldEtherFiOracle = UUPSProxy(payable(addressProviderInstance.getContractAddress("EtherFiOracle"))); - UUPSProxy oldEtherFiAdmin = UUPSProxy(payable(addressProviderInstance.getContractAddress("EtherFiAdmin"))); - uint256 share_price_not_upgrade_before = weEthInstance.getWeETHByeETH(1 ether); - vm.prank(oracleAdmin); - (bool success1, bytes memory res1) = address(oldEtherFiOracle).call(abi.encodeWithSignature("submitReport((uint32,uint32,uint32,uint32,uint32,int128,uint256[],uint256[],uint256[],uint32[],uint256[],uint256[],uint32,uint32,uint32,uint128,uint32))", old_report)); - skip(1000); - (bool success2, bytes memory res2) = address(oldEtherFiAdmin).call(abi.encodeWithSignature("executeTasks((uint32,uint32,uint32,uint32,uint32,int128,uint256[],uint256[],uint256[],uint32[],uint256[],uint256[],uint32,uint32,uint32,uint128,uint32),bytes[],bytes[])", old_report, new bytes[](0), new bytes[](0))); - uint256 share_price_not_upgrade_after = weEthInstance.getWeETHByeETH(1 ether); - - //share price should remain the same after paying protocolFees - assertTrue(success1); - assertTrue(success2); - assertEq(share_price_not_upgrade_before, share_price_upgraded_before); - assertEq(share_price_not_upgrade_after, share_price_upgraded_after); - } - - function test_lowProtocolFees() public { - helperSubmitReport(1 ether); - } - - function test_highProtocolFees() public { - int128 protocolFees = 1000 ether; - IEtherFiOracle.OracleReport memory report = generateReport(); - report.protocolFees = int128(protocolFees); - vm.startPrank(oracleAdmin); - etherFiOracleInstance.submitReport(report); - skip(1000); - vm.startPrank(alice); - bool isAdmin = roleRegistryInstance.hasRole(etherFiAdminInstance.ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE(), alice); - vm.expectRevert("EtherFiAdmin: protocol fees exceed 20% total rewards"); - etherFiAdminInstance.executeTasks(report); - vm.stopPrank(); - } - - function test_negativeFees() public { - IEtherFiOracle.OracleReport memory report = generateReport(); - report.protocolFees = int128(-1000); - vm.startPrank(oracleAdmin); - etherFiOracleInstance.submitReport(report); - skip(1000); - vm.expectRevert(); - etherFiAdminInstance.executeTasks(report); - vm.stopPrank(); - } - - function test_permissionFordepositToRecipient() public { - vm.startPrank(address(etherFiAdminInstance)); - liquidityPoolInstance.depositToRecipient(treasury, 10 ether, address(0)); - vm.stopPrank(); - vm.startPrank(address(liquifierInstance)); - liquidityPoolInstance.depositToRecipient(treasury, 10 ether, address(0)); - vm.stopPrank(); - vm.startPrank(bob); - vm.expectRevert(); - liquidityPoolInstance.depositToRecipient(treasury, 10 ether, address(0)); - vm.stopPrank(); - } -} diff --git a/test/eethPayoutUpgradeFork.t.sol b/test/eethPayoutUpgradeFork.t.sol deleted file mode 100644 index d0f78ca83..000000000 --- a/test/eethPayoutUpgradeFork.t.sol +++ /dev/null @@ -1,119 +0,0 @@ -import "./TestSetup.sol"; -import "forge-std/console.sol"; - -contract eethPayoutUpgradeTest is TestSetup { - address treasury; - address lpAdmin; - address oracleAdmin; - uint256 setupSnapshot; - - //forge test --match-test 'test_oraclefork' -vv - - function setUp() public { - setUpTests(); - oracleAdmin = address(0x12582A27E5e19492b4FcD194a60F8f5e1aa31B0F); - committeeMember = address(0x72F4EDd19a96Bcd796d2ba49C6AC534680785619); - //initializeRealisticFork(MAINNET_FORK); - initializeRealisticForkWithBlock(MAINNET_FORK, 20915172); - vm.startPrank(liquidityPoolInstance.owner()); - etherFiOracleInstance.setQuorumSize(1); - vm.stopPrank(); - //todo: set up the treasury and uncomment upgradeContract line - //treasury = liquidityPoolInstance.treasury(); - upgradeContract(); - } - - function upgradeContract() public { - setupSnapshot = vm.snapshot(); - LiquidityPool newLiquidityImplementation = new LiquidityPool(); - EtherFiAdmin newEtherFiAdminImplementation = new EtherFiAdmin(); - EtherFiOracle newEtherFiOracleImplementation = new EtherFiOracle(); - vm.startPrank(liquidityPoolInstance.owner()); - liquidityPoolInstance.upgradeTo(address(newLiquidityImplementation)); - etherFiAdminInstance.upgradeTo(address(newEtherFiAdminImplementation)); - etherFiOracleInstance.upgradeTo(address(newEtherFiOracleImplementation)); - liquidityPoolInstance.setFeeRecipient(alice); - treasury = alice; - vm.stopPrank(); - } - - function generateReport() public returns (IEtherFiOracle.OracleReport memory) { - (uint32 slotFrom, uint32 slotTo, uint32 blockFrom) = etherFiOracleInstance.blockStampForNextReport(); - //todo: update this to block after last admin task more than from - uint32 blockTo = blockFrom + 2000; //test if it works - console.log(slotFrom, slotTo, blockFrom, blockTo); - IEtherFiOracle.OracleReport memory report = IEtherFiOracle.OracleReport({ - consensusVersion: 1, - refSlotFrom: slotFrom, - refSlotTo: slotTo, - refBlockFrom: blockFrom, - refBlockTo: blockTo, - accruedRewards: 2 ether, //todo: adjust value optional - protocolFees: 1 ether / 100, - validatorsToApprove: new uint256[](0), - liquidityPoolValidatorsToExit: new uint256[](0), - exitedValidators: new uint256[](0), - exitedValidatorsExitTimestamps: new uint32[](0), - slashedValidators: new uint256[](0), - withdrawalRequestsToInvalidate: new uint256[](0), - lastFinalizedWithdrawalRequestId: 30403, - eEthTargetAllocationWeight: 0, - etherFanTargetAllocationWeight: 0, - finalizedWithdrawalAmount: 2298792938059759651463, - numValidatorsToSpinUp: 100 - }); - return report; - } - - function helperSubmitReport(uint128 _protocolFees) public { - //setup - IEtherFiOracle.OracleReport memory report = generateReport(); - report.protocolFees = int128(_protocolFees); - vm.startPrank(committeeMember); - - //todo: adjust value - skip(100); - etherFiOracleInstance.submitReport(report); - vm.stopPrank(); - vm.startPrank(oracleAdmin); - skip(1000); - //pre state - uint256 oldRate = weEthInstance.getRate(); - uint128 totalValueOutOfLpBefore = liquidityPoolInstance.totalValueOutOfLp(); - - etherFiAdminInstance.executeTasks(report); - - //post state - uint256 newRate = weEthInstance.getRate(); - uint128 totalValueOutOfLpAfter = liquidityPoolInstance.totalValueOutOfLp(); - uint256 balOfTreasury = eETHInstance.balanceOf(treasury); - int128 theoretical_totalValueOutOfLp = int128(totalValueOutOfLpBefore) + int128(_protocolFees) + report.accruedRewards; - - //visual change - console.log("-------protocolFee", _protocolFees, '-------'); - console.log("oldRate: ", oldRate); - console.log("newRate: ", newRate); - console.log("totalValueOutOfLpBefore: ", totalValueOutOfLpBefore); - console.log("totalValueOutOfLpAfter: ", totalValueOutOfLpAfter); - console2.log("theoretical_totalValueOutOfLp: ", theoretical_totalValueOutOfLp); - - - assertApproxEqAbs(theoretical_totalValueOutOfLp, int128(totalValueOutOfLpAfter), 1); - assertApproxEqAbs(balOfTreasury, _protocolFees, 1); - assert(newRate > oldRate); - vm.stopPrank(); - } - - //test that users got their funds and totalValueOutOfLp is correct - function test_oraclefork1() public { - helperSubmitReport(0); - } - - function test_oraclefork2() public { - helperSubmitReport(0.01 ether); - } - - function test_oraclefork3() public { - helperSubmitReport(2700 ether); - } -} \ No newline at end of file From fd050344b07c23424afa920951f3d44bdf9c4d9d Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 4 May 2025 15:38:15 -0600 Subject: [PATCH 06/47] working test setup --- src/EtherFiNodesManager.sol | 30 ++++-- src/StakingManager.sol | 39 ++++--- src/interfaces/IEtherFiNode.sol | 1 - src/interfaces/IStakingManager.sol | 15 ++- src/libraries/DepositRootGenerator.sol | 5 +- test/ContractCodeChecker.t.sol | 2 +- test/EigenLayerIntegration.t.sol | 2 +- test/TestSetup.sol | 10 +- test/common/ArrayTestHelper.sol | 7 ++ test/prelude.t.sol | 141 +++++++++++++++++++++++++ 10 files changed, 215 insertions(+), 37 deletions(-) create mode 100644 test/prelude.t.sol diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index e4cfce0a7..26a43ca3d 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -74,8 +74,8 @@ contract EtherFiNodesManager is mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; */ - address public immutable eigenPodManager; - address public immutable delegationManager; + address public immutable eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); + address public immutable delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); address public immutable stakingManager; //----------------------------------------------------------------- @@ -92,10 +92,6 @@ contract EtherFiNodesManager is //-------------------------------------------------------------------------------------- //------------------------------------- EVENTS --------------------------------------- //-------------------------------------------------------------------------------------- - event NodeExitRequested(uint256 _validatorId); - event NodeExitProcessed(uint256 _validatorId); - //event PhaseChanged(uint256 indexed _validatorId, IEtherFiNode.VALIDATOR_PHASE _phase); - event PartialWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event QueuedRestakingWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, bytes32[] withdrawalRoots); @@ -108,7 +104,8 @@ contract EtherFiNodesManager is event NodeDeployed(address indexed nodeAddress, uint256 indexed nodeNonce); /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { + constructor(address _stakingManager) { + stakingManager = _stakingManager; _disableInitializers(); } @@ -129,9 +126,22 @@ contract EtherFiNodesManager is function pauseContract() external { _pause(); } function unPauseContract() external { _unpause(); } - function etherfiNodeAddress(uint256) public view returns (address) { - // TODO(dave): implement - return address(0); + // Note that this ID can either be a a traditional etherfi validatorID or + // a validatorPubkeyHash cast as a uint256. This was done maintaint compatibility + // with minimal changes as we migrate from our id system to using pubkey hash instead + function etherfiNodeAddress(uint256 id) public view returns (address) { + // if the ID is a legacy validatorID use the old storage array + // otherwise assume it is a pubkey hash. + // In a future upgrade we can fully remove the legacy path + + // heuristic that if a pubkey hash, at least 1 bit of higher order bits must be 1 + // all of the legacy id's were incrementing integers that will not have those bits set + uint256 mask = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000; + if (mask & id > 0) { + return address(etherFiNodeFromPubkeyHash[bytes32(id)]); + } else { + return legacyState.DEPRECATED_etherfiNodeAddress[id]; + } } function etherFiNodeFromId(uint256 id) public view returns (address) { diff --git a/src/StakingManager.sol b/src/StakingManager.sol index e0847f3c3..9a1fe7214 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +pragma solidity ^0.8.27; import "./interfaces/ITNFT.sol"; import "./interfaces/IBNFT.sol"; @@ -35,11 +35,11 @@ contract StakingManager is UUPSUpgradeable { - /* uint128 public maxBatchDepositSize; uint128 public stakeAmount; - address public implementationContract; + //address public implementationContract; + address public etherFiNodeImplementation; address public liquidityPoolContract; bool public isFullStakeEnabled; @@ -47,21 +47,21 @@ contract StakingManager is ITNFT public TNFTInterfaceInstance; IBNFT public BNFTInterfaceInstance; - IAuctionManager public auctionManager; - IDepositContract public depositContractEth2; + IAuctionManager public DEPRECATED_auctionManager; + IDepositContract public DEPRECATED_depositContractEth2; IEtherFiNodesManager public nodesManager; UpgradeableBeacon private upgradableBeacon; - mapping(uint256 => StakerInfo) public bidIdToStakerInfo; + //mapping(uint256 => StakerInfo) public bidIdToStakerInfo; + mapping(uint256 => uint256) public bidIdToStakerInfo; address public DEPRECATED_admin; address public nodeOperatorManager; mapping(address => bool) public admins; - */ // TODO(dave): fix storage shift - UpgradeableBeacon private upgradableBeacon; - address public etherFiNodeImplementation; + //UpgradeableBeacon private upgradableBeacon; + //address public etherFiNodeImplementation; address public immutable liquidityPool; IEtherFiNodesManager public immutable etherFiNodesManager; @@ -89,14 +89,29 @@ contract StakingManager is // legacy event still being emitted in its original form to play nice with existing external tooling event ValidatorRegistered(address indexed operator, address indexed bNftOwner, address indexed tNftOwner, uint256 validatorId, bytes validatorPubKey, string ipfsHashForEncryptedValidatorKey); - - //-------------------------------------------------------------------------------------- //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ //-------------------------------------------------------------------------------------- /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { + constructor( + address _liquidityPool, + address _etherFiNodesManager, + address _ethDepositContract, + address _auctionManager, + address _tnft, + address _bnft, + address _etherfiOracle + ) { + + liquidityPool = _liquidityPool; + etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); + depositContractEth2 = IDepositContract(_ethDepositContract); + auctionManager = IAuctionManager(_auctionManager); + tnft = ITNFT(_tnft); + bnft = IBNFT(_bnft); + etherfiOracle = _etherfiOracle; + _disableInitializers(); } diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index ccef5cb2a..e91f64afc 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -8,7 +8,6 @@ import "../eigenlayer-interfaces/IEigenPod.sol"; interface IEtherFiNode { - // eigenlayer function createEigenPod() external; function getEigenPod() external view returns (IEigenPod); diff --git a/src/interfaces/IStakingManager.sol b/src/interfaces/IStakingManager.sol index e4640bb20..676578de3 100644 --- a/src/interfaces/IStakingManager.sol +++ b/src/interfaces/IStakingManager.sol @@ -11,17 +11,24 @@ interface IStakingManager { string ipfsHashForEncryptedValidatorKey; } - function pauseContract() external; - function unPauseContract() external; + function calculateValidatorPubkeyHash(bytes memory pubkey) external pure returns (bytes32); + + // deposit flow function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode) external payable; function confirmAndFundBeaconValidators(DepositData[] calldata depositData, uint256 validatorSizeWei) external payable; - function calculateValidatorPubkeyHash(bytes memory pubkey) external pure returns (bytes32); - function upgradeEtherFiNode(address _newImplementation) external; + // EtherFiNode Beacon Proxy + function upgradeEtherFiNode(address _newImplementation) external; function getEtherFiNodeBeacon() external view returns (address); + // protocol + function pauseContract() external; + function unPauseContract() external; + //function owner() external returns (address); + //function upgradeTo(address _newImplementation) external; + // this is implementation for the etherFiNode as the staking manager serves as the beacon //function implementation() external view returns (address); diff --git a/src/libraries/DepositRootGenerator.sol b/src/libraries/DepositRootGenerator.sol index 133fb00f7..500396e74 100644 --- a/src/libraries/DepositRootGenerator.sol +++ b/src/libraries/DepositRootGenerator.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; -import "../MembershipManager.sol"; -import "../LiquidityPool.sol"; - library depositRootGenerator { uint constant GWEI = 1e9; @@ -50,4 +47,4 @@ library depositRootGenerator { ret[6] = bytesValue[1]; ret[7] = bytesValue[0]; } -} \ No newline at end of file +} diff --git a/test/ContractCodeChecker.t.sol b/test/ContractCodeChecker.t.sol index aeb2a81f4..104ebcbd1 100644 --- a/test/ContractCodeChecker.t.sol +++ b/test/ContractCodeChecker.t.sol @@ -18,7 +18,7 @@ contract ContractCodeCheckerTest is TestSetup { function test_deployment_bytecode() public { // Create new implementations - EtherFiNodesManager etherFiNodesManagerImplementation = new EtherFiNodesManager(); + EtherFiNodesManager etherFiNodesManagerImplementation = new EtherFiNodesManager(address(0x0)); address etherFiNodesManagerImplAddress = address(0xE9EE6923D41Cf5F964F11065436BD90D4577B5e4); EtherFiNode etherFiNodeImplementation = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); diff --git a/test/EigenLayerIntegration.t.sol b/test/EigenLayerIntegration.t.sol index aba75a2e3..6486accd8 100644 --- a/test/EigenLayerIntegration.t.sol +++ b/test/EigenLayerIntegration.t.sol @@ -70,7 +70,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { liquidityPoolInstance.setRestakeBnftDeposits(true); vm.stopPrank(); - EtherFiNodesManager newManagerImpl = new EtherFiNodesManager(); + EtherFiNodesManager newManagerImpl = new EtherFiNodesManager(address(0x0)); EtherFiNode newNodeImpl = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); vm.startPrank(managerInstance.owner()); diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 0d2a315ea..4aa4edfde 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -472,7 +472,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen auctionInstance.initialize(address(nodeOperatorManagerInstance)); auctionInstance.updateAdmin(alice, true); - stakingManagerImplementation = new StakingManager(); + //stakingManagerImplementation = new StakingManager(); stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation), ""); stakingManagerInstance = StakingManager(address(stakingManagerProxy)); //stakingManagerInstance.initialize(address(auctionInstance), address(mockDepositContractEth2)); @@ -494,7 +494,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen protocolRevenueManagerInstance.initialize(); protocolRevenueManagerInstance.updateAdmin(alice); - managerImplementation = new EtherFiNodesManager(); + managerImplementation = new EtherFiNodesManager(address(stakingManagerInstance)); etherFiNodeManagerProxy = new UUPSProxy(address(managerImplementation), ""); managerInstance = EtherFiNodesManager(payable(address(etherFiNodeManagerProxy))); @@ -1448,13 +1448,15 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } function _upgrade_etherfi_nodes_manager_contract() internal { - address newImpl = address(new EtherFiNodesManager()); + address newImpl = address(new EtherFiNodesManager(address(stakingManagerInstance))); vm.prank(managerInstance.owner()); managerInstance.upgradeTo(newImpl); } function _upgrade_staking_manager_contract() internal { - address newImpl = address(new StakingManager()); + // TODO(dave): fix + //address newImpl = address(new StakingManager()); + address newImpl = address(0x0); vm.prank(stakingManagerInstance.owner()); stakingManagerInstance.upgradeTo(newImpl); } diff --git a/test/common/ArrayTestHelper.sol b/test/common/ArrayTestHelper.sol index 06f7f4760..f8887f694 100644 --- a/test/common/ArrayTestHelper.sol +++ b/test/common/ArrayTestHelper.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.27; import "../../src/eigenlayer-interfaces/IDelegationManager.sol"; +import "../../src/interfaces/IStakingManager.sol"; contract ArrayTestHelper { // Common types used throughout our and eigenlayers protocol @@ -30,4 +31,10 @@ contract ArrayTestHelper { vals[0] = withdrawal; return vals; } + + function toArray(IStakingManager.DepositData memory deposit) public returns (IStakingManager.DepositData[] memory) { + IStakingManager.DepositData[] memory vals = new IStakingManager.DepositData[](1); + vals[0] = deposit; + return vals; + } } diff --git a/test/prelude.t.sol b/test/prelude.t.sol new file mode 100644 index 000000000..2fe2d269e --- /dev/null +++ b/test/prelude.t.sol @@ -0,0 +1,141 @@ +pragma solidity ^0.8.27; + +import "forge-std/console2.sol"; +import "forge-std/Test.sol"; +import "../test/common/ArrayTestHelper.sol"; +import "../src/interfaces/ILiquidityPool.sol"; +import "../src/interfaces/IStakingManager.sol"; +import "../src/StakingManager.sol"; +import "../src/interfaces/IEtherFiNodesManager.sol"; +import "../src/EtherFiNodesManager.sol"; +import "../src/interfaces/IEtherFiNode.sol"; +import "../src/interfaces/ITNFT.sol"; +import "../src/interfaces/IBNFT.sol"; +import "../src/libraries/DepositRootGenerator.sol"; + + +contract PreludeTest is Test, ArrayTestHelper { + + StakingManager stakingManager; + ILiquidityPool liquidityPool; + EtherFiNodesManager etherFiNodesManager; + IAuctionManager auctionManager; + ITNFT tnft; + IBNFT bnft; + + address stakingDepositContract = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); + address eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); + address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); + + // i don't think i need this anymore + address oracle; + + + function setUp() public { + + console2.log("setup start"); + vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); + console2.log("post fork"); + + stakingManager = StakingManager(0x25e821b7197B146F7713C3b89B6A4D83516B912d); + liquidityPool = ILiquidityPool(0x308861A430be4cce5502d0A12724771Fc6DaF216); + etherFiNodesManager = EtherFiNodesManager(payable(0x8B71140AD2e5d1E7018d2a7f8a288BD3CD38916F)); + auctionManager = IAuctionManager(0x00C452aFFee3a17d9Cecc1Bcd2B8d5C7635C4CB9); + tnft = ITNFT(0x7B5ae07E2AF1C861BcC4736D23f5f66A61E0cA5e); + bnft = IBNFT(0x6599861e55abd28b91dd9d86A826eC0cC8D72c2c); + + // deploy new staking manager implementation + StakingManager stakingManagerImpl = new StakingManager( + address(liquidityPool), + address(etherFiNodesManager), + address(stakingDepositContract), + address(auctionManager), + address(tnft), + address(bnft), + oracle + ); + vm.prank(stakingManager.owner()); + stakingManager.upgradeTo(address(stakingManagerImpl)); + console2.log("sm upgrade"); + + // upgrade etherFiNode impl + EtherFiNode etherFiNodeImpl = new EtherFiNode( + eigenPodManager, + delegationManager, + address(liquidityPool), + address(etherFiNodesManager) + ); + vm.prank(stakingManager.owner()); + stakingManager.upgradeEtherFiNode(address(etherFiNodeImpl)); + console2.log("efn upgrade"); + + // deploy new efnm implementation + EtherFiNodesManager etherFiNodesManagerImpl = new EtherFiNodesManager(address(stakingManager)); + vm.prank(etherFiNodesManager.owner()); + etherFiNodesManager.upgradeTo(address(etherFiNodesManagerImpl)); + + console2.log("efnm upgrade"); + } + + /* + address etherFiNode = managerInstance.etherFiNodeFromId(11); + root = generateDepositRoot( + hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", + hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", + managerInstance.addressToWithdrawalCredentials(etherFiNode), + 1 ether + ); + + depositDataRootsForApproval[0] = generateDepositRoot( + hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", + hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", + managerInstance.addressToWithdrawalCredentials(etherFiNode), + 31 ether + ); + + IStakingManager.DepositData memory depositData = IStakingManager + .DepositData({ + publicKey: hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", + signature: hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", + depositDataRoot: root, + ipfsHashForEncryptedValidatorKey: "test_ipfs" + }); + */ + + + function test_createBeaconValidators() public { + + // create a bid + address nodeOperator = address(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); + vm.prank(nodeOperator); + uint256[] memory bidIds = auctionManager.createBid{value: 0.1 ether}( + 1, + 0.1 ether + ); + + bytes memory pubkey = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; + bytes memory signature = hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df"; + //uint256 validatorID = 5; + + // TODO: fix + address etherFiNode = address(0x1234); + bytes32 depositRoot = depositRootGenerator.generateDepositRoot( + pubkey, + signature, + etherFiNodesManager.addressToWithdrawalCredentials(etherFiNode), + 1 ether + ); + //uint256[] memory validatorIDs = toArray_u256(validatorID); + IStakingManager.DepositData memory depositData = IStakingManager + .DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: depositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs" + }); + //ISta[] memory depositDatas = toArray(depositData); + + stakingManager.createBeaconValidators(toArray(depositData), bidIds, etherFiNode); + + } +} From a49bf3a4aa5d80d48910f4523e7cb62ca7cb621b Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 5 May 2025 11:10:14 -0600 Subject: [PATCH 07/47] initial deposit test --- src/StakingManager.sol | 36 +++++++++++++++++------ test/prelude.t.sol | 65 +++++++++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 22 deletions(-) diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 9a1fe7214..0fba4b33f 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -71,6 +71,8 @@ contract StakingManager is IBNFT public immutable bnft; address public immutable etherfiOracle; + uint256 public constant initialDepositAmount = 1 ether; + error InvalidCaller(); error UnlinkedPubkey(); error IncorrectBeaconRoot(); @@ -136,14 +138,14 @@ contract StakingManager is // verify deposit root bytes memory withdrawalCredentials = etherFiNodesManager.addressToWithdrawalCredentials(address(IEtherFiNode(etherFiNode).getEigenPod())); - bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, 1 ether); + bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, initialDepositAmount); if (computedDataRoot != depositData[i].depositDataRoot) revert IncorrectBeaconRoot(); // Link the pubkey to a node. Will revert if this pubkey is already registered to a different target etherFiNodesManager.linkPubkeyToNode(depositData[i].publicKey, etherFiNode, bidIds[i]); // Deposit to the Beacon Chain - depositContractEth2.deposit{value: 1 ether}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); + depositContractEth2.deposit{value: initialDepositAmount}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); bytes32 pubkeyHash = calculateValidatorPubkeyHash(depositData[i].publicKey); emit validatorCreated(pubkeyHash, etherFiNode, depositData[i].publicKey); @@ -154,25 +156,27 @@ contract StakingManager is /// @notice send remaining eth to activate validators created by "createBeaconValidators" /// The oracle is expected to have confirmed the withdrawal credentials function confirmAndFundBeaconValidators(DepositData[] calldata depositData, uint256 validatorSizeWei) external payable { - if (msg.sender != etherfiOracle) revert InvalidCaller(); + if (msg.sender != liquidityPool) revert InvalidCaller(); if (validatorSizeWei < 32 ether || validatorSizeWei > 2048 ether) revert InvalidValidatorSize(); + // we already deposited the initial amount to create the validators in createBeaconValidators() + uint256 remainingDeposit = validatorSizeWei - initialDepositAmount; + for (uint256 i = 0; i < depositData.length; i++) { // check that withdrawal credentials for pubkey match what we originally intended - // It is expected that the oracle will not call the function for any key that was front-run + // It is expected that the oracle will not call the function for any key that was front-run by a malicious operator bytes32 pubkeyHash = calculateValidatorPubkeyHash(depositData[i].publicKey); IEtherFiNode etherFiNode = etherFiNodesManager.etherFiNodeFromPubkeyHash(pubkeyHash); if (address(etherFiNode) == address(0x0)) revert UnlinkedPubkey(); // verify deposit root bytes memory withdrawalCredentials = etherFiNodesManager.addressToWithdrawalCredentials(address(etherFiNode.getEigenPod())); - bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, 31 ether); + bytes32 computedDataRoot = depositRootGenerator.generateDepositRoot(depositData[i].publicKey, depositData[i].signature, withdrawalCredentials, remainingDeposit); if (computedDataRoot != depositData[i].depositDataRoot) revert IncorrectBeaconRoot(); // Deposit the remaining eth to the validator - uint256 amountToDeposit = validatorSizeWei - 1 ether; // already did 1 eth deposit - depositContractEth2.deposit{value: amountToDeposit}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); + depositContractEth2.deposit{value: remainingDeposit}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); // Use pubkey hash as the minted token ID tnft.mint(liquidityPool, uint256(pubkeyHash)); @@ -205,7 +209,7 @@ contract StakingManager is etherFiNodeImplementation = _newImplementation; } - function _authorizeUpgrade(address newImplementation) internal override {} + function _authorizeUpgrade(address _newImplementation) internal override {} /// @notice Fetches the address of the beacon contract for future EtherFiNodes (withdrawal safes) function getEtherFiNodeBeacon() external view returns (address) { @@ -218,4 +222,20 @@ contract StakingManager is return upgradableBeacon.implementation(); } + // TODO(dave): Permissions + + /// @dev create a new proxy instance of the etherFiNode withdrawal safe contract. + /// @param _createEigenPod whether or not to create an associated eigenPod contract. + function instantiateEtherFiNode(bool _createEigenPod) external returns (address) { + // TODO(dave): Permissions + //if (msg.sender != address(nodesManager), "INCORRECT_CALLER"); + + BeaconProxy proxy = new BeaconProxy(address(upgradableBeacon), ""); + address node = address(proxy); + if (_createEigenPod) { + IEtherFiNode(node).createEigenPod(); + } + return node; + } + } diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 2fe2d269e..068543766 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -9,8 +9,10 @@ import "../src/StakingManager.sol"; import "../src/interfaces/IEtherFiNodesManager.sol"; import "../src/EtherFiNodesManager.sol"; import "../src/interfaces/IEtherFiNode.sol"; +import "../src/NodeOperatorManager.sol"; import "../src/interfaces/ITNFT.sol"; import "../src/interfaces/IBNFT.sol"; +import "../src/AuctionManager.sol"; import "../src/libraries/DepositRootGenerator.sol"; @@ -19,9 +21,10 @@ contract PreludeTest is Test, ArrayTestHelper { StakingManager stakingManager; ILiquidityPool liquidityPool; EtherFiNodesManager etherFiNodesManager; - IAuctionManager auctionManager; + AuctionManager auctionManager; ITNFT tnft; IBNFT bnft; + NodeOperatorManager nodeOperatorManager = NodeOperatorManager(0xd5edf7730ABAd812247F6F54D7bd31a52554e35E); address stakingDepositContract = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); address eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); @@ -40,7 +43,7 @@ contract PreludeTest is Test, ArrayTestHelper { stakingManager = StakingManager(0x25e821b7197B146F7713C3b89B6A4D83516B912d); liquidityPool = ILiquidityPool(0x308861A430be4cce5502d0A12724771Fc6DaF216); etherFiNodesManager = EtherFiNodesManager(payable(0x8B71140AD2e5d1E7018d2a7f8a288BD3CD38916F)); - auctionManager = IAuctionManager(0x00C452aFFee3a17d9Cecc1Bcd2B8d5C7635C4CB9); + auctionManager = AuctionManager(0x00C452aFFee3a17d9Cecc1Bcd2B8d5C7635C4CB9); tnft = ITNFT(0x7B5ae07E2AF1C861BcC4736D23f5f66A61E0cA5e); bnft = IBNFT(0x6599861e55abd28b91dd9d86A826eC0cC8D72c2c); @@ -75,6 +78,10 @@ contract PreludeTest is Test, ArrayTestHelper { etherFiNodesManager.upgradeTo(address(etherFiNodesManagerImpl)); console2.log("efnm upgrade"); + + vm.prank(auctionManager.owner()); + auctionManager.disableWhitelist(); + } /* @@ -105,8 +112,12 @@ contract PreludeTest is Test, ArrayTestHelper { function test_createBeaconValidators() public { + address nodeOperator = vm.addr(0x123456); + vm.prank(nodeOperator); + nodeOperatorManager.registerNodeOperator("test_ipfs_hash", 1000); + // create a bid - address nodeOperator = address(0xCd5EBC2dD4Cb3dc52ac66CEEcc72c838B40A5931); + vm.deal(nodeOperator, 33 ether); vm.prank(nodeOperator); uint256[] memory bidIds = auctionManager.createBid{value: 0.1 ether}( 1, @@ -118,24 +129,50 @@ contract PreludeTest is Test, ArrayTestHelper { //uint256 validatorID = 5; // TODO: fix - address etherFiNode = address(0x1234); - bytes32 depositRoot = depositRootGenerator.generateDepositRoot( + address etherFiNode = stakingManager.instantiateEtherFiNode(true); + address eigenPod = address(IEtherFiNode(etherFiNode).getEigenPod()); + bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( pubkey, signature, - etherFiNodesManager.addressToWithdrawalCredentials(etherFiNode), + etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), 1 ether ); //uint256[] memory validatorIDs = toArray_u256(validatorID); - IStakingManager.DepositData memory depositData = IStakingManager - .DepositData({ - publicKey: pubkey, - signature: signature, - depositDataRoot: depositRoot, - ipfsHashForEncryptedValidatorKey: "test_ipfs" - }); + IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: initialDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); //ISta[] memory depositDatas = toArray(depositData); - stakingManager.createBeaconValidators(toArray(depositData), bidIds, etherFiNode); + vm.deal(address(liquidityPool), 100 ether); + vm.prank(address(liquidityPool)); + stakingManager.createBeaconValidators{value: 1 ether}(toArray(initialDepositData), bidIds, etherFiNode); + + uint256 validatorSize = 32 ether; + uint256 confirmAmount = validatorSize - 1 ether; + + bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot( + pubkey, + signature, + etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), + confirmAmount + ); + + IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: confirmDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + + + + vm.prank(address(liquidityPool)); + stakingManager.confirmAndFundBeaconValidators{value: confirmAmount}(toArray(confirmDepositData), validatorSize); + + } } From 44b6b87efffddcae7eab5d36cac6371a045b0202 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 5 May 2025 15:36:36 -0600 Subject: [PATCH 08/47] start cleaning up permissions and forwarding --- src/EtherFiNode.sol | 82 ++++++++++++++++------- src/EtherFiNodesManager.sol | 88 ++++++++++++++++++------- src/StakingManager.sol | 1 - src/interfaces/IEtherFiNode.sol | 2 +- src/interfaces/IEtherFiNodesManager.sol | 8 +-- 5 files changed, 125 insertions(+), 56 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 60b5229a2..646c05ab3 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -8,23 +8,40 @@ import {IERC20} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC20. import {IEtherFiNode} from "../src/interfaces/IEtherFiNode.sol"; import {IEtherFiNodesManager} from "../src/interfaces/IEtherFiNodesManager.sol"; +import {IRoleRegistry} from "../src/interfaces/IRoleRegistry.sol"; import {ILiquidityPool} from "../src/interfaces/ILiquidityPool.sol"; import {LibCall} from "../lib/solady/src/utils/LibCall.sol"; contract EtherFiNode is IEtherFiNode { - IEigenPodManager public immutable eigenPodManager; - IDelegationManager public immutable delegationManager; ILiquidityPool public immutable liquidityPool; IEtherFiNodesManager public immutable etherFiNodesManager; + IRoleRegistry public immutable roleRegistry; - constructor(address _eigenPodManager, address _delegationManager, address _liquidityPool, address _etherFiNodesManager) { + // eigenlayer core contracts + IEigenPodManager public immutable eigenPodManager; + IDelegationManager public immutable delegationManager; + uint32 public constant EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS = 100800; + + error TransferFailed(); + error IncorrectRole(); + + constructor(address _liquidityPool, address _etherFiNodesManager, address _eigenPodManager, address _delegationManager) { eigenPodManager = IEigenPodManager(_eigenPodManager); delegationManager = IDelegationManager(_delegationManager); liquidityPool = ILiquidityPool(_liquidityPool); etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); + + // TODO(dave): add to constructor + roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + } + //-------------------------------------------------------------------------------------- + //------------------------------------- ROLES --------------------------------------- + //-------------------------------------------------------------------------------------- + + bytes32 public constant ETHERFI_NODE_ADMIN_ROLE = keccak256("ETHERFI_NODE_ADMIN_ROLE"); //-------------------------------------------------------------------------------------- //---------------------------- Eigenlayer Interactions -------------------------------- @@ -36,23 +53,20 @@ contract EtherFiNode is IEtherFiNode { return eigenPodManager.ownerToPod(address(this)); } - function createEigenPod() external { - eigenPodManager.createPod(); + function createEigenPod() external onlyAdmin returns (address) { + return eigenPodManager.createPod(); } - // TODO(dave): permissions - function startCheckpoint() external { - bool revertIfNoBalance = true; // protect from wasting gas if checkpoint will not increase shares - getEigenPod().startCheckpoint(revertIfNoBalance); + function setProofSubmitter(address _newProofSubmitter) external onlyAdmin { + getEigenPod().setProofSubmitter(_newProofSubmitter); } - // TODO(dave): permissions - function setProofSubmitter(address _newProofSubmitter) external { - getEigenPod().setProofSubmitter(_newProofSubmitter); + function startCheckpoint() external onlyAdmin { + bool revertIfNoBalance = true; // protect from wasting gas if checkpoint will not increase shares + getEigenPod().startCheckpoint(revertIfNoBalance); } - // TODO(dave): permissions - function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot) { + function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { // Implemented this way because we almost never queue multiple withdrawals at the same time // so I chose to improve our internal interface and simplify testing IDelegationManager.QueuedWithdrawalParams[] memory paramsArray = new IDelegationManager.QueuedWithdrawalParams[](1); @@ -60,39 +74,57 @@ contract EtherFiNode is IEtherFiNode { return delegationManager.queueWithdrawals(paramsArray)[0]; } - // TODO(dave): permissions - /// @dev the latest slashing release adds eigenPodManager.getQueuedWithdrawals which allows us - /// to complete withdrawals without needing an external indexer to track the queued withdrawal params - function completeQueuedWithdrawals(bool receiveAsTokens) external { + /// @dev completes all queued withdrawals that are currently claimable + function completeQueuedWithdrawals(bool receiveAsTokens) external onlyAdmin { // because we are just dealing with beacon eth we don't need to populate the tokens[] array IERC20[] memory tokens; - //TODO: skip withdrawals that haven't had enough time pass yet (IDelegationManager.Withdrawal[] memory queuedWithdrawals, ) = delegationManager.getQueuedWithdrawals(address(this)); for (uint256 i = 0; i < queuedWithdrawals.length; i++) { + + // skip this withdrawal if not enough time has passed + uint32 slashableUntil = queuedWithdrawals[i].startBlock + EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS; + if (uint32(block.number) > slashableUntil) continue; + delegationManager.completeQueuedWithdrawal(queuedWithdrawals[i], tokens, receiveAsTokens); } - // if there are available rewards, forward them to the rewardsManager + // if there are available rewards, forward them to the liquidityPool if (address(this).balance > 0) { - //rewardsManager.depositETHRewards{value: address(this).balance}(); + (bool sent, ) = payable(address(liquidityPool)).call{value: address(this).balance, gas: 20000}(""); + if (!sent) revert TransferFailed(); } } + // @notice transfers any funds held by the node to the liquidity pool. + // @dev under normal operations it is not expected for eth to accumulate in the nodes, + // this is just to handle any exceptional cases such as someone sending directly to the node. + function sweepFunds() external onlyAdmin { + (bool sent, ) = payable(address(liquidityPool)).call{value: address(this).balance, gas: 20000}(""); + if (!sent) revert TransferFailed(); + } + //-------------------------------------------------------------------------------------- //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- - // TODO(dave): Permissions - - function forwardEigenPodCall(bytes calldata data) external returns (bytes memory) { + function forwardEigenPodCall(bytes calldata data) external onlyAdmin returns (bytes memory) { // callContract will revert if targeting an EOA so it is safe if getEigenPod() returns the zero address return LibCall.callContract(address(getEigenPod()), 0, data); } - function forwardExternalCall(address to, bytes calldata data) external returns (bytes memory) { + function forwardExternalCall(address to, bytes calldata data) external onlyAdmin returns (bytes memory) { return LibCall.callContract(to, 0, data); } + //-------------------------------------------------------------------------------------- + //----------------------------------- MODIFIERS -------------------------------------- + //-------------------------------------------------------------------------------------- + + modifier onlyAdmin() { + if (!roleRegistry.hasRole(ETHERFI_NODE_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _; + } + } diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 26a43ca3d..f1740e0b4 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -115,12 +115,6 @@ contract EtherFiNodesManager is error InvalidParams(); - function setProofSubmitter(uint256 id, address proofSubmitter) external { - // TODO(dave): implement - } - function startCheckpoint(uint256 id) external { - // TODO(dave): implement - } // TODO(dave): reimplement pausing with role registry function pauseContract() external { _pause(); } @@ -159,11 +153,37 @@ contract EtherFiNodesManager is } } + //-------------------------------------------------------------------------------------- + //---------------------------- Eigenlayer Interactions -------------------------------- + //-------------------------------------------------------------------------------------- + + // TODO(dave): permissions + function getEigenPod(uint256 id) public view returns (address) { - IEtherFiNode node = IEtherFiNode(etherFiNodeFromId(id)); - return address(node.getEigenPod()); + return address(IEtherFiNode(etherfiNodeAddress(id)).getEigenPod()); + } + + function createEigenPod(uint256 id) public returns (address) { + return IEtherFiNode(etherfiNodeAddress(id)).createEigenPod(); + } + + function startCheckpoint(uint256 id) external { + IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); + } + + function setProofSubmitter(uint256 id, address proofSubmitter) external { + IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } + function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot) { + return IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawal(params); + } + + function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external { + IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(receiveAsTokens); + } + + //------------------------------------------------------------------- //--------------------- Key Management ---------------------------- //------------------------------------------------------------------- @@ -220,47 +240,67 @@ contract EtherFiNodesManager is emit AllowedForwardedEigenpodCallsUpdated(selector, allowed); } - function batchForwardEigenpodCall(bytes32[] calldata pubkeys, bytes[] calldata data) external returns (bytes[] memory returnData) { - returnData = new bytes[](pubkeys.length); - for (uint256 i = 0; i < pubkeys.length; i++) { + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { + if (nodes.length != data.length) revert InvalidForwardedCall(); + + returnData = new bytes[](nodes.length); + for (uint256 i = 0; i < nodes.length; i++) { // validate the call if (data[i].length < 4) revert InvalidForwardedCall(); bytes4 selector = bytes4(data[i][:4]); - if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); + if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); - returnData[i] = etherFiNodeFromPubkeyHash[pubkeys[i]].forwardEigenPodCall(data[i]); + returnData[i] = IEtherFiNode(nodes[i]).forwardExternalCall(target, data[i]); } } - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { - returnData = new bytes[](nodes.length); - for (uint256 i = 0; i < nodes.length; i++) { + function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { + if (ids.length != data.length) revert InvalidForwardedCall(); + + returnData = new bytes[](ids.length); + for (uint256 i = 0; i < ids.length; i++) { // validate the call if (data[i].length < 4) revert InvalidForwardedCall(); bytes4 selector = bytes4(data[i][:4]); if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); - returnData[i] = IEtherFiNode(nodes[i]).forwardExternalCall(target, data[i]); + IEtherFiNode node = IEtherFiNode(etherfiNodeAddress(ids[i])); + returnData[i] = node.forwardExternalCall(target, data[i]); } } - function forwardExternalCall(bytes32[] calldata pubkeys, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { - returnData = new bytes[](pubkeys.length); - for (uint256 i = 0; i < pubkeys.length; i++) { + function forwardEigenPodCall(address[] calldata etherFiNodes, bytes[] calldata data) external returns (bytes[] memory returnData) { + if (etherFiNodes.length != data.length) revert InvalidForwardedCall(); + + returnData = new bytes[](etherFiNodes.length); + for (uint256 i = 0; i < etherFiNodes.length; i++) { // validate the call if (data[i].length < 4) revert InvalidForwardedCall(); bytes4 selector = bytes4(data[i][:4]); - if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); + if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); - returnData[i] = etherFiNodeFromPubkeyHash[pubkeys[i]].forwardExternalCall(target, data[i]); + IEtherFiNode node = IEtherFiNode(etherFiNodes[i]); + returnData[i] = node.forwardEigenPodCall(data[i]); } } - function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes memory) { - // TODO(dave): implement + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData) { + if (ids.length != data.length) revert InvalidForwardedCall(); + + returnData = new bytes[](ids.length); + for (uint256 i = 0; i < ids.length; i++) { + + // validate the call + if (data[i].length < 4) revert InvalidForwardedCall(); + bytes4 selector = bytes4(data[i][:4]); + if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); + + IEtherFiNode node = IEtherFiNode(etherFiNodeFromId(ids[i])); + returnData[i] = node.forwardEigenPodCall(data[i]); + } } } diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 0fba4b33f..e7dde79a6 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -95,7 +95,6 @@ contract StakingManager is //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ //-------------------------------------------------------------------------------------- - /// @custom:oz-upgrades-unsafe-allow constructor constructor( address _liquidityPool, address _etherFiNodesManager, diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index e91f64afc..11c477012 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -9,7 +9,7 @@ import "../eigenlayer-interfaces/IEigenPod.sol"; interface IEtherFiNode { // eigenlayer - function createEigenPod() external; + function createEigenPod() external returns (address); function getEigenPod() external view returns (IEigenPod); function startCheckpoint() external; function setProofSubmitter(address _newProofSubmitter) external; diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index a0d20e960..85b843a56 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -80,10 +80,6 @@ interface IEtherFiNodesManager { } */ - //function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; - //function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); - //function etherFiNodeFromId(uint256 id) external view returns (address); - function addressToWithdrawalCredentials(address addr) external pure returns (bytes memory); function etherfiNodeAddress(uint256 id) external view returns(address); function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; @@ -100,8 +96,10 @@ interface IEtherFiNodesManager { // call forwarding function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external; - function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes memory); function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); + function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external returns (bytes[] memory returnData); + function forwardEigenPodCall(address[] calldata nodes, bytes[] calldata data) external returns (bytes[] memory returnData); + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData); // protocol function pauseContract() external; From c9648c96834c8875e904bbed32ba6c5774c1812e Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 5 May 2025 23:12:37 -0600 Subject: [PATCH 09/47] wip permissions updates for etherfiNodesManager --- src/EtherFiNode.sol | 6 ++- src/EtherFiNodesManager.sol | 92 ++++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 37 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 646c05ab3..329133d4a 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -34,7 +34,6 @@ contract EtherFiNode is IEtherFiNode { // TODO(dave): add to constructor roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); - } //-------------------------------------------------------------------------------------- @@ -53,19 +52,24 @@ contract EtherFiNode is IEtherFiNode { return eigenPodManager.ownerToPod(address(this)); } + /// @dev creates a new eigenpod and returns its address. Reverts if a pod already exists for this node. + /// This address is deterministic and you can pre-compute it if necessary. function createEigenPod() external onlyAdmin returns (address) { return eigenPodManager.createPod(); } + /// @dev specify another address with permissions to submit checkpoint and withdrawal credential proofs function setProofSubmitter(address _newProofSubmitter) external onlyAdmin { getEigenPod().setProofSubmitter(_newProofSubmitter); } + /// @dev start an eigenlayer checkpoint proof. Once a checkpoint is started, it must be completed function startCheckpoint() external onlyAdmin { bool revertIfNoBalance = true; // protect from wasting gas if checkpoint will not increase shares getEigenPod().startCheckpoint(revertIfNoBalance); } + /// @dev queue a withdrawal from eigenlayer function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { // Implemented this way because we almost never queue multiple withdrawals at the same time // so I chose to improve our internal interface and simplify testing diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index f1740e0b4..7faa8ad2c 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -13,8 +13,9 @@ import "./interfaces/IEtherFiNode.sol"; import "./interfaces/IEtherFiNodesManager.sol"; import "./interfaces/IProtocolRevenueManager.sol"; import "./interfaces/IStakingManager.sol"; -import "./TNFT.sol"; -import "./BNFT.sol"; +import "./interfaces/ITNFT.sol"; +import "./interfaces/IBNFT.sol"; +import "./interfaces/IRoleRegistry.sol"; contract EtherFiNodesManager is Initializable, @@ -74,13 +75,12 @@ contract EtherFiNodesManager is mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; */ + // TODO(dave): these are only used by viewer so we should just move them there address public immutable eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); address public immutable delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); - address public immutable stakingManager; - //----------------------------------------------------------------- - //----------------------- Storage ------------------------------- - //----------------------------------------------------------------- + address public immutable stakingManager; + IRoleRegistry public immutable roleRegistry; mapping(bytes32 => IEtherFiNode) public etherFiNodeFromPubkeyHash; @@ -89,14 +89,10 @@ contract EtherFiNodesManager is // Call Forwarding: functionSignature -> targetAddress -> allowed mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; - //-------------------------------------------------------------------------------------- - //------------------------------------- EVENTS --------------------------------------- - //-------------------------------------------------------------------------------------- event PartialWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); event QueuedRestakingWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, bytes32[] withdrawalRoots); - error AlreadyLinked(); error InvalidPubKeyLength(); error InvalidCaller(); @@ -113,30 +109,19 @@ contract EtherFiNodesManager is receive() external payable {} - error InvalidParams(); - - // TODO(dave): reimplement pausing with role registry function pauseContract() external { _pause(); } function unPauseContract() external { _unpause(); } - // Note that this ID can either be a a traditional etherfi validatorID or - // a validatorPubkeyHash cast as a uint256. This was done maintaint compatibility - // with minimal changes as we migrate from our id system to using pubkey hash instead - function etherfiNodeAddress(uint256 id) public view returns (address) { - // if the ID is a legacy validatorID use the old storage array - // otherwise assume it is a pubkey hash. - // In a future upgrade we can fully remove the legacy path + //-------------------------------------------------------------------------------------- + //------------------------------------- ROLES --------------------------------------- + //-------------------------------------------------------------------------------------- + error IncorrectRole(); + + bytes32 public constant ETHERFI_NODES_MANAGER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_ADMIN_ROLE"); + bytes32 public constant ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE"); + - // heuristic that if a pubkey hash, at least 1 bit of higher order bits must be 1 - // all of the legacy id's were incrementing integers that will not have those bits set - uint256 mask = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000; - if (mask & id > 0) { - return address(etherFiNodeFromPubkeyHash[bytes32(id)]); - } else { - return legacyState.DEPRECATED_etherfiNodeAddress[id]; - } - } function etherFiNodeFromId(uint256 id) public view returns (address) { // if the ID is a legacy validatorID use the old storage array @@ -199,7 +184,7 @@ contract EtherFiNodesManager is return abi.encodePacked(bytes1(0x01), bytes11(0x0), addr); } - + /// @dev associate the provided pubkey with particular EtherFiNode instance. function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external { if (msg.sender != address(stakingManager)) revert InvalidCaller(); bytes32 pubkeyHash = calculateValidatorPubkeyHash(pubkey); @@ -213,6 +198,27 @@ contract EtherFiNodesManager is emit PubkeyLinked(pubkeyHash, nodeAddress, pubkey); } + // TODO(dave): is it better to revert if no address exists for provided id? + + /// @notice get the etherFiNode instance associated with the provided ID. (Legacy validatorId or pubkeyHash) + /// @dev Note that this ID can either be a a traditional etherfi validatorID or + // a validatorPubkeyHash cast as a uint256. This was done maintaint compatibility + // with minimal changes as we migrate from our id system to using pubkey hash instead + function etherfiNodeAddress(uint256 id) public view returns (address) { + // if the ID is a legacy validatorID use the old storage array + // otherwise assume it is a pubkey hash. + // In a future upgrade we can fully remove the legacy path + + // heuristic that if a pubkey hash, at least 1 bit of higher order bits must be 1 + // all of the legacy id's were incrementing integers that will not have those bits set + uint256 mask = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000; + if (mask & id > 0) { + return address(etherFiNodeFromPubkeyHash[bytes32(id)]); + } else { + return legacyState.DEPRECATED_etherfiNodeAddress[id]; + } + } + //-------------------------------------------------------------------------------------- //-------------------------------- CALL FORWARDING ------------------------------------ @@ -227,7 +233,7 @@ contract EtherFiNodesManager is /// @param selector method selector /// @param target call target for forwarded call /// @param allowed enable or disable the call - function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external { + function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external onlyAdmin { allowedForwardedExternalCalls[selector][target] = allowed; emit AllowedForwardedExternalCallsUpdated(selector, target, allowed); } @@ -235,12 +241,12 @@ contract EtherFiNodesManager is /// @notice Update the whitelist for external calls that can be executed against the corresponding eigenpod /// @param selector method selector /// @param allowed enable or disable the call - function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external { + function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external onlyAdmin { allowedForwardedEigenpodCalls[selector] = allowed; emit AllowedForwardedEigenpodCallsUpdated(selector, allowed); } - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external onlyCallForwarder returns (bytes[] memory returnData) { if (nodes.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](nodes.length); @@ -255,7 +261,7 @@ contract EtherFiNodesManager is } } - function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) public returns (bytes[] memory returnData) { + function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external onlyCallForwarder returns (bytes[] memory returnData) { if (ids.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](ids.length); @@ -271,7 +277,7 @@ contract EtherFiNodesManager is } } - function forwardEigenPodCall(address[] calldata etherFiNodes, bytes[] calldata data) external returns (bytes[] memory returnData) { + function forwardEigenPodCall(address[] calldata etherFiNodes, bytes[] calldata data) external onlyCallForwarder returns (bytes[] memory returnData) { if (etherFiNodes.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](etherFiNodes.length); @@ -287,7 +293,7 @@ contract EtherFiNodesManager is } } - function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData) { + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external onlyCallForwarder returns (bytes[] memory returnData) { if (ids.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](ids.length); @@ -303,4 +309,18 @@ contract EtherFiNodesManager is } } + //-------------------------------------------------------------------------------------- + //----------------------------------- MODIFIERS -------------------------------------- + //-------------------------------------------------------------------------------------- + + modifier onlyAdmin() { + if (!roleRegistry.hasRole(ETHERFI_NODES_MANAGER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _; + } + + modifier onlyCallForwarder() { + if (!roleRegistry.hasRole(ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE, msg.sender)) revert IncorrectRole(); + _; + } + } From 3e81d8c56df3a6fffd90fbfed0cf5a65282875da Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 May 2025 14:38:21 -0600 Subject: [PATCH 10/47] continue updating permissions and roles --- src/EtherFiNodesManager.sol | 27 ++++++++++++++-------- src/StakingManager.sol | 46 ++++++++++++++++++++++++------------- test/prelude.t.sol | 26 +++++++++++++-------- 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 7faa8ad2c..0c60babff 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -102,6 +102,10 @@ contract EtherFiNodesManager is /// @custom:oz-upgrades-unsafe-allow constructor constructor(address _stakingManager) { stakingManager = _stakingManager; + + // TODO(dave): add to constructor + roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + _disableInitializers(); } @@ -110,8 +114,14 @@ contract EtherFiNodesManager is receive() external payable {} // TODO(dave): reimplement pausing with role registry - function pauseContract() external { _pause(); } - function unPauseContract() external { _unpause(); } + function pauseContract() external { + if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender)) revert IncorrectRole(); + _pause(); + } + function unPauseContract() external { + if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_UNPAUSER(), msg.sender)) revert IncorrectRole(); + _unpause(); + } //-------------------------------------------------------------------------------------- //------------------------------------- ROLES --------------------------------------- @@ -121,8 +131,6 @@ contract EtherFiNodesManager is bytes32 public constant ETHERFI_NODES_MANAGER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_ADMIN_ROLE"); bytes32 public constant ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE"); - - function etherFiNodeFromId(uint256 id) public view returns (address) { // if the ID is a legacy validatorID use the old storage array // otherwise assume it is a pubkey hash. @@ -148,27 +156,26 @@ contract EtherFiNodesManager is return address(IEtherFiNode(etherfiNodeAddress(id)).getEigenPod()); } - function createEigenPod(uint256 id) public returns (address) { + function createEigenPod(uint256 id) public onlyAdmin returns (address) { return IEtherFiNode(etherfiNodeAddress(id)).createEigenPod(); } - function startCheckpoint(uint256 id) external { + function startCheckpoint(uint256 id) external onlyAdmin { IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); } - function setProofSubmitter(uint256 id, address proofSubmitter) external { + function setProofSubmitter(uint256 id, address proofSubmitter) external onlyAdmin { IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } - function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot) { + function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { return IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawal(params); } - function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external { + function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external onlyAdmin { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(receiveAsTokens); } - //------------------------------------------------------------------- //--------------------- Key Management ---------------------------- //------------------------------------------------------------------- diff --git a/src/StakingManager.sol b/src/StakingManager.sol index e7dde79a6..254ba4128 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -69,17 +69,18 @@ contract StakingManager is IAuctionManager public immutable auctionManager; ITNFT public immutable tnft; IBNFT public immutable bnft; + IRoleRegistry public immutable roleRegistry; address public immutable etherfiOracle; uint256 public constant initialDepositAmount = 1 ether; error InvalidCaller(); + error IncorrectRole(); error UnlinkedPubkey(); error IncorrectBeaconRoot(); error InvalidPubKeyLength(); error InvalidDepositData(); error InactiveBid(); - error IncorrectValidatorFunds(); error InvalidEtherFiNode(); error InvalidValidatorSize(); error InvalidUpgrade(); @@ -91,9 +92,6 @@ contract StakingManager is // legacy event still being emitted in its original form to play nice with existing external tooling event ValidatorRegistered(address indexed operator, address indexed bNftOwner, address indexed tNftOwner, uint256 validatorId, bytes validatorPubKey, string ipfsHashForEncryptedValidatorKey); - //-------------------------------------------------------------------------------------- - //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ - //-------------------------------------------------------------------------------------- constructor( address _liquidityPool, @@ -113,14 +111,38 @@ contract StakingManager is bnft = IBNFT(_bnft); etherfiOracle = _etherfiOracle; + // TODO(dave): add to constructor + roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + _disableInitializers(); } // TODO(dave): implement function initialize() external { - + + } + + function _authorizeUpgrade(address _newImplementation) internal override {} + + function pauseContract() external { + if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender)) revert IncorrectRole(); + _pause(); + } + function unPauseContract() external { + if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_UNPAUSER(), msg.sender)) revert IncorrectRole(); + _unpause(); } + //-------------------------------------------------------------------------------------- + //------------------------------------- ROLES --------------------------------------- + //-------------------------------------------------------------------------------------- + + bytes32 public constant STAKING_MANAGER_NODE_CREATOR_ROLE = keccak256("STAKING_MANAGER_NODE_CREATOR_ROLE"); + + //-------------------------------------------------------------------------------------- + //------------------------- Deposit Flow ----------------------------------------------- + //-------------------------------------------------------------------------------------- + /// @notice send 1 eth to deposit contract to create the validator. /// The rest of the eth will not be sent until the oracle confirms the withdrawal credentials function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode) external payable { @@ -178,8 +200,8 @@ contract StakingManager is depositContractEth2.deposit{value: remainingDeposit}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); // Use pubkey hash as the minted token ID - tnft.mint(liquidityPool, uint256(pubkeyHash)); - bnft.mint(liquidityPool, uint256(pubkeyHash)); + //tnft.mint(liquidityPool, uint256(pubkeyHash)); + //bnft.mint(liquidityPool, uint256(pubkeyHash)); emit validatorConfirmed(pubkeyHash, liquidityPool, liquidityPool, depositData[i].publicKey); } @@ -191,9 +213,6 @@ contract StakingManager is return sha256(abi.encodePacked(pubkey, bytes16(0))); } - // TODO(dave): reimplement pausing with role registry - function pauseContract() external { _pause(); } - function unPauseContract() external { _unpause(); } //-------------------------------------------------------------------------------------- //--------------------- EtherFiNode Beacon Proxy ---------------------------------- @@ -208,8 +227,6 @@ contract StakingManager is etherFiNodeImplementation = _newImplementation; } - function _authorizeUpgrade(address _newImplementation) internal override {} - /// @notice Fetches the address of the beacon contract for future EtherFiNodes (withdrawal safes) function getEtherFiNodeBeacon() external view returns (address) { return address(upgradableBeacon); @@ -221,13 +238,10 @@ contract StakingManager is return upgradableBeacon.implementation(); } - // TODO(dave): Permissions - /// @dev create a new proxy instance of the etherFiNode withdrawal safe contract. /// @param _createEigenPod whether or not to create an associated eigenPod contract. function instantiateEtherFiNode(bool _createEigenPod) external returns (address) { - // TODO(dave): Permissions - //if (msg.sender != address(nodesManager), "INCORRECT_CALLER"); + if (!roleRegistry.hasRole(STAKING_MANAGER_NODE_CREATOR_ROLE, msg.sender)) revert IncorrectRole(); BeaconProxy proxy = new BeaconProxy(address(upgradableBeacon), ""); address node = address(proxy); diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 068543766..09a880e1c 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -26,9 +26,11 @@ contract PreludeTest is Test, ArrayTestHelper { IBNFT bnft; NodeOperatorManager nodeOperatorManager = NodeOperatorManager(0xd5edf7730ABAd812247F6F54D7bd31a52554e35E); + address admin = vm.addr(0x9876543210); address stakingDepositContract = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); address eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); + RoleRegistry roleRegistry = RoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); // i don't think i need this anymore address oracle; @@ -63,15 +65,17 @@ contract PreludeTest is Test, ArrayTestHelper { // upgrade etherFiNode impl EtherFiNode etherFiNodeImpl = new EtherFiNode( - eigenPodManager, - delegationManager, address(liquidityPool), - address(etherFiNodesManager) + address(etherFiNodesManager), + eigenPodManager, + delegationManager ); vm.prank(stakingManager.owner()); stakingManager.upgradeEtherFiNode(address(etherFiNodeImpl)); console2.log("efn upgrade"); + console2.log("epm:", address(etherFiNodeImpl.eigenPodManager())); + // deploy new efnm implementation EtherFiNodesManager etherFiNodesManagerImpl = new EtherFiNodesManager(address(stakingManager)); vm.prank(etherFiNodesManager.owner()); @@ -82,6 +86,14 @@ contract PreludeTest is Test, ArrayTestHelper { vm.prank(auctionManager.owner()); auctionManager.disableWhitelist(); + // permissions + vm.startPrank(roleRegistry.owner()); + roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_ADMIN_ROLE(), address(etherFiNodesManager)); + roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_ADMIN_ROLE(), address(stakingManager)); + roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_ADMIN_ROLE(), admin); + roleRegistry.grantRole(stakingManager.STAKING_MANAGER_NODE_CREATOR_ROLE(), admin); + vm.stopPrank(); + } /* @@ -126,10 +138,10 @@ contract PreludeTest is Test, ArrayTestHelper { bytes memory pubkey = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; bytes memory signature = hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df"; - //uint256 validatorID = 5; - // TODO: fix + vm.prank(admin); address etherFiNode = stakingManager.instantiateEtherFiNode(true); + address eigenPod = address(IEtherFiNode(etherFiNode).getEigenPod()); bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( pubkey, @@ -167,12 +179,8 @@ contract PreludeTest is Test, ArrayTestHelper { ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" }); - - vm.prank(address(liquidityPool)); stakingManager.confirmAndFundBeaconValidators{value: confirmAmount}(toArray(confirmDepositData), validatorSize); - - } } From 20576099ba9d86908ac71949d62927e3121043a0 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 May 2025 18:14:20 -0600 Subject: [PATCH 11/47] start cleaning up events and errors --- src/EtherFiNode.sol | 18 ++-- src/EtherFiNodesManager.sol | 70 ++----------- src/StakingManager.sol | 92 ++++------------- src/interfaces/IEtherFiNode.sol | 9 ++ src/interfaces/IEtherFiNodesManager.sol | 125 +++++++++++++----------- src/interfaces/IStakingManager.sol | 93 +++++++++++------- test/prelude.t.sol | 1 + 7 files changed, 174 insertions(+), 234 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 329133d4a..1d8b5b51f 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -23,14 +23,12 @@ contract EtherFiNode is IEtherFiNode { IDelegationManager public immutable delegationManager; uint32 public constant EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS = 100800; - error TransferFailed(); - error IncorrectRole(); constructor(address _liquidityPool, address _etherFiNodesManager, address _eigenPodManager, address _delegationManager) { - eigenPodManager = IEigenPodManager(_eigenPodManager); - delegationManager = IDelegationManager(_delegationManager); liquidityPool = ILiquidityPool(_liquidityPool); etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); + eigenPodManager = IEigenPodManager(_eigenPodManager); + delegationManager = IDelegationManager(_delegationManager); // TODO(dave): add to constructor roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); @@ -69,16 +67,20 @@ contract EtherFiNode is IEtherFiNode { getEigenPod().startCheckpoint(revertIfNoBalance); } - /// @dev queue a withdrawal from eigenlayer + /// @dev queue a withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. + /// It is fine to queue a withdrawal before validators have finished exiting on the beacon chain. function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { - // Implemented this way because we almost never queue multiple withdrawals at the same time - // so I chose to improve our internal interface and simplify testing + + // Implemented this way for convenience because we almost never queue multiple withdrawals at the same time IDelegationManager.QueuedWithdrawalParams[] memory paramsArray = new IDelegationManager.QueuedWithdrawalParams[](1); paramsArray[0] = params; return delegationManager.queueWithdrawals(paramsArray)[0]; } - /// @dev completes all queued withdrawals that are currently claimable + /// @dev completes all queued withdrawals that are currently claimable. + /// Note that since the node is usually delegated to an operator, + /// most of the time this should be called with "receiveAsTokens" = true because + /// receiving shares while delegated will simply redelegate the shares. function completeQueuedWithdrawals(bool receiveAsTokens) external onlyAdmin { // because we are just dealing with beacon eth we don't need to populate the tokens[] array diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 0c60babff..71ec0eae9 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -25,55 +25,6 @@ contract EtherFiNodesManager is ReentrancyGuardUpgradeable, UUPSUpgradeable { - //-------------------------------------------------------------------------------------- - //--------------------------------- STATE-VARIABLES ---------------------------------- - //-------------------------------------------------------------------------------------- - LegacyManagerState public legacyState; - /* - uint64 public numberOfValidators; // # of validators in LIVE or WAITING_FOR_APPROVAL phases - uint64 public nonExitPenaltyPrincipal; - uint64 public nonExitPenaltyDailyRate; // in basis points - uint64 public SCALE; - - address public treasuryContract; - address public stakingManagerContract; - address public DEPRECATED_protocolRevenueManagerContract; - - // validatorId == bidId -> withdrawalSafeAddress - mapping(uint256 => address) public etherfiNodeAddress; - - TNFT public tnft; - BNFT public bnft; - IAuctionManager public auctionManager; - IProtocolRevenueManager public DEPRECATED_protocolRevenueManager; - - RewardsSplit public stakingRewardsSplit; - RewardsSplit public DEPRECATED_protocolRewardsSplit; - - address public DEPRECATED_admin; - mapping(address => bool) public admins; - - IEigenPodManager public eigenPodManager; - IDelayedWithdrawalRouter public delayedWithdrawalRouter; - // max number of queued eigenlayer withdrawals to attempt to claim in a single tx - uint8 public maxEigenlayerWithdrawals; - - // stack of re-usable withdrawal safes to save gas - address[] public unusedWithdrawalSafes; - - bool public DEPRECATED_enableNodeRecycling; - - mapping(uint256 => ValidatorInfo) private validatorInfos; - - IDelegationManager public delegationManager; - - mapping(address => bool) public operatingAdmin; - - // function -> allowed - mapping(bytes4 => bool) public allowedForwardedEigenpodCalls; - // function -> target_address -> allowed - mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; - */ // TODO(dave): these are only used by viewer so we should just move them there address public immutable eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); @@ -82,22 +33,18 @@ contract EtherFiNodesManager is address public immutable stakingManager; IRoleRegistry public immutable roleRegistry; - mapping(bytes32 => IEtherFiNode) public etherFiNodeFromPubkeyHash; + //--------------------------------------------------------------------------- + //----------------------------- Storage ----------------------------------- + //--------------------------------------------------------------------------- - // Call Forwarding: functionSignature -> allowed + LegacyNodesManagerState private legacyState; + // Call Forwarding: functionSelector -> allowed mapping(bytes4 => bool) public allowedForwardedEigenpodCalls; - // Call Forwarding: functionSignature -> targetAddress -> allowed + // Call Forwarding: functionSelector -> targetAddress -> allowed mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; - event PartialWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); - event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); - event QueuedRestakingWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, bytes32[] withdrawalRoots); + mapping(bytes32 => IEtherFiNode) public etherFiNodeFromPubkeyHash; - error AlreadyLinked(); - error InvalidPubKeyLength(); - error InvalidCaller(); - event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, bytes pubkey); - event NodeDeployed(address indexed nodeAddress, uint256 indexed nodeNonce); /// @custom:oz-upgrades-unsafe-allow constructor constructor(address _stakingManager) { @@ -126,7 +73,6 @@ contract EtherFiNodesManager is //-------------------------------------------------------------------------------------- //------------------------------------- ROLES --------------------------------------- //-------------------------------------------------------------------------------------- - error IncorrectRole(); bytes32 public constant ETHERFI_NODES_MANAGER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_ADMIN_ROLE"); bytes32 public constant ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE"); @@ -230,8 +176,6 @@ contract EtherFiNodesManager is //-------------------------------------------------------------------------------------- //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- - event AllowedForwardedExternalCallsUpdated(bytes4 indexed selector, address indexed _target, bool _allowed); - event AllowedForwardedEigenpodCallsUpdated(bytes4 indexed selector, bool _allowed); error ForwardedCallNotAllowed(); error InvalidForwardedCall(); diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 254ba4128..b4c71ba35 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -3,17 +3,14 @@ pragma solidity ^0.8.27; import "./interfaces/ITNFT.sol"; import "./interfaces/IBNFT.sol"; +import "./interfaces/IRoleRegistry.sol"; import "./interfaces/IAuctionManager.sol"; import "./interfaces/IStakingManager.sol"; import "./interfaces/IDepositContract.sol"; import "./interfaces/IEtherFiNode.sol"; import "./interfaces/IEtherFiNodesManager.sol"; -import "./interfaces/INodeOperatorManager.sol"; -import "./interfaces/ILiquidityPool.sol"; -import "./TNFT.sol"; -import "./BNFT.sol"; -import "./EtherFiNode.sol"; -import "./LiquidityPool.sol"; +import "./libraries/DepositRootGenerator.sol"; + import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import "@openzeppelin-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol"; @@ -22,7 +19,6 @@ import "@openzeppelin-upgradeable/contracts/security/PausableUpgradeable.sol"; import "@openzeppelin-upgradeable/contracts/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; -import "./libraries/DepositRootGenerator.sol"; contract StakingManager is @@ -34,64 +30,19 @@ contract StakingManager is ReentrancyGuardUpgradeable, UUPSUpgradeable { - - uint128 public maxBatchDepositSize; - uint128 public stakeAmount; - - //address public implementationContract; - address public etherFiNodeImplementation; - address public liquidityPoolContract; - - bool public isFullStakeEnabled; - bytes32 public merkleRoot; - - ITNFT public TNFTInterfaceInstance; - IBNFT public BNFTInterfaceInstance; - IAuctionManager public DEPRECATED_auctionManager; - IDepositContract public DEPRECATED_depositContractEth2; - IEtherFiNodesManager public nodesManager; - UpgradeableBeacon private upgradableBeacon; - - //mapping(uint256 => StakerInfo) public bidIdToStakerInfo; - mapping(uint256 => uint256) public bidIdToStakerInfo; - - address public DEPRECATED_admin; - address public nodeOperatorManager; - mapping(address => bool) public admins; - - // TODO(dave): fix storage shift - //UpgradeableBeacon private upgradableBeacon; - //address public etherFiNodeImplementation; + UpgradeableBeacon private upgradableBeacon; address public immutable liquidityPool; + uint256 public constant initialDepositAmount = 1 ether; IEtherFiNodesManager public immutable etherFiNodesManager; IDepositContract public immutable depositContractEth2; IAuctionManager public immutable auctionManager; ITNFT public immutable tnft; IBNFT public immutable bnft; IRoleRegistry public immutable roleRegistry; - address public immutable etherfiOracle; - uint256 public constant initialDepositAmount = 1 ether; - - error InvalidCaller(); - error IncorrectRole(); - error UnlinkedPubkey(); - error IncorrectBeaconRoot(); - error InvalidPubKeyLength(); - error InvalidDepositData(); - error InactiveBid(); - error InvalidEtherFiNode(); - error InvalidValidatorSize(); - error InvalidUpgrade(); - - event validatorCreated(bytes32 indexed pubkeyHash, address indexed etherFiNode, bytes pubkey); - event validatorConfirmed(bytes32 indexed pubkeyHash, address indexed bnftRecipient, address indexed tnftRecipient, bytes pubkey); - event linkLegacyValidatorId(bytes32 indexed pubkeyHash, uint256 indexed legacyId); - - // legacy event still being emitted in its original form to play nice with existing external tooling - event ValidatorRegistered(address indexed operator, address indexed bNftOwner, address indexed tNftOwner, - uint256 validatorId, bytes validatorPubKey, string ipfsHashForEncryptedValidatorKey); + // Storage + LegacyStakingManagerState legacyState; constructor( address _liquidityPool, @@ -102,26 +53,21 @@ contract StakingManager is address _bnft, address _etherfiOracle ) { - liquidityPool = _liquidityPool; etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); depositContractEth2 = IDepositContract(_ethDepositContract); auctionManager = IAuctionManager(_auctionManager); tnft = ITNFT(_tnft); bnft = IBNFT(_bnft); - etherfiOracle = _etherfiOracle; + // TODO(dave): add to constructor + upgradableBeacon = UpgradeableBeacon(address(0x01)); // TODO(dave): add to constructor roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); _disableInitializers(); } - // TODO(dave): implement - function initialize() external { - - } - function _authorizeUpgrade(address _newImplementation) internal override {} function pauseContract() external { @@ -133,15 +79,15 @@ contract StakingManager is _unpause(); } - //-------------------------------------------------------------------------------------- - //------------------------------------- ROLES --------------------------------------- - //-------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------- + //--------------------------- ROLES --------------------------------------- + //--------------------------------------------------------------------------- bytes32 public constant STAKING_MANAGER_NODE_CREATOR_ROLE = keccak256("STAKING_MANAGER_NODE_CREATOR_ROLE"); - //-------------------------------------------------------------------------------------- - //------------------------- Deposit Flow ----------------------------------------------- - //-------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------- + //------------------------- Deposit Flow ------------------------------------ + //--------------------------------------------------------------------------- /// @notice send 1 eth to deposit contract to create the validator. /// The rest of the eth will not be sent until the oracle confirms the withdrawal credentials @@ -213,10 +159,9 @@ contract StakingManager is return sha256(abi.encodePacked(pubkey, bytes16(0))); } - - //-------------------------------------------------------------------------------------- - //--------------------- EtherFiNode Beacon Proxy ---------------------------------- - //-------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------- + //--------------------- EtherFiNode Beacon Proxy ---------------------------- + //--------------------------------------------------------------------------- /// @notice Upgrades the etherfi node /// @param _newImplementation The new address of the etherfi node @@ -224,7 +169,6 @@ contract StakingManager is if (_newImplementation == address(0)) revert InvalidUpgrade(); upgradableBeacon.upgradeTo(_newImplementation); - etherFiNodeImplementation = _newImplementation; } /// @notice Fetches the address of the beacon contract for future EtherFiNodes (withdrawal safes) diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index 11c477012..d32e7a905 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -8,6 +8,7 @@ import "../eigenlayer-interfaces/IEigenPod.sol"; interface IEtherFiNode { + // eigenlayer function createEigenPod() external returns (address); function getEigenPod() external view returns (IEigenPod); @@ -19,4 +20,12 @@ interface IEtherFiNode { // call forwarding function forwardEigenPodCall(bytes memory data) external returns (bytes memory); function forwardExternalCall(address to, bytes memory data) external returns (bytes memory); + + //-------------------------------------------------------------------------- + //----------------------------- Errors ----------------------------------- + //-------------------------------------------------------------------------- + + error TransferFailed(); + error IncorrectRole(); + } diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 85b843a56..4ce72a657 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -20,66 +20,64 @@ interface IEtherFiNodesManager { function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); function etherFiNodeFromId(uint256 id) external view returns (address); - struct LegacyManagerState { - uint64 test; + struct LegacyNodesManagerState { + uint256[4] legacyPadding1; + // we are continuing to use this field in the short term before we fully transition to using pubkeyhash mapping(uint256 => address) DEPRECATED_etherfiNodeAddress; + uint256[15] legacyPadding2; /* - uint64 numberOfValidators; // # of validators in LIVE or WAITING_FOR_APPROVAL phases - uint64 nonExitPenaltyPrincipal; - uint64 nonExitPenaltyDailyRate; // in basis points - uint64 SCALE; - - address treasuryContract; - address stakingManagerContract; - address DEPRECATED_protocolRevenueManagerContract; - - // validatorId == bidId -> withdrawalSafeAddress - */ - - /* - address tnft; - address bnft; - IAuctionManager auctionManager; - IProtocolRevenueManager DEPRECATED_protocolRevenueManager; - - RewardsSplit stakingRewardsSplit; - RewardsSplit DEPRECATED_protocolRewardsSplit; - - address DEPRECATED_admin; - mapping(address => bool) admins; - - IEigenPodManager eigenPodManager; - IDelayedWithdrawalRouter delayedWithdrawalRouter; - // max number of queued eigenlayer withdrawals to attempt to claim in a single tx - uint8 maxEigenlayerWithdrawals; - - // stack of re-usable withdrawal safes to save gas - address[] unusedWithdrawalSafes; - - bool DEPRECATED_enableNodeRecycling; - - mapping(uint256 => ValidatorInfo) validatorInfos; - - IDelegationManager delegationManager; - - mapping(address => bool) operatingAdmin; - - // function -> allowed - mapping(bytes4 => bool) allowedForwardedEigenpodCalls; - // function -> target_address -> allowed - mapping(bytes4 => mapping(address => bool)) allowedForwardedExternalCalls; + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | numberOfValidators | uint64 | 301 | 0 | 8 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | nonExitPenaltyPrincipal | uint64 | 301 | 8 | 8 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | nonExitPenaltyDailyRate | uint64 | 301 | 16 | 8 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | SCALE | uint64 | 301 | 24 | 8 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | treasuryContract | address | 302 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | stakingManagerContract | address | 303 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | DEPRECATED_protocolRevenueManagerContract | address | 304 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | etherfiNodeAddress | mapping(uint256 => address) | 305 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | tnft | contract TNFT | 306 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | bnft | contract BNFT | 307 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | auctionManager | contract IAuctionManager | 308 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | DEPRECATED_protocolRevenueManager | contract IProtocolRevenueManager | 309 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | stakingRewardsSplit | struct IEtherFiNodesManager.RewardsSplit | 310 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | DEPRECATED_protocolRewardsSplit | struct IEtherFiNodesManager.RewardsSplit | 311 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | DEPRECATED_admin | address | 312 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | admins | mapping(address => bool) | 313 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | eigenPodManager | contract IEigenPodManager | 314 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | delayedWithdrawalRouter | contract IDelayedWithdrawalRouter | 315 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | maxEigenlayerWithdrawals | uint8 | 315 | 20 | 1 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | unusedWithdrawalSafes | address[] | 316 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | DEPRECATED_enableNodeRecycling | bool | 317 | 0 | 1 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | validatorInfos | mapping(uint256 => struct IEtherFiNodesManager.ValidatorInfo) | 318 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | delegationManager | contract IDelegationManager | 319 | 0 | 20 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| + | operatingAdmin | mapping(address => bool) | 320 | 0 | 32 | src/EtherFiNodesManager.sol:EtherFiNodesManager | + |-------------------------------------------+---------------------------------------------------------------+------+--------+-------+-------------------------------------------------| */ } - /* - struct ValidatorInfo { - uint32 validatorIndex; - uint32 exitRequestTimestamp; - uint32 exitTimestamp; - IEtherFiNode.VALIDATOR_PHASE phase; - } - */ - function addressToWithdrawalCredentials(address addr) external pure returns (bytes memory); function etherfiNodeAddress(uint256 id) external view returns(address); function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; @@ -105,5 +103,22 @@ interface IEtherFiNodesManager { function pauseContract() external; function unPauseContract() external; + //--------------------------------------------------------------------------- + //----------------------------- Events ----------------------------------- + //--------------------------------------------------------------------------- + + event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, bytes pubkey); + event AllowedForwardedExternalCallsUpdated(bytes4 indexed selector, address indexed _target, bool _allowed); + event AllowedForwardedEigenpodCallsUpdated(bytes4 indexed selector, bool _allowed); + + //-------------------------------------------------------------------------- + //----------------------------- Errors ----------------------------------- + //-------------------------------------------------------------------------- + + error AlreadyLinked(); + error InvalidPubKeyLength(); + error InvalidCaller(); + error IncorrectRole(); + } diff --git a/src/interfaces/IStakingManager.sol b/src/interfaces/IStakingManager.sol index 676578de3..b98f437bb 100644 --- a/src/interfaces/IStakingManager.sol +++ b/src/interfaces/IStakingManager.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.13; import "./ILiquidityPool.sol"; interface IStakingManager { + struct DepositData { bytes publicKey; bytes signature; @@ -11,13 +12,10 @@ interface IStakingManager { string ipfsHashForEncryptedValidatorKey; } - - function calculateValidatorPubkeyHash(bytes memory pubkey) external pure returns (bytes32); - // deposit flow function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode) external payable; function confirmAndFundBeaconValidators(DepositData[] calldata depositData, uint256 validatorSizeWei) external payable; - + function calculateValidatorPubkeyHash(bytes memory pubkey) external pure returns (bytes32); // EtherFiNode Beacon Proxy function upgradeEtherFiNode(address _newImplementation) external; @@ -26,42 +24,69 @@ interface IStakingManager { // protocol function pauseContract() external; function unPauseContract() external; - //function owner() external returns (address); - //function upgradeTo(address _newImplementation) external; - - // this is implementation for the etherFiNode as the staking manager serves as the beacon - //function implementation() external view returns (address); - /* - struct StakerInfo { - address staker; - ILiquidityPool.SourceOfFunds sourceOfFund; + // prevent storage shift on upgrade + struct LegacyStakingManagerState { + uint256[15] legacyState; + /* + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | stakeAmount | uint128 | 301 | 16 | 16 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | implementationContract | address | 302 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | liquidityPoolContract | address | 303 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | isFullStakeEnabled | bool | 303 | 20 | 1 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | merkleRoot | bytes32 | 304 | 0 | 32 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | TNFTInterfaceInstance | contract ITNFT | 305 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | BNFTInterfaceInstance | contract IBNFT | 306 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | auctionManager | contract IAuctionManager | 307 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | depositContractEth2 | contract IDepositContract | 308 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | nodesManager | contract IEtherFiNodesManager | 309 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | upgradableBeacon | contract UpgradeableBeacon | 310 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | bidIdToStakerInfo | mapping(uint256 => struct IStakingManager.StakerInfo) | 311 | 0 | 32 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | DEPRECATED_admin | address | 312 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | nodeOperatorManager | address | 313 | 0 | 20 | src/StakingManager.sol:StakingManager | + |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| + | admins | mapping(address => bool) | 314 | 0 | 32 | src/StakingManager.sol:StakingManager | + ╰------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------╯ + */ } - function bidIdToStaker(uint256 id) external view returns (address); + //--------------------------------------------------------------------------- + //----------------------------- Events ----------------------------------- + //--------------------------------------------------------------------------- - function getEtherFiNodeBeacon() external view returns (address); - - function initialize(address _auctionAddress, address _depositContractAddress) external; - function setEtherFiNodesManagerAddress(address _managerAddress) external; - function setLiquidityPoolAddress(address _liquidityPoolAddress) external; - - function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators, address _staker, address _tnftHolder, address _bnftHolder, ILiquidityPool.SourceOfFunds source, bool _enableRestaking, uint256 _validatorIdToCoUseWithdrawalSafe) external returns (uint256[] memory); - function batchDepositWithBidIds(uint256[] calldata _candidateBidIds, bool _enableRestaking) external payable returns (uint256[] memory); - - function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, DepositData[] calldata _depositData) external; - function batchRegisterValidators(bytes32 _depositRoot, uint256[] calldata _validatorId, address _bNftRecipient, address _tNftRecipient, DepositData[] calldata _depositData, address _user) external payable; + event validatorCreated(bytes32 indexed pubkeyHash, address indexed etherFiNode, bytes pubkey); + event validatorConfirmed(bytes32 indexed pubkeyHash, address indexed bnftRecipient, address indexed tnftRecipient, bytes pubkey); + event linkLegacyValidatorId(bytes32 indexed pubkeyHash, uint256 indexed legacyId); - function batchApproveRegistration(uint256[] memory _validatorId, bytes[] calldata _pubKey, bytes[] calldata _signature, bytes32[] calldata _depositDataRootApproval) external payable; + // legacy event still being emitted in its original form to play nice with existing external tooling + event ValidatorRegistered(address indexed operator, address indexed bNftOwner, address indexed tNftOwner, uint256 validatorId, bytes validatorPubKey, string ipfsHashForEncryptedValidatorKey); - function batchCancelDeposit(uint256[] calldata _validatorIds) external; + //-------------------------------------------------------------------------- + //----------------------------- Errors ----------------------------------- + //-------------------------------------------------------------------------- - function batchCancelDepositAsBnftHolder(uint256[] calldata _validatorIds, address _caller) external; + error InvalidCaller(); + error UnlinkedPubkey(); + error IncorrectBeaconRoot(); + error InvalidPubKeyLength(); + error InvalidDepositData(); + error InactiveBid(); + error InvalidEtherFiNode(); + error InvalidValidatorSize(); + error IncorrectRole(); + error InvalidUpgrade(); - function instantiateEtherFiNode(bool _createEigenPod) external returns (address); - - function updateAdmin(address _address, bool _isAdmin) external; - function pauseContract() external; - function unPauseContract() external; - */ } diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 09a880e1c..d16aad82b 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -9,6 +9,7 @@ import "../src/StakingManager.sol"; import "../src/interfaces/IEtherFiNodesManager.sol"; import "../src/EtherFiNodesManager.sol"; import "../src/interfaces/IEtherFiNode.sol"; +import "../src/EtherFiNode.sol"; import "../src/NodeOperatorManager.sol"; import "../src/interfaces/ITNFT.sol"; import "../src/interfaces/IBNFT.sol"; From a5921ec286389062224e58436ac6987268ab3a55 Mon Sep 17 00:00:00 2001 From: Shivam Agrawal Date: Wed, 7 May 2025 12:05:32 +0530 Subject: [PATCH 12/47] add recovery function in weETH and eETH --- src/AssetRecovery.sol | 109 +++++++++++++++++++++++++++++++ src/EETH.sol | 37 +++++++++-- src/WeETH.sol | 46 +++++++++---- src/interfaces/IRoleRegistry.sol | 93 ++++++++++++++++++++++++++ 4 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 src/AssetRecovery.sol create mode 100644 src/interfaces/IRoleRegistry.sol diff --git a/src/AssetRecovery.sol b/src/AssetRecovery.sol new file mode 100644 index 000000000..a67e62786 --- /dev/null +++ b/src/AssetRecovery.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +/** + * @title AssetRecovery + * @dev A library for recovering ETH, ERC20 tokens, and ERC721 tokens that were + * mistakenly sent to this contract. + */ +abstract contract AssetRecovery is ReentrancyGuard { + using SafeERC20 for IERC20; + + /** + * @dev Emitted when ETH is recovered + */ + event ETHRecovered(address indexed to, uint256 amount); + + /** + * @dev Emitted when ERC20 tokens are recovered + */ + event ERC20Recovered(address indexed token, address indexed to, uint256 amount); + + /** + * @dev Emitted when an ERC721 token is recovered + */ + event ERC721Recovered(address indexed token, address indexed to, uint256 tokenId); + + /** + * @dev Error thrown when address or amount inputs are invalid + * Reverted when: + * - Target address is the zero address + * - Token address is the zero address + * - Amount is zero + */ + error InvalidInput(); + + /** + * @dev Error thrown when trying to recover more assets than available + * Reverted when: + * - Trying to recover more ETH than the contract's balance + * - Trying to recover more ERC20 tokens than the contract's balance + */ + error InsufficientBalance(); + + /** + * @dev Error thrown when ETH transfer fails + * Reverted when the ETH transfer fails for any reason, which could indicate: + * - Gas issues + * - Recipient contract rejecting the transfer + * - Revert in recipient's fallback/receive function + */ + error EthTransferFailed(); + + /** + * @dev Error thrown when trying to recover an ERC721 token that this contract doesn't own + * Reverted when: + * - The contract is not the actual owner of the specified tokenId + * - The tokenId doesn't exist + */ + error ContractIsNotOwnerOfERC721Token(); + + /** + * @dev Recover ETH from the contract + * @param to Address to send the recovered ETH to + * @param amount Amount of ETH to recover + */ + function _recoverETH(address payable to, uint256 amount) internal nonReentrant { + if (to == address(0) || amount == 0) revert InvalidInput(); + if (amount > address(this).balance) revert InsufficientBalance(); + + (bool success, ) = to.call{value: amount}(""); + if (!success) revert EthTransferFailed(); + + emit ETHRecovered(to, amount); + } + + /** + * @dev Recover ERC20 tokens from the contract + * @param token Address of the ERC20 token + * @param to Address to send the recovered tokens to + * @param amount Amount of tokens to recover + */ + function _recoverERC20(address token, address to, uint256 amount) internal nonReentrant { + if (token == address(0) || to == address(0) || amount == 0) revert InvalidInput(); + if (amount > IERC20(token).balanceOf(address(this))) revert InsufficientBalance(); + + IERC20(token).safeTransfer(to, amount); + + emit ERC20Recovered(token, to, amount); + } + + /** + * @dev Recover an ERC721 token from the contract + * @param token Address of the ERC721 token + * @param to Address to send the recovered token to + * @param tokenId ID of the token to recover + */ + function _recoverERC721(address token, address to, uint256 tokenId) internal nonReentrant { + if (token == address(0) || to == address(0)) revert InvalidInput(); + if (IERC721(token).ownerOf(tokenId) != address(this)) revert ContractIsNotOwnerOfERC721Token(); + + IERC721(token).safeTransferFrom(address(this), to, tokenId); + + emit ERC721Recovered(token, to, tokenId); + } +} \ No newline at end of file diff --git a/src/EETH.sol b/src/EETH.sol index 9d8574122..1834d174f 100644 --- a/src/EETH.sol +++ b/src/EETH.sol @@ -11,9 +11,10 @@ import "@openzeppelin-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable. import "./interfaces/IeETH.sol"; import "./interfaces/ILiquidityPool.sol"; +import "./AssetRecovery.sol"; +import "./interfaces/IRoleRegistry.sol"; - -contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20PermitUpgradeable, IeETH { +contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20PermitUpgradeable, IeETH, AssetRecovery { using CountersUpgradeable for CountersUpgradeable.Counter; ILiquidityPool public liquidityPool; @@ -34,8 +35,14 @@ contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20P bytes32 private immutable _HASHED_VERSION; bytes32 private immutable _TYPE_HASH; + IRoleRegistry public roleRegistry; + + bytes32 public constant EETH_OPERATING_ADMIN_ROLE = keccak256("EETH_OPERATING_ADMIN_ROLE"); + event TransferShares( address indexed from, address indexed to, uint256 sharesValue); + error IncorrectRole(); + // TODO: Figure our what `name` and `version` are for constructor() { bytes32 hashedName = keccak256("EETH"); @@ -59,6 +66,11 @@ contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20P liquidityPool = ILiquidityPool(_liquidityPool); } + function initializeRoleRegistry(address _roleRegistry) external onlyOwner { + require(address(roleRegistry) == address(0x00), "already initialized"); + roleRegistry = IRoleRegistry(_roleRegistry); + } + function mintShares(address _user, uint256 _share) external onlyPoolContract { shares[_user] += _share; totalShares += _share; @@ -139,6 +151,21 @@ contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20P _approve(owner, spender, value); } + function recoverETH(address payable to, uint256 amount) external { + if(!roleRegistry.hasRole(EETH_OPERATING_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _recoverETH(to, amount); + } + + function recoverERC20(address token, address to, uint256 amount) external { + if(!roleRegistry.hasRole(EETH_OPERATING_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _recoverERC20(token, to, amount); + } + + function recoverERC721(address token, address to, uint256 tokenId) external { + if(!roleRegistry.hasRole(EETH_OPERATING_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _recoverERC721(token, to, tokenId); + } + // [INTERNAL FUNCTIONS] function _transfer(address _sender, address _recipient, uint256 _amount) internal { uint256 _sharesToTransfer = liquidityPool.sharesForAmount(_amount); @@ -166,8 +193,10 @@ contract EETH is IERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, IERC20P } function _authorizeUpgrade( - address newImplementation - ) internal override onlyOwner {} + address /* newImplementation */ + ) internal view override { + roleRegistry.onlyProtocolUpgrader(msg.sender); + } function _useNonce(address owner) internal virtual returns (uint256 current) { CountersUpgradeable.Counter storage nonce = _nonces[owner]; diff --git a/src/WeETH.sol b/src/WeETH.sol index d0cdc4bb2..a589817a1 100644 --- a/src/WeETH.sol +++ b/src/WeETH.sol @@ -9,7 +9,10 @@ import "./interfaces/IeETH.sol"; import "./interfaces/ILiquidityPool.sol"; import "./interfaces/IRateProvider.sol"; -contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable, IRateProvider { +import "./AssetRecovery.sol"; +import "./interfaces/IRoleRegistry.sol"; + +contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20PermitUpgradeable, IRateProvider, AssetRecovery { //-------------------------------------------------------------------------------------- //--------------------------------- STATE-VARIABLES ---------------------------------- //-------------------------------------------------------------------------------------- @@ -17,6 +20,13 @@ contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20Pe IeETH public eETH; ILiquidityPool public liquidityPool; + IRoleRegistry public roleRegistry; + + bytes32 public constant WE_ETH_OPERATING_ADMIN_ROLE = keccak256("EETH_OPERATING_ADMIN_ROLE"); + + error IncorrectRole(); + error CannotRecoverEETH(); + //-------------------------------------------------------------------------------------- //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ //-------------------------------------------------------------------------------------- @@ -38,6 +48,11 @@ contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20Pe liquidityPool = ILiquidityPool(_liquidityPool); } + function initializeRoleRegistry(address _roleRegistry) external onlyOwner { + require(address(roleRegistry) == address(0x00), "already initialized"); + roleRegistry = IRoleRegistry(_roleRegistry); + } + /// @dev name changed from the version initially deployed function name() public view virtual override returns (string memory) { return "Wrapped eETH"; @@ -76,14 +91,20 @@ contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20Pe return eETHAmount; } - /// @notice Transfer weEth out of treasury to the owner - /// @dev The address 0x6329004E903B7F420245E7aF3f355186f2432466 is EtherFi's deprecated address - /// to receive the rewards. Since it doesn't have functionality to transfer out ERC20 tokens, we - /// need a function here to rescue those funds - function rescueTreasuryWeeth() public onlyOwner { - address treasury = 0x6329004E903B7F420245E7aF3f355186f2432466; - uint256 treasuryBal = balanceOf(treasury); - _transfer(treasury, msg.sender, treasuryBal); + function recoverETH(address payable to, uint256 amount) external { + if(!roleRegistry.hasRole(WE_ETH_OPERATING_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _recoverETH(to, amount); + } + + function recoverERC20(address token, address to, uint256 amount) external { + if(!roleRegistry.hasRole(WE_ETH_OPERATING_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + if (token == address(eETH)) revert CannotRecoverEETH(); + _recoverERC20(token, to, amount); + } + + function recoverERC721(address token, address to, uint256 tokenId) external { + if(!roleRegistry.hasRole(WE_ETH_OPERATING_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _recoverERC721(token, to, tokenId); } //-------------------------------------------------------------------------------------- @@ -91,8 +112,11 @@ contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20Pe //-------------------------------------------------------------------------------------- function _authorizeUpgrade( - address newImplementation - ) internal override onlyOwner {} + address /* newImplementation */ + ) internal view override { + roleRegistry.onlyProtocolUpgrader(msg.sender); + } + //-------------------------------------------------------------------------------------- //------------------------------------ GETTERS --------------------------------------- diff --git a/src/interfaces/IRoleRegistry.sol b/src/interfaces/IRoleRegistry.sol new file mode 100644 index 000000000..b67fea7aa --- /dev/null +++ b/src/interfaces/IRoleRegistry.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/** + * @title IRoleRegistry + * @notice Interface for the RoleRegistry contract + * @dev Defines the external interface for RoleRegistry with role management functions + * @author ether.fi + */ +interface IRoleRegistry { + /** + * @dev Error thrown when a function is called by an account without the protocol upgrader role + */ + error OnlyProtocolUpgrader(); + + /** + * @notice Returns the maximum allowed role value + * @dev This is used by EnumerableRoles._validateRole to ensure roles are within valid range + * @return The maximum role value + */ + function MAX_ROLE() external pure returns (uint256); + + /** + * @notice Initializes the contract with the specified owner + * @param _owner The address that will be set as the initial owner + */ + function initialize(address _owner) external; + + /** + * @notice Checks if an account has any of the specified roles + * @dev Reverts if the account doesn't have at least one of the roles + * @param account The address to check roles for + * @param encodedRoles ABI encoded roles (abi.encode(ROLE_1, ROLE_2, ...)) + */ + function checkRoles(address account, bytes memory encodedRoles) external view; + + /** + * @notice Checks if an account has a specific role + * @param role The role to check (as bytes32) + * @param account The address to check the role for + * @return bool True if the account has the role, false otherwise + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @notice Grants a role to an account + * @dev Only callable by the contract owner + * @param role The role to grant (as bytes32) + * @param account The address to grant the role to + */ + function grantRole(bytes32 role, address account) external; + + /** + * @notice Revokes a role from an account + * @dev Only callable by the contract owner + * @param role The role to revoke (as bytes32) + * @param account The address to revoke the role from + */ + function revokeRole(bytes32 role, address account) external; + + /** + * @notice Gets all addresses that have a specific role + * @dev Wrapper around EnumerableRoles roleHolders function + * @param role The role to query (as bytes32) + * @return Array of addresses that have the specified role + */ + function roleHolders(bytes32 role) external view returns (address[] memory); + + /** + * @notice Checks if an account is the protocol upgrader + * @dev Reverts if the account is not the protocol upgrader + * @param account The address to check + */ + function onlyProtocolUpgrader(address account) external view; + + /** + * @notice Returns the PROTOCOL_PAUSER role identifier + * @return The bytes32 identifier for the PROTOCOL_PAUSER role + */ + function PROTOCOL_PAUSER() external view returns (bytes32); + + /** + * @notice Returns the PROTOCOL_UNPAUSER role identifier + * @return The bytes32 identifier for the PROTOCOL_UNPAUSER role + */ + function PROTOCOL_UNPAUSER() external view returns (bytes32); + + /** + * @notice Returns the current owner of the contract + * @return The address of the current owner + */ + function owner() external view returns (address); +} \ No newline at end of file From f96622aa4ff512c371764c44a46b22dbf8ae1192 Mon Sep 17 00:00:00 2001 From: Shivam Agrawal Date: Wed, 7 May 2025 12:22:31 +0530 Subject: [PATCH 13/47] fix role name --- src/WeETH.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WeETH.sol b/src/WeETH.sol index a589817a1..6b2d3a407 100644 --- a/src/WeETH.sol +++ b/src/WeETH.sol @@ -22,7 +22,7 @@ contract WeETH is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable, ERC20Pe IRoleRegistry public roleRegistry; - bytes32 public constant WE_ETH_OPERATING_ADMIN_ROLE = keccak256("EETH_OPERATING_ADMIN_ROLE"); + bytes32 public constant WE_ETH_OPERATING_ADMIN_ROLE = keccak256("WE_ETH_OPERATING_ADMIN_ROLE"); error IncorrectRole(); error CannotRecoverEETH(); From 31450ac4fa37b57bad1f422727204b45052478b6 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 7 May 2025 13:34:28 -0600 Subject: [PATCH 14/47] continue core cleanup --- src/EtherFiNode.sol | 20 ++++++--- src/EtherFiNodesManager.sol | 56 +++++++------------------ src/StakingManager.sol | 21 ++++++---- src/helpers/EtherFiViewer.sol | 10 +++-- src/interfaces/IEtherFiNodesManager.sol | 51 +++++++++++----------- test/EigenLayerIntegration.t.sol | 6 +-- test/TestSetup.sol | 8 ++-- 7 files changed, 82 insertions(+), 90 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 1d8b5b51f..5587f9f28 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -23,6 +23,21 @@ contract EtherFiNode is IEtherFiNode { IDelegationManager public immutable delegationManager; uint32 public constant EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS = 100800; + //--------------------------------------------------------------------------- + //----------------------------- Storage ----------------------------------- + //--------------------------------------------------------------------------- + + // TODO: Legacy storage struct + + //-------------------------------------------------------------------------------------- + //------------------------------------- ROLES --------------------------------------- + //-------------------------------------------------------------------------------------- + + bytes32 public constant ETHERFI_NODE_ADMIN_ROLE = keccak256("ETHERFI_NODE_ADMIN_ROLE"); + + //------------------------------------------------------------------------- + //----------------------------- Admin ----------------------------------- + //------------------------------------------------------------------------- constructor(address _liquidityPool, address _etherFiNodesManager, address _eigenPodManager, address _delegationManager) { liquidityPool = ILiquidityPool(_liquidityPool); @@ -34,11 +49,6 @@ contract EtherFiNode is IEtherFiNode { roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); } - //-------------------------------------------------------------------------------------- - //------------------------------------- ROLES --------------------------------------- - //-------------------------------------------------------------------------------------- - - bytes32 public constant ETHERFI_NODE_ADMIN_ROLE = keccak256("ETHERFI_NODE_ADMIN_ROLE"); //-------------------------------------------------------------------------------------- //---------------------------- Eigenlayer Interactions -------------------------------- diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 71ec0eae9..3f8fe0b3a 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -26,10 +26,6 @@ contract EtherFiNodesManager is UUPSUpgradeable { - // TODO(dave): these are only used by viewer so we should just move them there - address public immutable eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); - address public immutable delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); - address public immutable stakingManager; IRoleRegistry public immutable roleRegistry; @@ -38,13 +34,20 @@ contract EtherFiNodesManager is //--------------------------------------------------------------------------- LegacyNodesManagerState private legacyState; - // Call Forwarding: functionSelector -> allowed - mapping(bytes4 => bool) public allowedForwardedEigenpodCalls; - // Call Forwarding: functionSelector -> targetAddress -> allowed - mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; - + mapping(bytes4 => bool) public allowedForwardedEigenpodCalls; // Call Forwarding: functionSelector -> allowed + mapping(bytes4 => mapping(address => bool)) public allowedForwardedExternalCalls; // Call Forwarding: functionSelector -> targetAddress -> allowed mapping(bytes32 => IEtherFiNode) public etherFiNodeFromPubkeyHash; + //-------------------------------------------------------------------------------------- + //------------------------------------- ROLES --------------------------------------- + //-------------------------------------------------------------------------------------- + + bytes32 public constant ETHERFI_NODES_MANAGER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_ADMIN_ROLE"); + bytes32 public constant ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE"); + + //------------------------------------------------------------------------- + //----------------------------- Admin ----------------------------------- + //------------------------------------------------------------------------- /// @custom:oz-upgrades-unsafe-allow constructor constructor(address _stakingManager) { @@ -58,46 +61,20 @@ contract EtherFiNodesManager is function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} - receive() external payable {} - - // TODO(dave): reimplement pausing with role registry function pauseContract() external { if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender)) revert IncorrectRole(); _pause(); } + function unPauseContract() external { if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_UNPAUSER(), msg.sender)) revert IncorrectRole(); _unpause(); } - //-------------------------------------------------------------------------------------- - //------------------------------------- ROLES --------------------------------------- - //-------------------------------------------------------------------------------------- - - bytes32 public constant ETHERFI_NODES_MANAGER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_ADMIN_ROLE"); - bytes32 public constant ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE"); - - function etherFiNodeFromId(uint256 id) public view returns (address) { - // if the ID is a legacy validatorID use the old storage array - // otherwise assume it is a pubkey hash. - // In a future upgrade we can fully remove the legacy path - - // heuristic that if a pubkey hash, at least 1 bit of higher order bits must be 1 - // all of the legacy id's were incrementing integers that will not have those bits set - uint256 mask = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000; - if (mask & id > 0) { - return address(etherFiNodeFromPubkeyHash[bytes32(id)]); - } else { - return legacyState.DEPRECATED_etherfiNodeAddress[id]; - } - } - //-------------------------------------------------------------------------------------- //---------------------------- Eigenlayer Interactions -------------------------------- //-------------------------------------------------------------------------------------- - // TODO(dave): permissions - function getEigenPod(uint256 id) public view returns (address) { return address(IEtherFiNode(etherfiNodeAddress(id)).getEigenPod()); } @@ -148,11 +125,9 @@ contract EtherFiNodesManager is legacyState.DEPRECATED_etherfiNodeAddress[legacyId] = nodeAddress; etherFiNodeFromPubkeyHash[pubkeyHash] = IEtherFiNode(nodeAddress); - emit PubkeyLinked(pubkeyHash, nodeAddress, pubkey); + emit PubkeyLinked(pubkeyHash, nodeAddress, legacyId, pubkey); } - // TODO(dave): is it better to revert if no address exists for provided id? - /// @notice get the etherFiNode instance associated with the provided ID. (Legacy validatorId or pubkeyHash) /// @dev Note that this ID can either be a a traditional etherfi validatorID or // a validatorPubkeyHash cast as a uint256. This was done maintaint compatibility @@ -172,7 +147,6 @@ contract EtherFiNodesManager is } } - //-------------------------------------------------------------------------------------- //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- @@ -255,7 +229,7 @@ contract EtherFiNodesManager is bytes4 selector = bytes4(data[i][:4]); if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); - IEtherFiNode node = IEtherFiNode(etherFiNodeFromId(ids[i])); + IEtherFiNode node = IEtherFiNode(etherfiNodeAddress(ids[i])); returnData[i] = node.forwardEigenPodCall(data[i]); } } diff --git a/src/StakingManager.sol b/src/StakingManager.sol index b4c71ba35..859b6e9a3 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -41,9 +41,22 @@ contract StakingManager is IBNFT public immutable bnft; IRoleRegistry public immutable roleRegistry; - // Storage + //--------------------------------------------------------------------------- + //----------------------------- Storage ----------------------------------- + //--------------------------------------------------------------------------- + LegacyStakingManagerState legacyState; + //--------------------------------------------------------------------------- + //--------------------------- ROLES --------------------------------------- + //--------------------------------------------------------------------------- + + bytes32 public constant STAKING_MANAGER_NODE_CREATOR_ROLE = keccak256("STAKING_MANAGER_NODE_CREATOR_ROLE"); + + //------------------------------------------------------------------------- + //----------------------------- Admin ----------------------------------- + //------------------------------------------------------------------------- + constructor( address _liquidityPool, address _etherFiNodesManager, @@ -79,12 +92,6 @@ contract StakingManager is _unpause(); } - //--------------------------------------------------------------------------- - //--------------------------- ROLES --------------------------------------- - //--------------------------------------------------------------------------- - - bytes32 public constant STAKING_MANAGER_NODE_CREATOR_ROLE = keccak256("STAKING_MANAGER_NODE_CREATOR_ROLE"); - //--------------------------------------------------------------------------- //------------------------- Deposit Flow ------------------------------------ //--------------------------------------------------------------------------- diff --git a/src/helpers/EtherFiViewer.sol b/src/helpers/EtherFiViewer.sol index 750f0f624..b85be950b 100644 --- a/src/helpers/EtherFiViewer.sol +++ b/src/helpers/EtherFiViewer.sol @@ -13,6 +13,10 @@ import "src/helpers/AddressProvider.sol"; contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { + // TODO(dave): add to constructor? + address public immutable eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); + address public immutable delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); + AddressProvider addressProvider; IEtherFiNodesManager nodesManager; @@ -27,15 +31,15 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { } function _getDelegationManager() internal view returns (IDelegationManager) { - return IDelegationManager(nodesManager.delegationManager()); + return IDelegationManager(delegationManager); } function _getEigenPodManager() internal view returns (IEigenPodManager) { - return IEigenPodManager(nodesManager.eigenPodManager()); + return IEigenPodManager(eigenPodManager); } function _getEtherFiNode(uint256 _validatorId) internal view returns (IEtherFiNode) { - return IEtherFiNode(nodesManager.etherFiNodeFromId(_validatorId)); + return IEtherFiNode(nodesManager.etherfiNodeAddress(_validatorId)); } function _getEigenPod(uint256 _validatorId) internal view returns (IEigenPod) { diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 4ce72a657..fbeb25a8b 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -18,7 +18,29 @@ import "../interfaces/IStakingManager.sol"; interface IEtherFiNodesManager { function etherFiNodeFromPubkeyHash(bytes32 pubkeyHash) external view returns (IEtherFiNode); - function etherFiNodeFromId(uint256 id) external view returns (address); + + function addressToWithdrawalCredentials(address addr) external pure returns (bytes memory); + function etherfiNodeAddress(uint256 id) external view returns(address); + function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; + + function stakingManager() external view returns (address); + + // eigenlayer interactions + function getEigenPod(uint256 id) external view returns (address); + function startCheckpoint(uint256 id) external; + function setProofSubmitter(uint256 id, address _newProofSubmitter) external; + + // call forwarding + function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; + function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external; + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); + function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external returns (bytes[] memory returnData); + function forwardEigenPodCall(address[] calldata nodes, bytes[] calldata data) external returns (bytes[] memory returnData); + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData); + + // protocol + function pauseContract() external; + function unPauseContract() external; struct LegacyNodesManagerState { uint256[4] legacyPadding1; @@ -78,36 +100,11 @@ interface IEtherFiNodesManager { */ } - function addressToWithdrawalCredentials(address addr) external pure returns (bytes memory); - function etherfiNodeAddress(uint256 id) external view returns(address); - function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external; - - function eigenPodManager() external view returns (address); - function delegationManager() external view returns (address); - function stakingManager() external view returns (address); - - // eigenlayer interactions - function getEigenPod(uint256 id) external view returns (address); - function startCheckpoint(uint256 id) external; - function setProofSubmitter(uint256 id, address _newProofSubmitter) external; - - // call forwarding - function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; - function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external; - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); - function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external returns (bytes[] memory returnData); - function forwardEigenPodCall(address[] calldata nodes, bytes[] calldata data) external returns (bytes[] memory returnData); - function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData); - - // protocol - function pauseContract() external; - function unPauseContract() external; - //--------------------------------------------------------------------------- //----------------------------- Events ----------------------------------- //--------------------------------------------------------------------------- - event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, bytes pubkey); + event PubkeyLinked(bytes32 indexed pubkeyHash, address indexed nodeAddress, uint256 indexed legacyId, bytes pubkey); event AllowedForwardedExternalCallsUpdated(bytes4 indexed selector, address indexed _target, bool _allowed); event AllowedForwardedEigenpodCallsUpdated(bytes4 indexed selector, bool _allowed); diff --git a/test/EigenLayerIntegration.t.sol b/test/EigenLayerIntegration.t.sol index 6486accd8..988867aca 100644 --- a/test/EigenLayerIntegration.t.sol +++ b/test/EigenLayerIntegration.t.sol @@ -52,7 +52,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { // {EigenPod, EigenPodOwner} used in EigenLayer's unit test eigenPod = IEigenPod(managerInstance.getEigenPod(validatorId)); - podOwner = managerInstance.etherFiNodeFromId(validatorId); + podOwner = managerInstance.etherfiNodeAddress(validatorId); validatorIds = new uint256[](1); validatorIds[0] = validatorId; ws = EtherFiNode(payable(podOwner)); @@ -121,7 +121,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { function create_validator() public returns (uint256, address, EtherFiNode) { uint256[] memory validatorIds = launch_validator(1, 0, true); - address nodeAddress = managerInstance.etherFiNodeFromId(validatorIds[0]); + address nodeAddress = managerInstance.etherfiNodeAddress(validatorIds[0]); EtherFiNode node = EtherFiNode(payable(nodeAddress)); return (validatorIds[0], nodeAddress, node); @@ -295,7 +295,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { validatorIds[0] = 338; uint32[] memory timeStamps = new uint32[](1); timeStamps[0] = 0; - address nodeAddress = managerInstance.etherFiNodeFromId(validatorIds[0]); + address nodeAddress = managerInstance.etherfiNodeAddress(validatorIds[0]); IDelegationManager mgr = managerInstance.delegationManager(); diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 4aa4edfde..c5ee1d4ba 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -1205,7 +1205,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes32 depositRoot = generateDepositRoot( hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.addressToWithdrawalCredentials(managerInstance.etherFiNodeFromId(createdBids[0])), + managerInstance.addressToWithdrawalCredentials(managerInstance.etherfiNodeAddress(createdBids[0])), 32 ether ); IStakingManager.DepositData memory depositData = IStakingManager @@ -1517,7 +1517,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes[] memory pubKey = new bytes[](_validatorIds.length); for (uint256 i = 0; i < _validatorIds.length; i++) { - address etherFiNode = managerInstance.etherFiNodeFromId(_validatorIds[i]); + address etherFiNode = managerInstance.etherfiNodeAddress(_validatorIds[i]); pubKey[i] = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; bytes32 root = generateDepositRoot( pubKey[i], @@ -1552,7 +1552,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen bytes32 root = generateDepositRoot( pubKey[i], hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.addressToWithdrawalCredentials(managerInstance.etherFiNodeFromId(_validatorIds[i])), + managerInstance.addressToWithdrawalCredentials(managerInstance.etherfiNodeAddress(_validatorIds[i])), 1 ether ); depositDataArray[i] = IStakingManager.DepositData({ @@ -1565,7 +1565,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen depositDataRootsForApproval[i] = generateDepositRoot( pubKey[i], hex"ad899d85dcfcc2506a8749020752f81353dd87e623b2982b7bbfbbdd7964790eab4e06e226917cba1253f063d64a7e5407d8542776631b96c4cea78e0968833b36d4e0ae0b94de46718f905ca6d9b8279e1044a41875640f8cb34dc3f6e4de65", - managerInstance.addressToWithdrawalCredentials(managerInstance.etherFiNodeFromId(_validatorIds[i])), + managerInstance.addressToWithdrawalCredentials(managerInstance.etherfiNodeAddress(_validatorIds[i])), 31 ether ); From a489697824a276b452cba8b5cf7dad55fe4b99ca Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 7 May 2025 21:05:52 -0600 Subject: [PATCH 15/47] add admin method to fill in old pubkeys --- src/EtherFiNodesManager.sol | 22 ++++++++++++++++++++-- src/interfaces/IEtherFiNode.sol | 9 ++++++++- src/interfaces/IEtherFiNodesManager.sol | 14 ++------------ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 3f8fe0b3a..1c97cf22d 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -13,8 +13,6 @@ import "./interfaces/IEtherFiNode.sol"; import "./interfaces/IEtherFiNodesManager.sol"; import "./interfaces/IProtocolRevenueManager.sol"; import "./interfaces/IStakingManager.sol"; -import "./interfaces/ITNFT.sol"; -import "./interfaces/IBNFT.sol"; import "./interfaces/IRoleRegistry.sol"; contract EtherFiNodesManager is @@ -147,6 +145,26 @@ contract EtherFiNodesManager is } } + + /// @dev this method is for linking our old legacy validator ids that were created before + /// we started tracking the pubkeys onchain. We can delete this method once we have linked all of our legacy validators + function linkLegacyValidatorIds(uint256[] calldata validatorIds, bytes[] calldata pubkeys) external onlyAdmin { + if (validatorIds.length != pubkeys.length) revert LengthMismatch(); + for (uint256 i = 0; i < validatorIds.length; i++) { + + // lookup which node we are linking against + address nodeAddress = legacyState.DEPRECATED_etherfiNodeAddress[validatorIds[i]]; + if (nodeAddress == address(0)) revert UnknownNode(); + + // ensure we haven't already linked this pubkey + bytes32 pubkeyHash = calculateValidatorPubkeyHash(pubkeys[i]); + if (address(etherFiNodeFromPubkeyHash[pubkeyHash]) != address(0)) revert AlreadyLinked(); + + etherFiNodeFromPubkeyHash[pubkeyHash] = IEtherFiNode(nodeAddress); + emit PubkeyLinked(pubkeyHash, nodeAddress, validatorIds[i], pubkeys[i]); + } + } + //-------------------------------------------------------------------------------------- //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index d32e7a905..78e3263be 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -8,7 +8,6 @@ import "../eigenlayer-interfaces/IEigenPod.sol"; interface IEtherFiNode { - // eigenlayer function createEigenPod() external returns (address); function getEigenPod() external view returns (IEigenPod); @@ -21,6 +20,14 @@ interface IEtherFiNode { function forwardEigenPodCall(bytes memory data) external returns (bytes memory); function forwardExternalCall(address to, bytes memory data) external returns (bytes memory); + //--------------------------------------------------------------------------- + //----------------------------- Events ----------------------------------- + //--------------------------------------------------------------------------- + + event PartialWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); + event FullWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury); + event QueuedRestakingWithdrawal(uint256 indexed _validatorId, address indexed etherFiNode, bytes32[] withdrawalRoots); + //-------------------------------------------------------------------------- //----------------------------- Errors ----------------------------------- //-------------------------------------------------------------------------- diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index fbeb25a8b..24229799f 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -1,19 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; - -import "./IEtherFiNode.sol"; - -import "../eigenlayer-interfaces/IEigenPodManager.sol"; -import "../eigenlayer-interfaces/IDelegationManager.sol"; -import "../eigenlayer-interfaces/IDelayedWithdrawalRouter.sol"; -import "../interfaces/IAuctionManager.sol"; import "../interfaces/IEtherFiNode.sol"; -import "../interfaces/IEtherFiNodesManager.sol"; -import "../interfaces/IProtocolRevenueManager.sol"; -import "../interfaces/IStakingManager.sol"; - - interface IEtherFiNodesManager { @@ -113,7 +101,9 @@ interface IEtherFiNodesManager { //-------------------------------------------------------------------------- error AlreadyLinked(); + error UnknownNode(); error InvalidPubKeyLength(); + error LengthMismatch(); error InvalidCaller(); error IncorrectRole(); From 3d4d958a0ba7a28e28f9d6fd6e7ee356bcd4bfe1 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 9 May 2025 14:28:56 -0600 Subject: [PATCH 16/47] update constructors for finalized fields --- src/EtherFiNode.sol | 9 ++---- src/EtherFiNodesManager.sol | 6 ++-- src/StakingManager.sol | 30 +++++++------------ src/interfaces/IEtherFiNode.sol | 49 ++++++++++++++++++++++++++++++++ test/ContractCodeChecker.t.sol | 4 +-- test/EigenLayerIntegration.t.sol | 6 ++-- test/TestSetup.sol | 8 +++--- test/prelude.t.sol | 17 ++++++----- 8 files changed, 83 insertions(+), 46 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 5587f9f28..cf621b399 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -27,7 +27,7 @@ contract EtherFiNode is IEtherFiNode { //----------------------------- Storage ----------------------------------- //--------------------------------------------------------------------------- - // TODO: Legacy storage struct + LegacyNodeState legacyState; // all legacy state in this contract has been deprecated //-------------------------------------------------------------------------------------- //------------------------------------- ROLES --------------------------------------- @@ -39,17 +39,14 @@ contract EtherFiNode is IEtherFiNode { //----------------------------- Admin ----------------------------------- //------------------------------------------------------------------------- - constructor(address _liquidityPool, address _etherFiNodesManager, address _eigenPodManager, address _delegationManager) { + constructor(address _liquidityPool, address _etherFiNodesManager, address _eigenPodManager, address _delegationManager, address _roleRegistry) { liquidityPool = ILiquidityPool(_liquidityPool); etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); eigenPodManager = IEigenPodManager(_eigenPodManager); delegationManager = IDelegationManager(_delegationManager); - - // TODO(dave): add to constructor - roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + roleRegistry = IRoleRegistry(_roleRegistry); } - //-------------------------------------------------------------------------------------- //---------------------------- Eigenlayer Interactions -------------------------------- //-------------------------------------------------------------------------------------- diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 1c97cf22d..29d0fea0f 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -48,11 +48,9 @@ contract EtherFiNodesManager is //------------------------------------------------------------------------- /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address _stakingManager) { + constructor(address _stakingManager, address _roleRegistry) { stakingManager = _stakingManager; - - // TODO(dave): add to constructor - roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + roleRegistry = IRoleRegistry(_roleRegistry); _disableInitializers(); } diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 859b6e9a3..cb7bdb1dd 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; -import "./interfaces/ITNFT.sol"; -import "./interfaces/IBNFT.sol"; import "./interfaces/IRoleRegistry.sol"; import "./interfaces/IAuctionManager.sol"; import "./interfaces/IStakingManager.sol"; @@ -31,21 +29,19 @@ contract StakingManager is UUPSUpgradeable { - UpgradeableBeacon private upgradableBeacon; address public immutable liquidityPool; uint256 public constant initialDepositAmount = 1 ether; IEtherFiNodesManager public immutable etherFiNodesManager; IDepositContract public immutable depositContractEth2; IAuctionManager public immutable auctionManager; - ITNFT public immutable tnft; - IBNFT public immutable bnft; + UpgradeableBeacon private etherFiNodeBeacon; IRoleRegistry public immutable roleRegistry; //--------------------------------------------------------------------------- //----------------------------- Storage ----------------------------------- //--------------------------------------------------------------------------- - LegacyStakingManagerState legacyState; + LegacyStakingManagerState legacyState; // all legacy state in this contract has been deprecated //--------------------------------------------------------------------------- //--------------------------- ROLES --------------------------------------- @@ -62,21 +58,15 @@ contract StakingManager is address _etherFiNodesManager, address _ethDepositContract, address _auctionManager, - address _tnft, - address _bnft, - address _etherfiOracle + address _etherFiNodeBeacon, + address _roleRegistry ) { liquidityPool = _liquidityPool; etherFiNodesManager = IEtherFiNodesManager(_etherFiNodesManager); depositContractEth2 = IDepositContract(_ethDepositContract); auctionManager = IAuctionManager(_auctionManager); - tnft = ITNFT(_tnft); - bnft = IBNFT(_bnft); - - // TODO(dave): add to constructor - upgradableBeacon = UpgradeableBeacon(address(0x01)); - // TODO(dave): add to constructor - roleRegistry = IRoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + etherFiNodeBeacon = UpgradeableBeacon(_etherFiNodeBeacon); + roleRegistry = IRoleRegistry(_roleRegistry); _disableInitializers(); } @@ -175,18 +165,18 @@ contract StakingManager is function upgradeEtherFiNode(address _newImplementation) public onlyOwner { if (_newImplementation == address(0)) revert InvalidUpgrade(); - upgradableBeacon.upgradeTo(_newImplementation); + etherFiNodeBeacon.upgradeTo(_newImplementation); } /// @notice Fetches the address of the beacon contract for future EtherFiNodes (withdrawal safes) function getEtherFiNodeBeacon() external view returns (address) { - return address(upgradableBeacon); + return address(etherFiNodeBeacon); } /// @notice Fetches the address of the implementation contract currently being used by the beacon proxy /// @return the address of the currently used implementation contract function implementation() public view override returns (address) { - return upgradableBeacon.implementation(); + return etherFiNodeBeacon.implementation(); } /// @dev create a new proxy instance of the etherFiNode withdrawal safe contract. @@ -194,7 +184,7 @@ contract StakingManager is function instantiateEtherFiNode(bool _createEigenPod) external returns (address) { if (!roleRegistry.hasRole(STAKING_MANAGER_NODE_CREATOR_ROLE, msg.sender)) revert IncorrectRole(); - BeaconProxy proxy = new BeaconProxy(address(upgradableBeacon), ""); + BeaconProxy proxy = new BeaconProxy(address(etherFiNodeBeacon), ""); address node = address(proxy); if (_createEigenPod) { IEtherFiNode(node).createEigenPod(); diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index 78e3263be..3554dc213 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -20,6 +20,55 @@ interface IEtherFiNode { function forwardEigenPodCall(bytes memory data) external returns (bytes memory); function forwardExternalCall(address to, bytes memory data) external returns (bytes memory); + struct LegacyNodeState { + uint256[10] legacyPadding; + /* + ╭---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------╮ + | Name | Type | Slot | Offset | Bytes | Contract | + +=================================================================================================================================================+ + | etherFiNodesManager | address | 0 | 0 | 20 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_localRevenueIndex | uint256 | 1 | 0 | 32 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_vestedAuctionRewards | uint256 | 2 | 0 | 32 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_ipfsHashForEncryptedValidatorKey | string | 3 | 0 | 32 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_exitRequestTimestamp | uint32 | 4 | 0 | 4 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_exitTimestamp | uint32 | 4 | 4 | 4 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_stakingStartTimestamp | uint32 | 4 | 8 | 4 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_phase | enum IEtherFiNode.VALIDATOR_PHASE | 4 | 12 | 1 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_restakingObservedExitBlock | uint32 | 4 | 13 | 4 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | eigenPod | address | 5 | 0 | 20 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | isRestakingEnabled | bool | 5 | 20 | 1 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | version | uint16 | 5 | 21 | 2 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | _numAssociatedValidators | uint16 | 5 | 23 | 2 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | numExitRequestsByTnft | uint16 | 5 | 25 | 2 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | numExitedValidators | uint16 | 5 | 27 | 2 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | associatedValidatorIndices | mapping(uint256 => uint256) | 6 | 0 | 32 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | associatedValidatorIds | uint256[] | 7 | 0 | 32 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_pendingWithdrawalFromRestakingInGwei | uint64 | 8 | 0 | 8 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_completedWithdrawalFromRestakingInGwei | uint64 | 8 | 8 | 8 | src/EtherFiNode.sol:EtherFiNode | + |---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------| + | DEPRECATED_restakingObservedExitBlocks | mapping(uint256 => uint32) | 9 | 0 | 32 | src/EtherFiNode.sol:EtherFiNode | + ╰---------------------------------------------------+-----------------------------------+------+--------+-------+---------------------------------╯ + */ + } + //--------------------------------------------------------------------------- //----------------------------- Events ----------------------------------- //--------------------------------------------------------------------------- diff --git a/test/ContractCodeChecker.t.sol b/test/ContractCodeChecker.t.sol index 104ebcbd1..58c59ad96 100644 --- a/test/ContractCodeChecker.t.sol +++ b/test/ContractCodeChecker.t.sol @@ -18,10 +18,10 @@ contract ContractCodeCheckerTest is TestSetup { function test_deployment_bytecode() public { // Create new implementations - EtherFiNodesManager etherFiNodesManagerImplementation = new EtherFiNodesManager(address(0x0)); + EtherFiNodesManager etherFiNodesManagerImplementation = new EtherFiNodesManager(address(0x0), address(0x0)); address etherFiNodesManagerImplAddress = address(0xE9EE6923D41Cf5F964F11065436BD90D4577B5e4); - EtherFiNode etherFiNodeImplementation = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); + EtherFiNode etherFiNodeImplementation = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0), address(0x0)); address etherFiNodeImplAddress = address(0xc5F2764383f93259Fba1D820b894B1DE0d47937e); EtherFiRestaker etherFiRestakerImplementation = new EtherFiRestaker(address(0x7750d328b314EfFa365A0402CcfD489B80B0adda)); diff --git a/test/EigenLayerIntegration.t.sol b/test/EigenLayerIntegration.t.sol index 988867aca..7070a042a 100644 --- a/test/EigenLayerIntegration.t.sol +++ b/test/EigenLayerIntegration.t.sol @@ -39,6 +39,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { bytes32[][] validatorFields; function setUp() public { + /* initializeRealisticFork(MAINNET_FORK); // yes bob! @@ -58,19 +59,17 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { ws = EtherFiNode(payable(podOwner)); // Override with Mock - /* vm.startPrank(eigenLayerEigenPodManager.owner()); beaconChainOracleMock = new BeaconChainOracleMock(); beaconChainOracle = IBeaconChainOracle(address(beaconChainOracleMock)); eigenLayerEigenPodManager.updateBeaconChainOracle(beaconChainOracle); vm.stopPrank(); - */ vm.startPrank(owner); liquidityPoolInstance.setRestakeBnftDeposits(true); vm.stopPrank(); - EtherFiNodesManager newManagerImpl = new EtherFiNodesManager(address(0x0)); + EtherFiNodesManager newManagerImpl = new EtherFiNodesManager(address(0x0), address(0x0)); EtherFiNode newNodeImpl = new EtherFiNode(address(0x0), address(0x0), address(0x0), address(0x0)); vm.startPrank(managerInstance.owner()); @@ -80,6 +79,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing { stakingManagerInstance.upgradeEtherFiNode(address(newNodeImpl)); vm.stopPrank(); + */ } diff --git a/test/TestSetup.sol b/test/TestSetup.sol index c5ee1d4ba..9baad9daf 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -494,7 +494,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen protocolRevenueManagerInstance.initialize(); protocolRevenueManagerInstance.updateAdmin(alice); - managerImplementation = new EtherFiNodesManager(address(stakingManagerInstance)); + managerImplementation = new EtherFiNodesManager(address(stakingManagerInstance), address(roleRegistryInstance)); etherFiNodeManagerProxy = new UUPSProxy(address(managerImplementation), ""); managerInstance = EtherFiNodesManager(payable(address(etherFiNodeManagerProxy))); @@ -517,7 +517,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen address delegationManager; address liquidityPool; address etherFiNodesManager; - node = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager); + node = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager, address(roleRegistryInstance)); rETH = new TestERC20("Rocket Pool ETH", "rETH"); @@ -1441,14 +1441,14 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen address liquidityPool; address etherFiNodesManager; - EtherFiNode etherFiNode = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager); + EtherFiNode etherFiNode = new EtherFiNode(eigenPodManager, delegationManager, liquidityPool, etherFiNodesManager, address(roleRegistryInstance)); address newImpl = address(etherFiNode); vm.prank(stakingManagerInstance.owner()); stakingManagerInstance.upgradeEtherFiNode(newImpl); } function _upgrade_etherfi_nodes_manager_contract() internal { - address newImpl = address(new EtherFiNodesManager(address(stakingManagerInstance))); + address newImpl = address(new EtherFiNodesManager(address(stakingManagerInstance), address(roleRegistryInstance))); vm.prank(managerInstance.owner()); managerInstance.upgradeTo(newImpl); } diff --git a/test/prelude.t.sol b/test/prelude.t.sol index d16aad82b..b5fb1d277 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -31,6 +31,7 @@ contract PreludeTest is Test, ArrayTestHelper { address stakingDepositContract = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); address eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); + address etherFiNodeBeacon = address(0x3c55986Cfee455E2533F4D29006634EcF9B7c03F); RoleRegistry roleRegistry = RoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); // i don't think i need this anymore @@ -47,8 +48,8 @@ contract PreludeTest is Test, ArrayTestHelper { liquidityPool = ILiquidityPool(0x308861A430be4cce5502d0A12724771Fc6DaF216); etherFiNodesManager = EtherFiNodesManager(payable(0x8B71140AD2e5d1E7018d2a7f8a288BD3CD38916F)); auctionManager = AuctionManager(0x00C452aFFee3a17d9Cecc1Bcd2B8d5C7635C4CB9); - tnft = ITNFT(0x7B5ae07E2AF1C861BcC4736D23f5f66A61E0cA5e); - bnft = IBNFT(0x6599861e55abd28b91dd9d86A826eC0cC8D72c2c); + //tnft = ITNFT(0x7B5ae07E2AF1C861BcC4736D23f5f66A61E0cA5e); + //bnft = IBNFT(0x6599861e55abd28b91dd9d86A826eC0cC8D72c2c); // deploy new staking manager implementation StakingManager stakingManagerImpl = new StakingManager( @@ -56,9 +57,10 @@ contract PreludeTest is Test, ArrayTestHelper { address(etherFiNodesManager), address(stakingDepositContract), address(auctionManager), - address(tnft), - address(bnft), - oracle + //address(tnft), + //address(bnft), + address(etherFiNodeBeacon), + address(roleRegistry) ); vm.prank(stakingManager.owner()); stakingManager.upgradeTo(address(stakingManagerImpl)); @@ -69,7 +71,8 @@ contract PreludeTest is Test, ArrayTestHelper { address(liquidityPool), address(etherFiNodesManager), eigenPodManager, - delegationManager + delegationManager, + address(roleRegistry) ); vm.prank(stakingManager.owner()); stakingManager.upgradeEtherFiNode(address(etherFiNodeImpl)); @@ -78,7 +81,7 @@ contract PreludeTest is Test, ArrayTestHelper { console2.log("epm:", address(etherFiNodeImpl.eigenPodManager())); // deploy new efnm implementation - EtherFiNodesManager etherFiNodesManagerImpl = new EtherFiNodesManager(address(stakingManager)); + EtherFiNodesManager etherFiNodesManagerImpl = new EtherFiNodesManager(address(stakingManager), address(roleRegistry)); vm.prank(etherFiNodesManager.owner()); etherFiNodesManager.upgradeTo(address(etherFiNodesManagerImpl)); From 5d16755a3cba3b01c96d92816cae2034c9a596e1 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 13 May 2025 18:18:39 -0600 Subject: [PATCH 17/47] liquidity pool updates for compatibility --- src/EtherFiAdmin.sol | 8 +- src/EtherFiNodesManager.sol | 8 +- src/LiquidityPool.sol | 227 ++++++++++++----------------- src/StakingManager.sol | 4 - src/interfaces/ILiquidityPool.sol | 12 +- src/interfaces/IStakingManager.sol | 1 + test/LiquidityPool.t.sol | 107 ++------------ test/TestSetup.sol | 15 +- 8 files changed, 124 insertions(+), 258 deletions(-) diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index f5f88a840..4c749a862 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -218,15 +218,13 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { validatorManagementTaskStatus[taskHash].completed = true; if (taskType == TaskType.ValidatorApproval) { - liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures); + liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures); } else if (taskType == TaskType.SendExitRequests) { liquidityPool.sendExitRequests(_validators); } else if (taskType == TaskType.ProcessNodeExit) { - // TODO(dave): re-implement for V3 - // etherFiNodesManager.processNodeExit(_validators, _timestamps); + // nothing to do } else if (taskType == TaskType.MarkBeingSlashed) { - // TODO(dave): re-imprement for V3 - //etherFiNodesManager.markBeingSlashed(_validators); + // nothing to do } emit ValidatorManagementTaskCompleted(taskHash, _reportHash, _validators, _timestamps, taskType); } diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 29d0fea0f..07327136c 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -79,19 +79,19 @@ contract EtherFiNodesManager is return IEtherFiNode(etherfiNodeAddress(id)).createEigenPod(); } - function startCheckpoint(uint256 id) external onlyAdmin { + function startCheckpoint(uint256 id) external onlyCallForwarder { IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); } - function setProofSubmitter(uint256 id, address proofSubmitter) external onlyAdmin { + function setProofSubmitter(uint256 id, address proofSubmitter) external onlyCallForwarder { IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } - function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { + function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external onlyCallForwarder returns (bytes32 withdrawalRoot) { return IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawal(params); } - function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external onlyAdmin { + function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external onlyCallForwarder { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(receiveAsTokens); } diff --git a/src/LiquidityPool.sol b/src/LiquidityPool.sol index 3e459dce8..ed7b4b327 100644 --- a/src/LiquidityPool.sol +++ b/src/LiquidityPool.sol @@ -2,28 +2,22 @@ pragma solidity ^0.8.13; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; -import "@openzeppelin-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol"; -import "@openzeppelin-upgradeable/contracts/token/ERC721/IERC721ReceiverUpgradeable.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; -import "./interfaces/IRegulationsManager.sol"; -import "./interfaces/IEtherFiNodesManager.sol"; +import "./EtherFiRedemptionManager.sol"; import "./interfaces/IeETH.sol"; import "./interfaces/IStakingManager.sol"; -import "./interfaces/IMembershipManager.sol"; -import "./interfaces/ITNFT.sol"; import "./interfaces/IWithdrawRequestNFT.sol"; import "./interfaces/ILiquidityPool.sol"; -import "./interfaces/IEtherFiAdmin.sol"; -import "./interfaces/IAuctionManager.sol"; import "./interfaces/ILiquifier.sol"; -import "./RoleRegistry.sol"; +import "./interfaces/IEtherFiNode.sol"; +import "./interfaces/IEtherFiNodesManager.sol"; +import "./interfaces/IRoleRegistry.sol"; +import "./libraries/DepositRootGenerator.sol"; -import "./EtherFiRedemptionManager.sol"; contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, ILiquidityPool { @@ -34,9 +28,9 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL IStakingManager public stakingManager; IEtherFiNodesManager public nodesManager; - IRegulationsManager public DEPRECATED_regulationsManager; - IMembershipManager public membershipManager; - ITNFT public tNft; + address public DEPRECATED_regulationsManager; + address public membershipManager; + address public DEPRECATED_TNFT; IeETH public eETH; bool public DEPRECATED_eEthliquidStakingOpened; @@ -68,14 +62,15 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL bool public restakeBnftDeposits; uint128 public ethAmountLockedForWithdrawal; bool public paused; - IAuctionManager public auctionManager; + address public DEPRECATED_auctionManager; ILiquifier public liquifier; bool private DEPRECATED_isLpBnftHolder; EtherFiRedemptionManager public etherFiRedemptionManager; - RoleRegistry public roleRegistry; + IRoleRegistry public roleRegistry; + uint256 public validatorSizeWei; //-------------------------------------------------------------------------------------- //------------------------------------- ROLES --------------------------------------- //-------------------------------------------------------------------------------------- @@ -111,6 +106,8 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL error InsufficientLiquidity(); error SendFail(); error IncorrectRole(); + error InvalidEtherFiNode(); + error InvalidValidatorSize(); //-------------------------------------------------------------------------------------- //---------------------------- STATE-CHANGING FUNCTIONS ------------------------------ @@ -135,8 +132,8 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL eETH = IeETH(_eEthAddress); stakingManager = IStakingManager(_stakingManagerAddress); nodesManager = IEtherFiNodesManager(_nodesManagerAddress); - membershipManager = IMembershipManager(_membershipManagerAddress); - tNft = ITNFT(_tNftAddress); + membershipManager = _membershipManagerAddress; + DEPRECATED_TNFT = _tNftAddress; paused = true; restakeBnftDeposits = false; ethAmountLockedForWithdrawal = 0; @@ -146,9 +143,9 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL } function initializeOnUpgrade(address _auctionManager, address _liquifier) external onlyOwner { - require(_auctionManager != address(0) && _liquifier != address(0) && address(auctionManager) == address(0) && address(liquifier) == address(0), "Invalid"); + require(_auctionManager != address(0) && _liquifier != address(0) && address(DEPRECATED_auctionManager) == address(0) && address(liquifier) == address(0), "Invalid"); - auctionManager = IAuctionManager(_auctionManager); + DEPRECATED_auctionManager = _auctionManager; liquifier = ILiquifier(_liquifier); } @@ -158,7 +155,7 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL require(address(roleRegistry) == address(0x00), "already initialized"); etherFiRedemptionManager = EtherFiRedemptionManager(payable(_etherFiRedemptionManager)); - roleRegistry = RoleRegistry(_roleRegistry); + roleRegistry = IRoleRegistry(_roleRegistry); //correct splits uint128 tvl = uint128(getTotalPooledEther()); @@ -167,7 +164,11 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL if(tvl != getTotalPooledEther()) revert(); } - + + function initializeV3Prelude(uint256 _validatorSizeWei) external onlyOwner { + validatorSizeWei = _validatorSizeWei; + } + // Used by eETH staking flow function deposit() external payable returns (uint256) { return deposit(address(0)); @@ -276,148 +277,102 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL return requestId; } + + //--------------------------------------------------------------------------- + //------------------------- Deposit Flow ------------------------------------ + //--------------------------------------------------------------------------- + // [Liquidty Pool Staking flow] // Step 1: (Off-chain) create the keys using the desktop app // Step 2: create validators with 1 eth deposits to official deposit contract // Step 3: oracle approves and funds the remaining balance for the validator - /// Step 2. createValidators sending 1 eth deposits for each - /// @param _candidateBidIds validator IDs that have been matched with the BNFT holder on the FE - /// @param _numberOfValidators how many validators the user wants to spin up. This can be less than the candidateBidIds length. - function batchDeposit(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external whenNotPaused returns (uint256[] memory) { - return batchDeposit(_candidateBidIds, _numberOfValidators, 0); - } - - /// @param _candidateBidIds the bid IDs of the node operators that the spawner wants to spin up validators for - /// @param _numberOfValidators how many validators the user wants to spin up; `len(_candidateBidIds)` must be >= `_numberOfValidators` - /// @param _validatorIdToShareSafeWith the validator ID of the validator that the spawner wants to shafe the withdrawal safe with - /// @return Array of bid IDs that were successfully processed. - function batchDeposit(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators, uint256 _validatorIdToShareSafeWith) public whenNotPaused returns (uint256[] memory) { - address tnftHolder = address(this); - address bnftHolder = address(this); - - require(validatorSpawner[msg.sender].registered, "Incorrect Caller"); - require(totalValueInLp >= 32 ether * _numberOfValidators, "Not enough balance"); - - /* - uint256[] memory newValidators = stakingManager.batchDepositWithBidIds(_candidateBidIds, _numberOfValidators, msg.sender, tnftHolder, bnftHolder, SourceOfFunds.EETH, restakeBnftDeposits, _validatorIdToShareSafeWith); - numPendingDeposits += uint32(newValidators.length); - - return newValidators; - */ - uint256[] memory newValidators = new uint256[](_candidateBidIds.length); - return newValidators; - } - - /// Step 3. [Register] - /// @notice register validators' keys and trigger a 1 ETH transaction to the beacon chain. - /// @param _validatorIds the ids of the validators to register - /// @param _registerValidatorDepositData the signature and deposit data root for a 1 ETH deposit - /// @param _depositDataRootApproval the root hash of the deposit data for the 31 ETH deposit which will happen in the approval step - /// @param _signaturesForApprovalDeposit the signature for the 31 ETH deposit which will happen in the approval step. + /// @dev step 2 of staking flow function batchRegister( - bytes32 _depositRoot, - uint256[] calldata _validatorIds, - IStakingManager.DepositData[] calldata _registerValidatorDepositData, - bytes32[] calldata _depositDataRootApproval, - bytes[] calldata _signaturesForApprovalDeposit + IStakingManager.DepositData[] calldata _depositData, + uint256[] calldata _bidIds, + address _etherFiNode ) external whenNotPaused { require(validatorSpawner[msg.sender].registered, "Incorrect Caller"); - // TODO(dave): add as param. - address DebugNodeAddress = address(0x1234); - - // As the LP is the B-nft holder, the 1 ether (for each validator) is taken from the LP - uint256 outboundEthAmountFromLp = 1 ether * _validatorIds.length; + // liquidity pool supplies 1 eth per validator + uint256 outboundEthAmountFromLp = 1 ether * _bidIds.length; _accountForEthSentOut(outboundEthAmountFromLp); - stakingManager.createBeaconValidators{value: outboundEthAmountFromLp}(_registerValidatorDepositData, _validatorIds, DebugNodeAddress); - - /* - address _bnftRecipient = address(this); - - require(validatorSpawner[msg.sender].registered, "Incorrect Caller"); - require(_validatorIds.length == _registerValidatorDepositData.length && _validatorIds.length == _depositDataRootApproval.length && _validatorIds.length == _signaturesForApprovalDeposit.length, "lengths differ"); - - numPendingDeposits -= uint32(_validatorIds.length); - - // As the LP is the B-nft holder, the 1 ether (for each validator) is taken from the LP - uint256 outboundEthAmountFromLp = 1 ether * _validatorIds.length; - _accountForEthSentOut(outboundEthAmountFromLp); - - stakingManager.batchRegisterValidators{value: outboundEthAmountFromLp}(_depositRoot, _validatorIds, _bnftRecipient, address(this), _registerValidatorDepositData, msg.sender); - - for(uint256 i; i < _validatorIds.length; i++) { - depositDataRootForApprovalDeposits[_validatorIds[i]] = _depositDataRootApproval[i]; - emit ValidatorRegistered(_validatorIds[i], _signaturesForApprovalDeposit[i], _registerValidatorDepositData[i].publicKey, _depositDataRootApproval[i]); - } - */ + stakingManager.createBeaconValidators{value: outboundEthAmountFromLp}(_depositData, _bidIds, _etherFiNode); } - //. Step 4. [Approve] - /// @notice Approves validators and triggers the 31 ETH deposit to the beacon chain - /// @dev This gets called by the Oracle only when it has confirmed the withdraw credentials of the 1 ETH deposit in the registration - /// phase match the withdraw credentials stored on the beacon chain. This prevents a front-running attack. - /// @param _validatorIds the IDs of the validators to be approved - /// @param _pubKey the pubKey for each validator being spun up. - /// @param _signature the signatures for each validator for the 31 ETH deposit that were emitted in the register phase - function batchApproveRegistration( - uint256[] memory _validatorIds, - bytes[] calldata _pubKey, - bytes[] calldata _signature + /// @dev step 3 of staking flow + function batchApproveRegistration( + uint256[] memory _validatorIds, + bytes[] calldata _pubkeys, + bytes[] calldata _signatures ) external whenNotPaused { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - /* - require(_validatorIds.length == _pubKey.length && _validatorIds.length == _signature.length, "lengths differ"); - - bytes32[] memory depositDataRootApproval = new bytes32[](_validatorIds.length); - for(uint256 i; i < _validatorIds.length; i++) { - depositDataRootApproval[i] = depositDataRootForApprovalDeposits[_validatorIds[i]]; - delete depositDataRootForApprovalDeposits[_validatorIds[i]]; + // TODO(Dave): set + uint256 validatorSizeWei = 32 ether; - emit ValidatorApproved(_validatorIds[i]); - } + // all validators provided must belong to same node + IEtherFiNode etherFiNode = IEtherFiNode(nodesManager.etherfiNodeAddress(_validatorIds[0])); + address eigenPod = address(etherFiNode.getEigenPod()); + bytes memory withdrawalCredentials = nodesManager.addressToWithdrawalCredentials(eigenPod); - // As the LP is the T-NFT holder, the 31 ETH is taken from the LP for each validator - uint256 outboundEthAmountFromLp = 31 ether * _validatorIds.length; - _accountForEthSentOut(outboundEthAmountFromLp); + // we have already deposited the initial amount to create the validator on the beacon chain + uint256 remainingEthPerValidator = validatorSizeWei - stakingManager.initialDepositAmount(); - stakingManager.batchApproveRegistration{value: outboundEthAmountFromLp}(_validatorIds, _pubKey, _signature, depositDataRootApproval); - */ + // In order to maintain compatibility with current callers in this upgrade + // need to construct data from old format + IStakingManager.DepositData[] memory depositData = new IStakingManager.DepositData[](_validatorIds.length); + for (uint256 i = 0; i < _validatorIds.length; i++) { + // enforce that all validators are part of same node + if (address(etherFiNode) != address(nodesManager.etherfiNodeAddress(_validatorIds[i]))) revert InvalidEtherFiNode(); + + bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot( + _pubkeys[i], + _signatures[i], + withdrawalCredentials, + remainingEthPerValidator + ); + IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ + publicKey: _pubkeys[i], + signature: _signatures[i], + depositDataRoot: confirmDepositRoot, + ipfsHashForEncryptedValidatorKey: "" + }); + depositData[i] = confirmDepositData; + } - // TODO(dave): need to update params. to take deposit data + validatorSize - uint256 outboundEthAmountFromLp = 31 ether * _validatorIds.length; + uint256 outboundEthAmountFromLp = remainingEthPerValidator * _validatorIds.length; _accountForEthSentOut(outboundEthAmountFromLp); - IStakingManager.DepositData[] memory debugDepositData = new IStakingManager.DepositData[](_validatorIds.length); - //stakingManager.confirmAndFundBeaconValidators(debugDepositData, _validatorIds, 32 ether); - stakingManager.confirmAndFundBeaconValidators(debugDepositData, 32 ether); - + stakingManager.confirmAndFundBeaconValidators{value: outboundEthAmountFromLp}(depositData, validatorSizeWei); } - /// @notice Cancels the process - /// @param _validatorIds the IDs to be cancelled - /// Note that if the spawner cancels the flow after the registration (where the 1 ETH deposit is made), the 1 ETH refund must be made manually - /// Until then the 1 ETH is considered as a loss - /// Be careful not to cancel the registration after the approval phase - function batchCancelDeposit(uint256[] calldata _validatorIds) external whenNotPaused { + /// @dev step 3 of staking flow + function batchApproveRegistration( + IStakingManager.DepositData[] calldata _depositData, + uint256 _validatorSizeWei + ) external whenNotPaused { + if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - // TODO(dave): how should we change cancel with new lifecycle changes - /* - address bnftHolder = address(this); + // we have already deposited the initial amount to create the validator on the beacon chain + uint256 remainingEthPerValidator = _validatorSizeWei - stakingManager.initialDepositAmount(); - for (uint256 i = 0; i < _validatorIds.length; i++) { - if(nodesManager.phase(_validatorIds[i]) == IEtherFiNode.VALIDATOR_PHASE.WAITING_FOR_APPROVAL) { - totalValueOutOfLp -= 1 ether; + uint256 outboundEthAmountFromLp = remainingEthPerValidator * _depositData.length; + _accountForEthSentOut(outboundEthAmountFromLp); - emit ValidatorRegistrationCanceled(_validatorIds[i]); - } - else numPendingDeposits -= 1; - } - */ + stakingManager.confirmAndFundBeaconValidators{value: outboundEthAmountFromLp}(_depositData, _validatorSizeWei); + } - //stakingManager.batchCancelDepositAsBnftHolder(_validatorIds, msg.sender); + + /// @dev set the size of validators created when caling batchApproveRegistration(). + /// In a future upgrade this will be a parameter to that call but was done like this to + /// to limit changes to other dependent contracts + function setValidatorSizeWei(uint256 _validatorSizeWei) external { + if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + if (_validatorSizeWei < 32 ether || _validatorSizeWei > 2048 ether) revert InvalidValidatorSize(); + validatorSizeWei = _validatorSizeWei; } /// @notice The admin can register an address to become a BNFT holder diff --git a/src/StakingManager.sol b/src/StakingManager.sol index cb7bdb1dd..43eb8d4ab 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -142,10 +142,6 @@ contract StakingManager is // Deposit the remaining eth to the validator depositContractEth2.deposit{value: remainingDeposit}(depositData[i].publicKey, withdrawalCredentials, depositData[i].signature, computedDataRoot); - // Use pubkey hash as the minted token ID - //tnft.mint(liquidityPool, uint256(pubkeyHash)); - //bnft.mint(liquidityPool, uint256(pubkeyHash)); - emit validatorConfirmed(pubkeyHash, liquidityPool, liquidityPool, depositData[i].publicKey); } } diff --git a/src/interfaces/ILiquidityPool.sol b/src/interfaces/ILiquidityPool.sol index 65255b163..c5af4f3f0 100644 --- a/src/interfaces/ILiquidityPool.sol +++ b/src/interfaces/ILiquidityPool.sol @@ -61,11 +61,9 @@ interface ILiquidityPool { function requestWithdrawWithPermit(address _owner, uint256 _amount, PermitInput calldata _permit) external returns (uint256); function requestMembershipNFTWithdraw(address recipient, uint256 amount, uint256 fee) external returns (uint256); - function batchDeposit(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators) external returns (uint256[] memory); - function batchDeposit(uint256[] calldata _candidateBidIds, uint256 _numberOfValidators, uint256 _validatorIdToCoUseWithdrawalSafe) external returns (uint256[] memory); - function batchRegister(bytes32 _depositRoot, uint256[] calldata _validatorIds, IStakingManager.DepositData[] calldata _registerValidatorDepositData, bytes32[] calldata _depositDataRootApproval, bytes[] calldata _signaturesForApprovalDeposit) external; - function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubKey, bytes[] calldata _signature) external; - function batchCancelDeposit(uint256[] calldata _validatorIds) external; + function batchRegister(IStakingManager.DepositData[] calldata _depositData, uint256[] calldata _bidIds, address _etherFiNode) external; + function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubkeys, bytes[] calldata _signatures) external; + function batchApproveRegistration(IStakingManager.DepositData[] calldata depositData, uint256 validatorSizeWei) external; function sendExitRequests(uint256[] calldata _validatorIds) external; function registerValidatorSpawner(address _user) external; @@ -74,10 +72,10 @@ interface ILiquidityPool { function rebase(int128 _accruedRewards) external; function payProtocolFees(uint128 _protocolFees) external; function addEthAmountLockedForWithdrawal(uint128 _amount) external; - + function pauseContract() external; function burnEEthShares(uint256 shares) external; function unPauseContract() external; function setStakingTargetWeights(uint32 _eEthWeight, uint32 _etherFanWeight) external; -} \ No newline at end of file +} diff --git a/src/interfaces/IStakingManager.sol b/src/interfaces/IStakingManager.sol index b98f437bb..10cee5745 100644 --- a/src/interfaces/IStakingManager.sol +++ b/src/interfaces/IStakingManager.sol @@ -16,6 +16,7 @@ interface IStakingManager { function createBeaconValidators(DepositData[] calldata depositData, uint256[] calldata bidIds, address etherFiNode) external payable; function confirmAndFundBeaconValidators(DepositData[] calldata depositData, uint256 validatorSizeWei) external payable; function calculateValidatorPubkeyHash(bytes memory pubkey) external pure returns (bytes32); + function initialDepositAmount() external returns (uint256); // EtherFiNode Beacon Proxy function upgradeEtherFiNode(address _newImplementation) external; diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index 0dba434da..8d4e61bd7 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -595,6 +595,8 @@ contract LiquidityPoolTest is TestSetup { assertEq(registered, true); } + // TODO(Dave): update for new deposit flow + /* function test_DepositAsBnftHolderSimple() public { //Sets up the list of BNFT holders @@ -659,6 +661,7 @@ contract LiquidityPoolTest is TestSetup { assertEq(validators[0], 5); assertEq(validators[1], 6); } + */ // function test_.unregisterValidatorSpawner() public { // setUpBnftHolders(); @@ -696,6 +699,9 @@ contract LiquidityPoolTest is TestSetup { liquidityPoolInstance.unregisterValidatorSpawner(owner); } + + // TODO(Dave): update for new deposit flow + /* function test_DepositWhenUserDeRegisters() public { IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); @@ -730,6 +736,7 @@ contract LiquidityPoolTest is TestSetup { liquidityPoolInstance.batchDeposit(bidIds, 4); vm.stopPrank(); } + */ /* function test_DepositFromBNFTHolder() public { @@ -950,106 +957,11 @@ contract LiquidityPoolTest is TestSetup { } */ - function test_bnftFlowCancel_1() public { - setUpBnftHolders(); - - vm.deal(alice, 1000 ether); - vm.startPrank(alice); - - uint256[] memory bidIds; - uint256[] memory validatorIds; - - bidIds = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - - // 1. Deposit -> Cancel - liquidityPoolInstance.deposit{value: 32 ether}(); - assertEq(address(stakingManagerInstance).balance, 0); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - validatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1); - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 1); - - liquidityPoolInstance.batchCancelDeposit(bidIds); - assertEq(address(stakingManagerInstance).balance, 0); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - // 2. Deposit -> Register -> Cancel - validatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1); - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 1); - { - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(bidIds); - liquidityPoolInstance.batchRegister(zeroRoot, bidIds, depositDataArray, depositDataRootsForApproval, sig); - } - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 31 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 1 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 31 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - liquidityPoolInstance.batchCancelDeposit(bidIds); - assertEq(address(stakingManagerInstance).balance, 0); - assertEq(address(liquidityPoolInstance).balance, 31 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 31 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 31 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - // Deposit 1 eth more - liquidityPoolInstance.deposit{value: 1 ether}(); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - - // 3. Deposit -> Register -> Approve - validatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1); - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 32 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 1); - - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(bidIds); - liquidityPoolInstance.batchRegister(zeroRoot, bidIds, depositDataArray, depositDataRootsForApproval, sig); - - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 31 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 1 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 31 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - liquidityPoolInstance.batchApproveRegistration(validatorIds, pubKey, sig); - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 0 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 32 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 0 ether); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - } + // TODO(dave): update when v3 changes finalized + /* function test_any_bnft_staker() public { _moveClock(1 days); @@ -1080,6 +992,7 @@ contract LiquidityPoolTest is TestSetup { vm.prank(alice); liquidityPoolInstance.batchCancelDeposit(bidIds); } + */ function test_deopsitToRecipient_by_rando_fails() public { vm.startPrank(alice); diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 9baad9daf..5a793d537 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -1297,8 +1297,10 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } vm.stopPrank(); - vm.prank(_bnftStaker); - uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, _numValidators, _validatorIdToCoUseWithdrawalSafe); + // TODO(dave): rework test setup + //vm.prank(_bnftStaker); + //uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, _numValidators, _validatorIdToCoUseWithdrawalSafe); + uint256[] memory newValidators = new uint256[](_numValidators); IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](_numValidators); @@ -1339,13 +1341,16 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen vm.startPrank(_bnftStaker); bytes32 depositRoot = zeroRoot; - liquidityPoolInstance.batchRegister(depositRoot, newValidators, depositDataArray, depositDataRootsForApproval, sig); + // TODO(Dave): fix for new deposit flow + address fix_nodeAddress = address(0x1234AABB); + liquidityPoolInstance.batchRegister(depositDataArray, newValidators, fix_nodeAddress); vm.stopPrank(); vm.startPrank(admin); - liquidityPoolInstance.batchApproveRegistration(newValidators, pubKey, sig); + // TODO(Dave): fix for new deposit flow + liquidityPoolInstance.batchApproveRegistration(depositDataArray, 31 ether); vm.stopPrank(); - + return newValidators; } From 857f07e293eb9c8f97a6c11e5cfdb0015cfd09b0 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 13 May 2025 22:26:38 -0600 Subject: [PATCH 18/47] admin can change validator size --- src/LiquidityPool.sol | 16 ++++++---------- src/interfaces/ILiquidityPool.sol | 2 +- test/TestSetup.sol | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/LiquidityPool.sol b/src/LiquidityPool.sol index ed7b4b327..aedee4aa9 100644 --- a/src/LiquidityPool.sol +++ b/src/LiquidityPool.sol @@ -165,10 +165,6 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL if(tvl != getTotalPooledEther()) revert(); } - function initializeV3Prelude(uint256 _validatorSizeWei) external onlyOwner { - validatorSizeWei = _validatorSizeWei; - } - // Used by eETH staking flow function deposit() external payable returns (uint256) { return deposit(address(0)); @@ -302,18 +298,17 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL stakingManager.createBeaconValidators{value: outboundEthAmountFromLp}(_depositData, _bidIds, _etherFiNode); } - /// @dev step 3 of staking flow + /// @dev step 3 of staking flow. This version exists to remain compatible with existing callers. + /// future services should use confirmAndFundBeaconValidators() function batchApproveRegistration( uint256[] memory _validatorIds, bytes[] calldata _pubkeys, bytes[] calldata _signatures ) external whenNotPaused { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + if (validatorSizeWei < 32 ether || validatorSizeWei > 2048 ether) revert InvalidValidatorSize(); - // TODO(Dave): set - uint256 validatorSizeWei = 32 ether; - - // all validators provided must belong to same node + // all validators provided should belong to same node IEtherFiNode etherFiNode = IEtherFiNode(nodesManager.etherfiNodeAddress(_validatorIds[0])); address eigenPod = address(etherFiNode.getEigenPod()); bytes memory withdrawalCredentials = nodesManager.addressToWithdrawalCredentials(eigenPod); @@ -350,11 +345,12 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL } /// @dev step 3 of staking flow - function batchApproveRegistration( + function confirmAndFundBeaconValidators( IStakingManager.DepositData[] calldata _depositData, uint256 _validatorSizeWei ) external whenNotPaused { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + if (validatorSizeWei < 32 ether || validatorSizeWei > 2048 ether) revert InvalidValidatorSize(); // we have already deposited the initial amount to create the validator on the beacon chain uint256 remainingEthPerValidator = _validatorSizeWei - stakingManager.initialDepositAmount(); diff --git a/src/interfaces/ILiquidityPool.sol b/src/interfaces/ILiquidityPool.sol index c5af4f3f0..13900da2f 100644 --- a/src/interfaces/ILiquidityPool.sol +++ b/src/interfaces/ILiquidityPool.sol @@ -63,7 +63,7 @@ interface ILiquidityPool { function batchRegister(IStakingManager.DepositData[] calldata _depositData, uint256[] calldata _bidIds, address _etherFiNode) external; function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubkeys, bytes[] calldata _signatures) external; - function batchApproveRegistration(IStakingManager.DepositData[] calldata depositData, uint256 validatorSizeWei) external; + function confirmAndFundBeaconValidators(IStakingManager.DepositData[] calldata depositData, uint256 validatorSizeWei) external; function sendExitRequests(uint256[] calldata _validatorIds) external; function registerValidatorSpawner(address _user) external; diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 5a793d537..ff621078e 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -1348,7 +1348,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen vm.startPrank(admin); // TODO(Dave): fix for new deposit flow - liquidityPoolInstance.batchApproveRegistration(depositDataArray, 31 ether); + liquidityPoolInstance.confirmAndFundBeaconValidators(depositDataArray, 32 ether); vm.stopPrank(); return newValidators; From a74daafa6e5d2eefba6934b52c2255dd066b6800 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 13 May 2025 23:16:21 -0600 Subject: [PATCH 19/47] clean up etherfi viewer --- src/LiquidityPool.sol | 4 +-- src/helpers/EtherFiViewer.sol | 46 +++++++---------------------------- test/EtherFiViewer.t.sol | 5 +++- 3 files changed, 14 insertions(+), 41 deletions(-) diff --git a/src/LiquidityPool.sol b/src/LiquidityPool.sol index aedee4aa9..e4f1fece5 100644 --- a/src/LiquidityPool.sol +++ b/src/LiquidityPool.sol @@ -99,6 +99,7 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL event Rebase(uint256 totalEthLocked, uint256 totalEEthShares); event ProtocolFeePaid(uint128 protocolFees); event WhitelistStatusUpdated(bool value); + event ValidatorExitRequested(uint256 indexed validatorId); error IncorrectCaller(); error InvalidAmount(); @@ -393,9 +394,6 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL emit ValidatorSpawnerUnregistered(_user); } - // TODO(dave): convert to pubkeyHash? - event ValidatorExitRequested(uint256 indexed validatorId); - /// @notice Send the exit requests as the T-NFT holder of the LiquidityPool validators function sendExitRequests(uint256[] calldata _validatorIds) external { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); diff --git a/src/helpers/EtherFiViewer.sol b/src/helpers/EtherFiViewer.sol index b85be950b..f85419267 100644 --- a/src/helpers/EtherFiViewer.sol +++ b/src/helpers/EtherFiViewer.sol @@ -8,19 +8,24 @@ import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; import "src/interfaces/IEtherFiNodesManager.sol"; import "src/eigenlayer-interfaces/IEigenPod.sol"; import "src/eigenlayer-interfaces/IEigenPodManager.sol"; +import "src/eigenlayer-interfaces/IDelegationManager.sol"; import "src/helpers/AddressProvider.sol"; contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { - // TODO(dave): add to constructor? - address public immutable eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); - address public immutable delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); AddressProvider addressProvider; - IEtherFiNodesManager nodesManager; + address public immutable eigenPodManager; + address public immutable delegationManager; + + constructor(address _eigenPodManager, address _delegationManager) { + eigenPodManager = _eigenPodManager; + delegationManager = _delegationManager; + } + function initialize(address _addressProvider) external initializer { __Ownable_init(); __UUPSUpgradeable_init(); @@ -108,38 +113,5 @@ contract EtherFiViewer is Initializable, OwnableUpgradeable, UUPSUpgradeable { } } - /* - function EtherFiNodesManager_splitBalanceInExecutionLayer(uint256[] memory _validatorIds) external view returns (uint256[] memory _withdrawalSafe, uint256[] memory _eigenPod, uint256[] memory _delayedWithdrawalRouter) { - _withdrawalSafe = new uint256[](_validatorIds.length); - _eigenPod = new uint256[](_validatorIds.length); - _delayedWithdrawalRouter = new uint256[](_validatorIds.length); - - for (uint256 i = 0; i < _validatorIds.length; i++) { - (_withdrawalSafe[i], _eigenPod[i], _delayedWithdrawalRouter[i]) = _getEtherFiNode(_validatorIds[i]).splitBalanceInExecutionLayer(); - } - } - - function EtherFiNodesManager_withdrawableBalanceInExecutionLayer(uint256[] memory _validatorIds) external view returns (uint256[] memory _withdrawableBalance) { - _withdrawableBalance = new uint256[](_validatorIds.length); - - for (uint256 i = 0; i < _validatorIds.length; i++) { - _withdrawableBalance[i] = _getEtherFiNode(_validatorIds[i]).withdrawableBalanceInExecutionLayer(); - } - } - */ - - /* - function EtherFiNodesManager_aggregatedBalanceOfUnusedSafes() external view returns (uint256 total) { - uint256 n = nodesManager.getUnusedWithdrawalSafesLength(); - - for (uint256 i = 0; i < n; i++) { - address safe = nodesManager.unusedWithdrawalSafes(i); - address eigenpod = address(IEtherFiNode(safe).getEigenPod()); - total += safe.balance + eigenpod.balance; - } - } - */ - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} - } diff --git a/test/EtherFiViewer.t.sol b/test/EtherFiViewer.t.sol index 97508a68f..ed57ce1fa 100644 --- a/test/EtherFiViewer.t.sol +++ b/test/EtherFiViewer.t.sol @@ -10,10 +10,13 @@ import "../src/UUPSProxy.sol"; contract EtherFiViewerTest is Test { EtherFiViewer public etherFiViewer; + address public eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); + address public delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); function setUp() public { vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); - etherFiViewer = EtherFiViewer(address(new UUPSProxy(address(new EtherFiViewer()), ""))); + + etherFiViewer = EtherFiViewer(address(new UUPSProxy(address(new EtherFiViewer(eigenPodManager, delegationManager)), ""))); etherFiViewer.initialize(address(0x8487c5F8550E3C3e7734Fe7DCF77DB2B72E4A848)); } From 3e116b7b06947c05a74f41c69bc4ae82177c43f5 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 13 May 2025 23:54:42 -0600 Subject: [PATCH 20/47] resolve merge conflicts --- src/interfaces/IRoleRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/IRoleRegistry.sol b/src/interfaces/IRoleRegistry.sol index b67fea7aa..bc504f213 100644 --- a/src/interfaces/IRoleRegistry.sol +++ b/src/interfaces/IRoleRegistry.sol @@ -90,4 +90,4 @@ interface IRoleRegistry { * @return The address of the current owner */ function owner() external view returns (address); -} \ No newline at end of file +} From 426f4d273c1860a3b27b791eebf422760031e868 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 14 May 2025 00:36:53 -0600 Subject: [PATCH 21/47] add verifyCheckpointProofs to eigenlayer convenience functions --- src/EtherFiNode.sol | 7 +++ src/EtherFiNodesManager.sol | 4 ++ src/interfaces/IEtherFiNode.sol | 1 + src/interfaces/IEtherFiNodesManager.sol | 6 +++ test/EtherFiTimelock.sol | 59 ------------------------- test/WeETH.t.sol | 22 --------- 6 files changed, 18 insertions(+), 81 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index cf621b399..401acc6f8 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.27; import {IDelegationManager} from "../src/eigenlayer-interfaces/IDelegationManager.sol"; import {IEigenPodManager} from "../src/eigenlayer-interfaces/IEigenPodManager.sol"; import {IEigenPod} from "../src/eigenlayer-interfaces/IEigenPod.sol"; +import {BeaconChainProofs} from "../src/eigenlayer-libraries/BeaconChainProofs.sol"; import {IERC20} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; import {IEtherFiNode} from "../src/interfaces/IEtherFiNode.sol"; @@ -74,6 +75,12 @@ contract EtherFiNode is IEtherFiNode { getEigenPod().startCheckpoint(revertIfNoBalance); } + /// @dev submit a subset of proofs for the currently active checkpoint + function verifyCheckpointProofs(BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyAdmin { + getEigenPod().verifyCheckpointProofs(balanceContainerProof, proofs); + } + + /// @dev queue a withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. /// It is fine to queue a withdrawal before validators have finished exiting on the beacon chain. function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 07327136c..2b29b4567 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -83,6 +83,10 @@ contract EtherFiNodesManager is IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); } + function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyCallForwarder { + IEtherFiNode(etherfiNodeAddress(id)).verifyCheckpointProofs(balanceContainerProof, proofs); + } + function setProofSubmitter(uint256 id, address proofSubmitter) external onlyCallForwarder { IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index 3554dc213..c8b31d0e6 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -15,6 +15,7 @@ interface IEtherFiNode { function setProofSubmitter(address _newProofSubmitter) external; function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot); function completeQueuedWithdrawals(bool receiveAsTokens) external; + function verifyCheckpointProofs(BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external; // call forwarding function forwardEigenPodCall(bytes memory data) external returns (bytes memory); diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 24229799f..84bea8414 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.13; import "../interfaces/IEtherFiNode.sol"; +import "../eigenlayer-interfaces/IDelegationManager.sol"; +import {BeaconChainProofs} from "../eigenlayer-libraries/BeaconChainProofs.sol"; interface IEtherFiNodesManager { @@ -15,8 +17,12 @@ interface IEtherFiNodesManager { // eigenlayer interactions function getEigenPod(uint256 id) external view returns (address); + function createEigenPod(uint256 id) external returns (address); function startCheckpoint(uint256 id) external; + function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external; function setProofSubmitter(uint256 id, address _newProofSubmitter) external; + function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot); + function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external; // call forwarding function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; diff --git a/test/EtherFiTimelock.sol b/test/EtherFiTimelock.sol index fd42afc0b..7eda0c3b8 100644 --- a/test/EtherFiTimelock.sol +++ b/test/EtherFiTimelock.sol @@ -418,65 +418,6 @@ contract TimelockTest is TestSetup { _execute_timelock(target, data, true, true, true, true); } - function test_v2_dot_49() public { - shouldSetupRoleRegistry = false; - //upgrade contracts - initializeRealisticFork(MAINNET_FORK); - address[] memory _targets = new address[](15); - bytes[] memory _data = new bytes[](15); - uint256[] memory _values = new uint256[](15); - address timelockAddress = address(0x9f26d4C958fD811A1F59B01B86Be7dFFc9d20761); - address operatingTimelockAddress = address(0xcD425f44758a08BaAB3C4908f3e3dE5776e45d7a); - address treasuryAddress = address(0x0c83EAe1FE72c390A02E426572854931EefF93BA); - address etherFiRedemptionManagerAddress = address(0xDadEf1fFBFeaAB4f68A9fD181395F68b4e4E7Ae0); - vm.startPrank(timelockAddress); - roleRegistryInstance = RoleRegistry(address(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9)); - roleRegistryInstance.acceptOwnership(); - roleRegistryInstance.onlyProtocolUpgrader(timelockAddress); - vm.stopPrank(); - uint256 balOldTreasury = weEthInstance.balanceOf(address(treasuryInstance)); - - - _targets[0] = address(managerInstance); - _targets[1] = address(etherFiAdminInstance); - _targets[2] = address(etherFiRewardsRouterInstance); - _targets[3] = address(liquidityPoolInstance); - _targets[4] = address(weEthInstance); - _targets[5] = address(withdrawRequestNFTInstance); - _targets[6] = address(etherFiAdminInstance); - _targets[7] = address(liquidityPoolInstance); - _targets[8] = address(withdrawRequestNFTInstance); - _targets[9] = address(weEthInstance); - _targets[10] = address(weEthInstance); - _targets[11] = address(addressProviderInstance); - _targets[12] = address(addressProviderInstance); - _targets[13] = address(addressProviderInstance); - _targets[14] = address(addressProviderInstance); - - //upgrade contracts - _data[0] = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0x572E25fD70b6eB9a3CaD1CE1D48E3CfB938767F1); - _data[1] = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0x683583979C8be7Bcfa41E788Ab38857dfF792f49); - _data[2] = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0xe94bF0DF71002ff0165CF4daB461dEBC3978B0fa); - _data[3] = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0xA6099d83A67a2c653feB5e4e48ec24C5aeE1C515); - _data[4] = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0x353E98F34b6E5a8D9d1876Bf6dF01284d05837cB); - _data[5] = abi.encodeWithSelector(UUPSUpgradeable.upgradeTo.selector, 0x685870a508b56c7f1002EEF5eFCFa01304474F61); - - //initialize contracts - _data[6] = abi.encodeWithSelector(EtherFiAdmin.initializeRoleRegistry.selector, address(roleRegistryInstance)); - _data[7] = abi.encodeWithSelector(LiquidityPool.initializeVTwoDotFourNine.selector, address(roleRegistryInstance), etherFiRedemptionManagerAddress); - _data[8] = abi.encodeWithSelector(WithdrawRequestNFT.initializeOnUpgrade.selector, address(roleRegistryInstance), 10000); - _data[9] = abi.encodeWithSelector(weEthInstance.rescueTreasuryWeeth.selector); - _data[10] = abi.encodeWithSelector(weEthInstance.transfer.selector, treasuryAddress, balOldTreasury); - - //add to addressProvider - _data[11] = abi.encodeWithSelector(AddressProvider.addContract.selector, etherFiRedemptionManagerAddress, "EtherFiRedemptionManager"); - _data[12] = abi.encodeWithSelector(AddressProvider.addContract.selector, address(etherFiRewardsRouterInstance), "EtherFiRewardsRouter"); - _data[13] = abi.encodeWithSelector(AddressProvider.addContract.selector, operatingTimelockAddress, "OperatingTimelock"); - _data[14] = abi.encodeWithSelector(AddressProvider.addContract.selector, address(roleRegistryInstance), "RoleRegistry"); - - - _batch_execute_timelock(_targets, _data, _values, true, true, true, true); - } } // {"version":"1.0","chainId":"1 diff --git a/test/WeETH.t.sol b/test/WeETH.t.sol index be643f083..c4540a645 100644 --- a/test/WeETH.t.sol +++ b/test/WeETH.t.sol @@ -304,26 +304,4 @@ contract WeETHTest is TestSetup { weEthInstance.wrapWithPermit(5 ether, permitInput); } - function test_rescueTreasuryWeeth() public { - uint256 treasuryBal = 31859761318927469119; - address treasuryInstance = 0x6329004E903B7F420245E7aF3f355186f2432466; - vm.deal(treasuryInstance, treasuryBal); - vm.startPrank(treasuryInstance); - liquidityPoolInstance.deposit{value: treasuryBal}(); - eETHInstance.approve(address(weEthInstance), treasuryBal); - weEthInstance.wrap(treasuryBal); - vm.stopPrank(); - uint256 preTreasuryBal = weEthInstance.balanceOf(treasuryInstance); - uint256 preOwnerBal = weEthInstance.balanceOf(owner); - vm.startPrank(alice); - vm.expectRevert(); - weEthInstance.rescueTreasuryWeeth(); - vm.stopPrank(); - vm.startPrank(owner); - weEthInstance.rescueTreasuryWeeth(); - vm.stopPrank(); - assertEq(weEthInstance.balanceOf(address(treasuryInstance)), 0); - assertEq(weEthInstance.balanceOf(owner), preTreasuryBal + preOwnerBal); - vm.stopPrank(); - } } From fcf5f1f580cc7fe7063201d9acb9465b6b49853c Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 2 Jun 2025 16:23:52 -0600 Subject: [PATCH 22/47] C-01 + I-05 audit fixes --- src/StakingManager.sol | 5 ++- src/interfaces/IStakingManager.sol | 2 +- test/TestSetup.sol | 2 +- test/common/ArrayTestHelper.sol | 12 +++---- test/prelude.t.sol | 56 +++++++++--------------------- 5 files changed, 27 insertions(+), 50 deletions(-) diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 43eb8d4ab..57c7258f8 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -18,7 +18,6 @@ import "@openzeppelin-upgradeable/contracts/security/ReentrancyGuardUpgradeable. import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol"; import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; - contract StakingManager is Initializable, IStakingManager, @@ -34,7 +33,7 @@ contract StakingManager is IEtherFiNodesManager public immutable etherFiNodesManager; IDepositContract public immutable depositContractEth2; IAuctionManager public immutable auctionManager; - UpgradeableBeacon private etherFiNodeBeacon; + UpgradeableBeacon public immutable etherFiNodeBeacon; IRoleRegistry public immutable roleRegistry; //--------------------------------------------------------------------------- @@ -71,7 +70,7 @@ contract StakingManager is _disableInitializers(); } - function _authorizeUpgrade(address _newImplementation) internal override {} + function _authorizeUpgrade(address _newImplementation) internal override onlyOwner {} function pauseContract() external { if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender)) revert IncorrectRole(); diff --git a/src/interfaces/IStakingManager.sol b/src/interfaces/IStakingManager.sol index 10cee5745..bdc02998a 100644 --- a/src/interfaces/IStakingManager.sol +++ b/src/interfaces/IStakingManager.sol @@ -28,7 +28,7 @@ interface IStakingManager { // prevent storage shift on upgrade struct LegacyStakingManagerState { - uint256[15] legacyState; + uint256[14] legacyState; /* |------------------------+-------------------------------------------------------+------+--------+-------+---------------------------------------| | stakeAmount | uint128 | 301 | 16 | 16 | src/StakingManager.sol:StakingManager | diff --git a/test/TestSetup.sol b/test/TestSetup.sol index ff621078e..51b2c79a5 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -512,7 +512,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen regulationsManagerInstance.updateAdmin(alice, true); - revert("FILL IN ADDRESSES"); + // TODO(Dave): fill in addresses address eigenPodManager; address delegationManager; address liquidityPool; diff --git a/test/common/ArrayTestHelper.sol b/test/common/ArrayTestHelper.sol index f8887f694..ed2214c8e 100644 --- a/test/common/ArrayTestHelper.sol +++ b/test/common/ArrayTestHelper.sol @@ -6,33 +6,33 @@ import "../../src/interfaces/IStakingManager.sol"; contract ArrayTestHelper { // Common types used throughout our and eigenlayers protocol - function toArray_u256(uint256 val) public returns (uint256[] memory) { + function toArray_u256(uint256 val) public pure returns (uint256[] memory) { uint256[] memory vals = new uint256[](1); vals[0] = val; return vals; } - function toArray_u256(uint32 val) public returns (uint256[] memory) { + function toArray_u256(uint32 val) public pure returns (uint256[] memory) { uint256[] memory vals = new uint256[](1); vals[0] = val; return vals; } - function toArray_u32(uint32 val) public returns (uint32[] memory) { + function toArray_u32(uint32 val) public pure returns (uint32[] memory) { uint32[] memory vals = new uint32[](1); vals[0] = val; return vals; } - function toArray_u40(uint40 val) public returns (uint40[] memory) { + function toArray_u40(uint40 val) public pure returns (uint40[] memory) { uint40[] memory vals = new uint40[](1); vals[0] = val; return vals; } - function toArray(IDelegationManager.Withdrawal memory withdrawal) public returns (IDelegationManager.Withdrawal[] memory) { + function toArray(IDelegationManager.Withdrawal memory withdrawal) public pure returns (IDelegationManager.Withdrawal[] memory) { IDelegationManager.Withdrawal[] memory vals = new IDelegationManager.Withdrawal[](1); vals[0] = withdrawal; return vals; } - function toArray(IStakingManager.DepositData memory deposit) public returns (IStakingManager.DepositData[] memory) { + function toArray(IStakingManager.DepositData memory deposit) public pure returns (IStakingManager.DepositData[] memory) { IStakingManager.DepositData[] memory vals = new IStakingManager.DepositData[](1); vals[0] = deposit; return vals; diff --git a/test/prelude.t.sol b/test/prelude.t.sol index b5fb1d277..9b0daab5d 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -34,22 +34,14 @@ contract PreludeTest is Test, ArrayTestHelper { address etherFiNodeBeacon = address(0x3c55986Cfee455E2533F4D29006634EcF9B7c03F); RoleRegistry roleRegistry = RoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); - // i don't think i need this anymore - address oracle; - - function setUp() public { - console2.log("setup start"); vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); - console2.log("post fork"); stakingManager = StakingManager(0x25e821b7197B146F7713C3b89B6A4D83516B912d); liquidityPool = ILiquidityPool(0x308861A430be4cce5502d0A12724771Fc6DaF216); etherFiNodesManager = EtherFiNodesManager(payable(0x8B71140AD2e5d1E7018d2a7f8a288BD3CD38916F)); auctionManager = AuctionManager(0x00C452aFFee3a17d9Cecc1Bcd2B8d5C7635C4CB9); - //tnft = ITNFT(0x7B5ae07E2AF1C861BcC4736D23f5f66A61E0cA5e); - //bnft = IBNFT(0x6599861e55abd28b91dd9d86A826eC0cC8D72c2c); // deploy new staking manager implementation StakingManager stakingManagerImpl = new StakingManager( @@ -57,14 +49,11 @@ contract PreludeTest is Test, ArrayTestHelper { address(etherFiNodesManager), address(stakingDepositContract), address(auctionManager), - //address(tnft), - //address(bnft), address(etherFiNodeBeacon), address(roleRegistry) ); vm.prank(stakingManager.owner()); stakingManager.upgradeTo(address(stakingManagerImpl)); - console2.log("sm upgrade"); // upgrade etherFiNode impl EtherFiNode etherFiNodeImpl = new EtherFiNode( @@ -76,17 +65,12 @@ contract PreludeTest is Test, ArrayTestHelper { ); vm.prank(stakingManager.owner()); stakingManager.upgradeEtherFiNode(address(etherFiNodeImpl)); - console2.log("efn upgrade"); - - console2.log("epm:", address(etherFiNodeImpl.eigenPodManager())); // deploy new efnm implementation EtherFiNodesManager etherFiNodesManagerImpl = new EtherFiNodesManager(address(stakingManager), address(roleRegistry)); vm.prank(etherFiNodesManager.owner()); etherFiNodesManager.upgradeTo(address(etherFiNodesManagerImpl)); - console2.log("efnm upgrade"); - vm.prank(auctionManager.owner()); auctionManager.disableWhitelist(); @@ -100,31 +84,27 @@ contract PreludeTest is Test, ArrayTestHelper { } - /* - address etherFiNode = managerInstance.etherFiNodeFromId(11); - root = generateDepositRoot( - hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.addressToWithdrawalCredentials(etherFiNode), - 1 ether - ); + function test_StakingManagerUpgradePermissions() public { - depositDataRootsForApproval[0] = generateDepositRoot( - hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - managerInstance.addressToWithdrawalCredentials(etherFiNode), - 31 ether + // deploy new staking manager implementation + StakingManager stakingManagerImpl = new StakingManager( + address(liquidityPool), + address(etherFiNodesManager), + address(stakingDepositContract), + address(auctionManager), + address(etherFiNodeBeacon), + address(roleRegistry) ); - IStakingManager.DepositData memory depositData = IStakingManager - .DepositData({ - publicKey: hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c", - signature: hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df", - depositDataRoot: root, - ipfsHashForEncryptedValidatorKey: "test_ipfs" - }); - */ + // only owner should be able to upgrade + vm.expectRevert("Ownable: caller is not the owner"); + stakingManager.upgradeTo(address(stakingManagerImpl)); + // should succeed when called by owner + address owner = stakingManager.owner(); + vm.prank(owner); + stakingManager.upgradeTo(address(stakingManagerImpl)); + } function test_createBeaconValidators() public { @@ -153,14 +133,12 @@ contract PreludeTest is Test, ArrayTestHelper { etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), 1 ether ); - //uint256[] memory validatorIDs = toArray_u256(validatorID); IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ publicKey: pubkey, signature: signature, depositDataRoot: initialDepositRoot, ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" }); - //ISta[] memory depositDatas = toArray(depositData); vm.deal(address(liquidityPool), 100 ether); vm.prank(address(liquidityPool)); From 2d535a9db105451d937df43ff1f807c50e974237 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 3 Jun 2025 17:02:14 -0600 Subject: [PATCH 23/47] enable dynamic test linking --- foundry.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/foundry.toml b/foundry.toml index 93d6cd43a..2b44f5ab5 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,6 +8,7 @@ optimizer_runs = 1500 extra_output = ["storageLayout"] bytecode_hash = 'none' solc-version = '0.8.27' +dynamic_test_linking = true [fuzz] max_shrink_iters = 100 From 42d12bcda9c75d1a44ce2591c575641b0aecb6ff Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 3 Jun 2025 17:14:51 -0600 Subject: [PATCH 24/47] [M-01] + [M-02] + [M-04] + [I-03] + [I-06] audit fixes --- src/EtherFiNode.sol | 64 ++++++++++++++++++------- src/EtherFiNodesManager.sol | 20 ++++++-- src/interfaces/IEtherFiNode.sol | 9 ++-- src/interfaces/IEtherFiNodesManager.sol | 9 ++-- test/common/ArrayTestHelper.sol | 6 ++- test/prelude.t.sol | 23 +++++++++ 6 files changed, 104 insertions(+), 27 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 401acc6f8..363af577b 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -1,16 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; +import {IEtherFiNode} from "../src/interfaces/IEtherFiNode.sol"; +import {IEtherFiNodesManager} from "../src/interfaces/IEtherFiNodesManager.sol"; +import {IRoleRegistry} from "../src/interfaces/IRoleRegistry.sol"; +import {ILiquidityPool} from "../src/interfaces/ILiquidityPool.sol"; + import {IDelegationManager} from "../src/eigenlayer-interfaces/IDelegationManager.sol"; +import {IDelegationManagerTypes} from "../src/eigenlayer-interfaces/IDelegationManager.sol"; import {IEigenPodManager} from "../src/eigenlayer-interfaces/IEigenPodManager.sol"; import {IEigenPod} from "../src/eigenlayer-interfaces/IEigenPod.sol"; +import {IStrategy} from "../src/eigenlayer-interfaces/IStrategy.sol"; import {BeaconChainProofs} from "../src/eigenlayer-libraries/BeaconChainProofs.sol"; -import {IERC20} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; -import {IEtherFiNode} from "../src/interfaces/IEtherFiNode.sol"; -import {IEtherFiNodesManager} from "../src/interfaces/IEtherFiNodesManager.sol"; -import {IRoleRegistry} from "../src/interfaces/IRoleRegistry.sol"; -import {ILiquidityPool} from "../src/interfaces/ILiquidityPool.sol"; +import {IERC20} from "../lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; import {LibCall} from "../lib/solady/src/utils/LibCall.sol"; contract EtherFiNode is IEtherFiNode { @@ -48,6 +51,8 @@ contract EtherFiNode is IEtherFiNode { roleRegistry = IRoleRegistry(_roleRegistry); } + fallback() external payable {} + //-------------------------------------------------------------------------------------- //---------------------------- Eigenlayer Interactions -------------------------------- //-------------------------------------------------------------------------------------- @@ -80,32 +85,42 @@ contract EtherFiNode is IEtherFiNode { getEigenPod().verifyCheckpointProofs(balanceContainerProof, proofs); } - - /// @dev queue a withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. + /// @dev convenience function to queue a beaconETH withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. /// It is fine to queue a withdrawal before validators have finished exiting on the beacon chain. - function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external onlyAdmin returns (bytes32 withdrawalRoot) { + function queueETHWithdrawal(uint256 amount) external onlyAdmin returns (bytes32 withdrawalRoot) { + + // beacon eth is always 1 to 1 with deposit shares + uint256[] memory depositShares = new uint256[](1); + depositShares[0] = amount; + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = IStrategy(address(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0)); + + IDelegationManagerTypes.QueuedWithdrawalParams[] memory paramsArray = new IDelegationManagerTypes.QueuedWithdrawalParams[](1); + paramsArray[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ + strategies: strategies, // beacon eth + depositShares: depositShares, + __deprecated_withdrawer: address(this) + }); - // Implemented this way for convenience because we almost never queue multiple withdrawals at the same time - IDelegationManager.QueuedWithdrawalParams[] memory paramsArray = new IDelegationManager.QueuedWithdrawalParams[](1); - paramsArray[0] = params; return delegationManager.queueWithdrawals(paramsArray)[0]; } - /// @dev completes all queued withdrawals that are currently claimable. + + /// @dev completes all queued beaconETH withdrawals that are currently claimable. /// Note that since the node is usually delegated to an operator, /// most of the time this should be called with "receiveAsTokens" = true because /// receiving shares while delegated will simply redelegate the shares. - function completeQueuedWithdrawals(bool receiveAsTokens) external onlyAdmin { + function completeQueuedETHWithdrawals(bool receiveAsTokens) external onlyAdmin { // because we are just dealing with beacon eth we don't need to populate the tokens[] array - IERC20[] memory tokens; + IERC20[] memory tokens = new IERC20[](1); (IDelegationManager.Withdrawal[] memory queuedWithdrawals, ) = delegationManager.getQueuedWithdrawals(address(this)); for (uint256 i = 0; i < queuedWithdrawals.length; i++) { // skip this withdrawal if not enough time has passed uint32 slashableUntil = queuedWithdrawals[i].startBlock + EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS; - if (uint32(block.number) > slashableUntil) continue; + if (uint32(block.number) < slashableUntil) continue; delegationManager.completeQueuedWithdrawal(queuedWithdrawals[i], tokens, receiveAsTokens); } @@ -117,9 +132,26 @@ contract EtherFiNode is IEtherFiNode { } } + /// @dev queue a withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. + /// For the general case of queuing a beaconETH withdrawal you can use queueETHWithdrawal instead. + function queueWithdrawals(IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyAdmin returns (bytes32[] memory withdrawalRoots) { + return delegationManager.queueWithdrawals(params); + } + + /// @dev complete an arbitrary withdrawal from eigenlayer. + /// For the general case of claiming beaconETH withdrawals you can use completeQueuedETHWithdrawals instead. + function completeQueuedWithdrawals( + IDelegationManager.Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens + ) external onlyAdmin { + delegationManager.completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); + } + + // @notice transfers any funds held by the node to the liquidity pool. // @dev under normal operations it is not expected for eth to accumulate in the nodes, - // this is just to handle any exceptional cases such as someone sending directly to the node. + // this is just to handle any exceptional cases such as someone sending directly to the node. function sweepFunds() external onlyAdmin { (bool sent, ) = payable(address(liquidityPool)).call{value: address(this).balance, gas: 20000}(""); if (!sent) revert TransferFailed(); diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 2b29b4567..093669369 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -91,12 +91,24 @@ contract EtherFiNodesManager is IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } - function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external onlyCallForwarder returns (bytes32 withdrawalRoot) { - return IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawal(params); + function queueETHWithdrawal(uint256 id, uint256 amount) external onlyCallForwarder returns (bytes32 withdrawalRoot) { + return IEtherFiNode(etherfiNodeAddress(id)).queueETHWithdrawal(amount); } - function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external onlyCallForwarder { - IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(receiveAsTokens); + function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external onlyCallForwarder { + IEtherFiNode(etherfiNodeAddress(id)).completeQueuedETHWithdrawals(receiveAsTokens); + } + + function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyCallForwarder { + IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawals(params); + } + + function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyCallForwarder { + IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); + } + + function sweepFunds(uint256 id) external onlyCallForwarder { + IEtherFiNode(etherfiNodeAddress(id)).sweepFunds; } //------------------------------------------------------------------- diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index c8b31d0e6..871d720b1 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -12,10 +12,13 @@ interface IEtherFiNode { function createEigenPod() external returns (address); function getEigenPod() external view returns (IEigenPod); function startCheckpoint() external; - function setProofSubmitter(address _newProofSubmitter) external; - function queueWithdrawal(IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot); - function completeQueuedWithdrawals(bool receiveAsTokens) external; + function setProofSubmitter(address newProofSubmitter) external; function verifyCheckpointProofs(BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external; + function queueETHWithdrawal(uint256 amount) external returns (bytes32 withdrawalRoot); + function completeQueuedETHWithdrawals(bool receiveAsTokens) external ; + function queueWithdrawals(IDelegationManager.QueuedWithdrawalParams[] calldata params) external returns (bytes32[] memory withdrawalRoot); + function completeQueuedWithdrawals(IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external; + function sweepFunds() external; // call forwarding function forwardEigenPodCall(bytes memory data) external returns (bytes memory); diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 84bea8414..4a34195d0 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -20,9 +20,12 @@ interface IEtherFiNodesManager { function createEigenPod(uint256 id) external returns (address); function startCheckpoint(uint256 id) external; function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external; - function setProofSubmitter(uint256 id, address _newProofSubmitter) external; - function queueWithdrawal(uint256 id, IDelegationManager.QueuedWithdrawalParams calldata params) external returns (bytes32 withdrawalRoot); - function completeQueuedWithdrawals(uint256 id, bool receiveAsTokens) external; + function setProofSubmitter(uint256 id, address newProofSubmitter) external; + function queueETHWithdrawal(uint256 id, uint256 amount) external returns (bytes32 withdrawalRoot); + function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external; + function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external; + function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external; + function sweepFunds(uint256 id) external; // call forwarding function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; diff --git a/test/common/ArrayTestHelper.sol b/test/common/ArrayTestHelper.sol index ed2214c8e..04b7fbcb6 100644 --- a/test/common/ArrayTestHelper.sol +++ b/test/common/ArrayTestHelper.sol @@ -26,12 +26,16 @@ contract ArrayTestHelper { vals[0] = val; return vals; } + function toArray_bytes(bytes memory val) public pure returns (bytes[] memory) { + bytes[] memory vals = new bytes[](1); + vals[0] = val; + return vals; + } function toArray(IDelegationManager.Withdrawal memory withdrawal) public pure returns (IDelegationManager.Withdrawal[] memory) { IDelegationManager.Withdrawal[] memory vals = new IDelegationManager.Withdrawal[](1); vals[0] = withdrawal; return vals; } - function toArray(IStakingManager.DepositData memory deposit) public pure returns (IStakingManager.DepositData[] memory) { IStakingManager.DepositData[] memory vals = new IStakingManager.DepositData[](1); vals[0] = deposit; diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 9b0daab5d..4e2221f77 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -28,6 +28,7 @@ contract PreludeTest is Test, ArrayTestHelper { NodeOperatorManager nodeOperatorManager = NodeOperatorManager(0xd5edf7730ABAd812247F6F54D7bd31a52554e35E); address admin = vm.addr(0x9876543210); + address forwarder = vm.addr(0x1234567890); address stakingDepositContract = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); address eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); @@ -79,6 +80,7 @@ contract PreludeTest is Test, ArrayTestHelper { roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_ADMIN_ROLE(), address(etherFiNodesManager)); roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_ADMIN_ROLE(), address(stakingManager)); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_ADMIN_ROLE(), admin); + roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), forwarder); roleRegistry.grantRole(stakingManager.STAKING_MANAGER_NODE_CREATOR_ROLE(), admin); vm.stopPrank(); @@ -163,6 +165,27 @@ contract PreludeTest is Test, ArrayTestHelper { vm.prank(address(liquidityPool)); stakingManager.confirmAndFundBeaconValidators{value: confirmAmount}(toArray(confirmDepositData), validatorSize); + } + + function test_withdrawRestakedValidatorETH() public { + + bytes memory validatorPubkey = hex"892c95f4e93ab042ee39397bff22cc43298ff4b2d6d6dec3f28b8b8ebcb5c65ab5e6fc29301c1faee473ec095f9e4306"; + bytes32 pubkeyHash = etherFiNodesManager.calculateValidatorPubkeyHash(validatorPubkey); + uint256 legacyID = 10885; + + // force link this validator + vm.prank(admin); + etherFiNodesManager.linkLegacyValidatorIds(toArray_u256(legacyID), toArray_bytes(validatorPubkey)); + + vm.prank(forwarder); + etherFiNodesManager.queueETHWithdrawal(uint256(pubkeyHash), 1 ether); + + // poke some withdrawable funds into the restakedExecutionLayerGwei storage slot of the eigenpod + address eigenpod = etherFiNodesManager.getEigenPod(uint256(pubkeyHash)); + vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(50 ether / 1 gwei))); + vm.roll(block.number + (7200 * 15)); + vm.prank(forwarder); + etherFiNodesManager.completeQueuedETHWithdrawals(uint256(pubkeyHash), true); } } From 7ba63b6f8f00c2c2124881243758f2e2cb4c5e12 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 3 Jun 2025 17:52:43 -0600 Subject: [PATCH 25/47] [L-01] audit fixes --- src/EtherFiNodesManager.sol | 4 ---- src/interfaces/IEtherFiNodesManager.sol | 1 - 2 files changed, 5 deletions(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 093669369..a6f590996 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -75,10 +75,6 @@ contract EtherFiNodesManager is return address(IEtherFiNode(etherfiNodeAddress(id)).getEigenPod()); } - function createEigenPod(uint256 id) public onlyAdmin returns (address) { - return IEtherFiNode(etherfiNodeAddress(id)).createEigenPod(); - } - function startCheckpoint(uint256 id) external onlyCallForwarder { IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); } diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 4a34195d0..0cf00f70c 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -17,7 +17,6 @@ interface IEtherFiNodesManager { // eigenlayer interactions function getEigenPod(uint256 id) external view returns (address); - function createEigenPod(uint256 id) external returns (address); function startCheckpoint(uint256 id) external; function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external; function setProofSubmitter(uint256 id, address newProofSubmitter) external; From 4441627a29fbb62432b3f3b8f6f36f1dad9a8b61 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 3 Jun 2025 18:14:40 -0600 Subject: [PATCH 26/47] [I-02] audit fixes --- src/EtherFiNodesManager.sol | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index a6f590996..d4c55d132 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -75,35 +75,35 @@ contract EtherFiNodesManager is return address(IEtherFiNode(etherfiNodeAddress(id)).getEigenPod()); } - function startCheckpoint(uint256 id) external onlyCallForwarder { + function startCheckpoint(uint256 id) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); } - function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyCallForwarder { + function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).verifyCheckpointProofs(balanceContainerProof, proofs); } - function setProofSubmitter(uint256 id, address proofSubmitter) external onlyCallForwarder { + function setProofSubmitter(uint256 id, address proofSubmitter) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } - function queueETHWithdrawal(uint256 id, uint256 amount) external onlyCallForwarder returns (bytes32 withdrawalRoot) { + function queueETHWithdrawal(uint256 id, uint256 amount) external onlyCallForwarder whenNotPaused returns (bytes32 withdrawalRoot) { return IEtherFiNode(etherfiNodeAddress(id)).queueETHWithdrawal(amount); } - function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external onlyCallForwarder { + function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedETHWithdrawals(receiveAsTokens); } - function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyCallForwarder { + function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawals(params); } - function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyCallForwarder { + function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); } - function sweepFunds(uint256 id) external onlyCallForwarder { + function sweepFunds(uint256 id) external onlyCallForwarder whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).sweepFunds; } @@ -199,7 +199,7 @@ contract EtherFiNodesManager is emit AllowedForwardedEigenpodCallsUpdated(selector, allowed); } - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external onlyCallForwarder returns (bytes[] memory returnData) { + function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { if (nodes.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](nodes.length); @@ -214,7 +214,7 @@ contract EtherFiNodesManager is } } - function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external onlyCallForwarder returns (bytes[] memory returnData) { + function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { if (ids.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](ids.length); @@ -230,7 +230,7 @@ contract EtherFiNodesManager is } } - function forwardEigenPodCall(address[] calldata etherFiNodes, bytes[] calldata data) external onlyCallForwarder returns (bytes[] memory returnData) { + function forwardEigenPodCall(address[] calldata etherFiNodes, bytes[] calldata data) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { if (etherFiNodes.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](etherFiNodes.length); @@ -246,7 +246,7 @@ contract EtherFiNodesManager is } } - function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external onlyCallForwarder returns (bytes[] memory returnData) { + function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { if (ids.length != data.length) revert InvalidForwardedCall(); returnData = new bytes[](ids.length); From 353765993b40e3c2bddcdcdf7adc6f2f6ec080c9 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 3 Jun 2025 18:24:37 -0600 Subject: [PATCH 27/47] [I-04] audit fixes --- src/EtherFiNodesManager.sol | 4 +++- src/StakingManager.sol | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index d4c55d132..84c1bf531 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -55,7 +55,9 @@ contract EtherFiNodesManager is _disableInitializers(); } - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + function _authorizeUpgrade(address newImplementation) internal override { + roleRegistry.onlyProtocolUpgrader(msg.sender); + } function pauseContract() external { if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender)) revert IncorrectRole(); diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 57c7258f8..d5ac8b762 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -70,7 +70,9 @@ contract StakingManager is _disableInitializers(); } - function _authorizeUpgrade(address _newImplementation) internal override onlyOwner {} + function _authorizeUpgrade(address _newImplementation) internal override { + roleRegistry.onlyProtocolUpgrader(msg.sender); + } function pauseContract() external { if (!roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender)) revert IncorrectRole(); From ecf6856fca5ed8cef601b3b02089d5ef42d6e161 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 4 Jun 2025 14:56:36 -0600 Subject: [PATCH 28/47] [I-01] audit fixes --- src/EtherFiNode.sol | 34 +++++++----- src/EtherFiNodesManager.sol | 69 ++++++++++--------------- src/interfaces/IEtherFiNodesManager.sol | 2 - test/EtherFiTimelock.sol | 26 ---------- test/prelude.t.sol | 4 +- 5 files changed, 49 insertions(+), 86 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 363af577b..ed72a9453 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -37,7 +37,8 @@ contract EtherFiNode is IEtherFiNode { //------------------------------------- ROLES --------------------------------------- //-------------------------------------------------------------------------------------- - bytes32 public constant ETHERFI_NODE_ADMIN_ROLE = keccak256("ETHERFI_NODE_ADMIN_ROLE"); + bytes32 public constant ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE = keccak256("ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE"); + bytes32 public constant ETHERFI_NODE_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODE_CALL_FORWARDER_ROLE"); //------------------------------------------------------------------------- //----------------------------- Admin ----------------------------------- @@ -65,29 +66,29 @@ contract EtherFiNode is IEtherFiNode { /// @dev creates a new eigenpod and returns its address. Reverts if a pod already exists for this node. /// This address is deterministic and you can pre-compute it if necessary. - function createEigenPod() external onlyAdmin returns (address) { + function createEigenPod() external onlyEigenlayerAdmin returns (address) { return eigenPodManager.createPod(); } /// @dev specify another address with permissions to submit checkpoint and withdrawal credential proofs - function setProofSubmitter(address _newProofSubmitter) external onlyAdmin { + function setProofSubmitter(address _newProofSubmitter) external onlyEigenlayerAdmin { getEigenPod().setProofSubmitter(_newProofSubmitter); } /// @dev start an eigenlayer checkpoint proof. Once a checkpoint is started, it must be completed - function startCheckpoint() external onlyAdmin { + function startCheckpoint() external onlyEigenlayerAdmin { bool revertIfNoBalance = true; // protect from wasting gas if checkpoint will not increase shares getEigenPod().startCheckpoint(revertIfNoBalance); } /// @dev submit a subset of proofs for the currently active checkpoint - function verifyCheckpointProofs(BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyAdmin { + function verifyCheckpointProofs(BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyEigenlayerAdmin { getEigenPod().verifyCheckpointProofs(balanceContainerProof, proofs); } /// @dev convenience function to queue a beaconETH withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. /// It is fine to queue a withdrawal before validators have finished exiting on the beacon chain. - function queueETHWithdrawal(uint256 amount) external onlyAdmin returns (bytes32 withdrawalRoot) { + function queueETHWithdrawal(uint256 amount) external onlyEigenlayerAdmin returns (bytes32 withdrawalRoot) { // beacon eth is always 1 to 1 with deposit shares uint256[] memory depositShares = new uint256[](1); @@ -110,7 +111,7 @@ contract EtherFiNode is IEtherFiNode { /// Note that since the node is usually delegated to an operator, /// most of the time this should be called with "receiveAsTokens" = true because /// receiving shares while delegated will simply redelegate the shares. - function completeQueuedETHWithdrawals(bool receiveAsTokens) external onlyAdmin { + function completeQueuedETHWithdrawals(bool receiveAsTokens) external onlyEigenlayerAdmin { // because we are just dealing with beacon eth we don't need to populate the tokens[] array IERC20[] memory tokens = new IERC20[](1); @@ -134,7 +135,7 @@ contract EtherFiNode is IEtherFiNode { /// @dev queue a withdrawal from eigenlayer. You must wait EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS before claiming. /// For the general case of queuing a beaconETH withdrawal you can use queueETHWithdrawal instead. - function queueWithdrawals(IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyAdmin returns (bytes32[] memory withdrawalRoots) { + function queueWithdrawals(IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyEigenlayerAdmin returns (bytes32[] memory withdrawalRoots) { return delegationManager.queueWithdrawals(params); } @@ -144,7 +145,7 @@ contract EtherFiNode is IEtherFiNode { IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens - ) external onlyAdmin { + ) external onlyEigenlayerAdmin { delegationManager.completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); } @@ -152,7 +153,7 @@ contract EtherFiNode is IEtherFiNode { // @notice transfers any funds held by the node to the liquidity pool. // @dev under normal operations it is not expected for eth to accumulate in the nodes, // this is just to handle any exceptional cases such as someone sending directly to the node. - function sweepFunds() external onlyAdmin { + function sweepFunds() external onlyEigenlayerAdmin { (bool sent, ) = payable(address(liquidityPool)).call{value: address(this).balance, gas: 20000}(""); if (!sent) revert TransferFailed(); } @@ -161,12 +162,12 @@ contract EtherFiNode is IEtherFiNode { //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- - function forwardEigenPodCall(bytes calldata data) external onlyAdmin returns (bytes memory) { + function forwardEigenPodCall(bytes calldata data) external onlyCallForwarder returns (bytes memory) { // callContract will revert if targeting an EOA so it is safe if getEigenPod() returns the zero address return LibCall.callContract(address(getEigenPod()), 0, data); } - function forwardExternalCall(address to, bytes calldata data) external onlyAdmin returns (bytes memory) { + function forwardExternalCall(address to, bytes calldata data) external onlyCallForwarder returns (bytes memory) { return LibCall.callContract(to, 0, data); } @@ -174,8 +175,13 @@ contract EtherFiNode is IEtherFiNode { //----------------------------------- MODIFIERS -------------------------------------- //-------------------------------------------------------------------------------------- - modifier onlyAdmin() { - if (!roleRegistry.hasRole(ETHERFI_NODE_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + modifier onlyEigenlayerAdmin() { + if (!roleRegistry.hasRole(ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _; + } + + modifier onlyCallForwarder() { + if (!roleRegistry.hasRole(ETHERFI_NODE_CALL_FORWARDER_ROLE, msg.sender)) revert IncorrectRole(); _; } diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 84c1bf531..ca8d64703 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -41,6 +41,7 @@ contract EtherFiNodesManager is //-------------------------------------------------------------------------------------- bytes32 public constant ETHERFI_NODES_MANAGER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_ADMIN_ROLE"); + bytes32 public constant ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE = keccak256("ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE"); bytes32 public constant ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256("ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE"); //------------------------------------------------------------------------- @@ -69,46 +70,53 @@ contract EtherFiNodesManager is _unpause(); } + /// @dev under normal conditions ETH should not accumulate in the EtherFiNode. This will forward + /// the eth to the liquidity pool in the event of ETH being accidentally sent there + function sweepFunds(uint256 id) external onlyAdmin whenNotPaused { + IEtherFiNode(etherfiNodeAddress(id)).sweepFunds; + } + //-------------------------------------------------------------------------------------- //---------------------------- Eigenlayer Interactions -------------------------------- //-------------------------------------------------------------------------------------- + // Note that most of these calls are pod-level actions and it is a little awkward to always + // provide a specific validator ID. This is to maintain compatibility with much of our existing + // tooling which used to operate on a per-validator level instead of per-pod/per-node. + // Over time we will migrate to directly calling the associated method on the EtherFiNode contract where applicable. + function getEigenPod(uint256 id) public view returns (address) { return address(IEtherFiNode(etherfiNodeAddress(id)).getEigenPod()); } - function startCheckpoint(uint256 id) external onlyCallForwarder whenNotPaused { + function startCheckpoint(uint256 id) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).startCheckpoint(); } - function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyCallForwarder whenNotPaused { + function verifyCheckpointProofs(uint256 id, BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).verifyCheckpointProofs(balanceContainerProof, proofs); } - function setProofSubmitter(uint256 id, address proofSubmitter) external onlyCallForwarder whenNotPaused { + function setProofSubmitter(uint256 id, address proofSubmitter) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).setProofSubmitter(proofSubmitter); } - function queueETHWithdrawal(uint256 id, uint256 amount) external onlyCallForwarder whenNotPaused returns (bytes32 withdrawalRoot) { + function queueETHWithdrawal(uint256 id, uint256 amount) external onlyEigenlayerAdmin whenNotPaused returns (bytes32 withdrawalRoot) { return IEtherFiNode(etherfiNodeAddress(id)).queueETHWithdrawal(amount); } - function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external onlyCallForwarder whenNotPaused { + function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedETHWithdrawals(receiveAsTokens); } - function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyCallForwarder whenNotPaused { + function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawals(params); } - function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyCallForwarder whenNotPaused { + function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); } - function sweepFunds(uint256 id) external onlyCallForwarder whenNotPaused { - IEtherFiNode(etherfiNodeAddress(id)).sweepFunds; - } - //------------------------------------------------------------------- //--------------------- Key Management ---------------------------- //------------------------------------------------------------------- @@ -201,21 +209,7 @@ contract EtherFiNodesManager is emit AllowedForwardedEigenpodCallsUpdated(selector, allowed); } - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { - if (nodes.length != data.length) revert InvalidForwardedCall(); - - returnData = new bytes[](nodes.length); - for (uint256 i = 0; i < nodes.length; i++) { - - // validate the call - if (data[i].length < 4) revert InvalidForwardedCall(); - bytes4 selector = bytes4(data[i][:4]); - if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); - - returnData[i] = IEtherFiNode(nodes[i]).forwardExternalCall(target, data[i]); - } - } - + /// @notice forward a whitelisted call to a whitelisted external contract with the EtherFiNode as the caller function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { if (ids.length != data.length) revert InvalidForwardedCall(); @@ -232,22 +226,8 @@ contract EtherFiNodesManager is } } - function forwardEigenPodCall(address[] calldata etherFiNodes, bytes[] calldata data) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { - if (etherFiNodes.length != data.length) revert InvalidForwardedCall(); - - returnData = new bytes[](etherFiNodes.length); - for (uint256 i = 0; i < etherFiNodes.length; i++) { - - // validate the call - if (data[i].length < 4) revert InvalidForwardedCall(); - bytes4 selector = bytes4(data[i][:4]); - if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); - - IEtherFiNode node = IEtherFiNode(etherFiNodes[i]); - returnData[i] = node.forwardEigenPodCall(data[i]); - } - } - + /// @notice forward a whitelisted call to the associated eigenPod of the EtherFiNode with the EtherFiNode as the caller. + /// This serves to allow us to support minor eigenlayer upgrades without needing to immediately upgrade our contracts. function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external onlyCallForwarder whenNotPaused returns (bytes[] memory returnData) { if (ids.length != data.length) revert InvalidForwardedCall(); @@ -273,6 +253,11 @@ contract EtherFiNodesManager is _; } + modifier onlyEigenlayerAdmin() { + if (!roleRegistry.hasRole(ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); + _; + } + modifier onlyCallForwarder() { if (!roleRegistry.hasRole(ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE, msg.sender)) revert IncorrectRole(); _; diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 0cf00f70c..c398eecbe 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -29,9 +29,7 @@ interface IEtherFiNodesManager { // call forwarding function updateAllowedForwardedExternalCalls(bytes4 selector, address target, bool allowed) external; function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external; - function forwardExternalCall(address[] calldata nodes, bytes[] calldata data, address target) external returns (bytes[] memory returnData); function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external returns (bytes[] memory returnData); - function forwardEigenPodCall(address[] calldata nodes, bytes[] calldata data) external returns (bytes[] memory returnData); function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData); // protocol diff --git a/test/EtherFiTimelock.sol b/test/EtherFiTimelock.sol index 7eda0c3b8..1586f8806 100644 --- a/test/EtherFiTimelock.sol +++ b/test/EtherFiTimelock.sol @@ -271,32 +271,6 @@ contract TimelockTest is TestSetup { } } - function test_EIGEN_transfer() internal { - initializeRealisticFork(MAINNET_FORK); - address target = address(managerInstance); - bytes4 selector = bytes4(keccak256("transfer(address,uint256)")); - - bytes memory data = abi.encodeWithSelector(EtherFiNodesManager.updateAllowedForwardedExternalCalls.selector, selector, 0xec53bF9167f50cDEB3Ae105f56099aaaB9061F83, true); - _execute_timelock(target, data, true, true, true, true); - - address[] memory nodes = new address[](1); - bytes[] memory datas = new bytes[](1); - nodes[0] = 0xe8e39aA7E08F13f1Ccd5F38706F9e1D60C661825; - datas[0] = abi.encodeWithSelector(selector, 0x2aCA71020De61bb532008049e1Bd41E451aE8AdC, 1 ether); - - vm.prank(0x7835fB36A8143a014A2c381363cD1A4DeE586d2A); - managerInstance.forwardExternalCall(nodes, datas, 0xec53bF9167f50cDEB3Ae105f56099aaaB9061F83); - } - - /* - function test_add_updateEigenLayerOperatingAdmin() internal { - initializeRealisticFork(MAINNET_FORK); - address target = address(managerInstance); - bytes memory data = abi.encodeWithSelector(EtherFiNodesManager.updateEigenLayerOperatingAdmin.selector, 0x44358b1cc2C296fFc7419835438D1BD97Ec1FB78, true); - _execute_timelock(target, data, true, true, true, true); - } - */ - function test_efip4() public { initializeRealisticFork(MAINNET_FORK); { diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 4e2221f77..c68052325 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -77,8 +77,8 @@ contract PreludeTest is Test, ArrayTestHelper { // permissions vm.startPrank(roleRegistry.owner()); - roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_ADMIN_ROLE(), address(etherFiNodesManager)); - roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_ADMIN_ROLE(), address(stakingManager)); + roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), address(etherFiNodesManager)); + roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), address(stakingManager)); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_ADMIN_ROLE(), admin); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), forwarder); roleRegistry.grantRole(stakingManager.STAKING_MANAGER_NODE_CREATOR_ROLE(), admin); From 4288ecd2094b5d68da556b9fec730f83b06e7804 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 4 Jun 2025 17:58:13 -0600 Subject: [PATCH 29/47] WIP: updating oracle/admin for v3 prelude --- src/EtherFiAdmin.sol | 8 ++++---- src/LiquidityPool.sol | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index 4c749a862..ee12150a7 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -220,11 +220,11 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { if (taskType == TaskType.ValidatorApproval) { liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures); } else if (taskType == TaskType.SendExitRequests) { - liquidityPool.sendExitRequests(_validators); + liquidityPool.DEPRECATED_sendExitRequests(_validators); } else if (taskType == TaskType.ProcessNodeExit) { - // nothing to do + // nothing to do anymore (v3 prelude upgrade) } else if (taskType == TaskType.MarkBeingSlashed) { - // nothing to do + // nothing to do anymore (v3 prelude upgrade) } emit ValidatorManagementTaskCompleted(taskHash, _reportHash, _validators, _timestamps, taskType); } @@ -306,7 +306,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { function _handleValidators(bytes32 _reportHash, IEtherFiOracle.OracleReport calldata _report) internal { uint32[] memory emptyTimestamps = new uint32[](0); _enqueueValidatorManagementTask(_reportHash, _report.validatorsToApprove, emptyTimestamps, TaskType.ValidatorApproval); - _enqueueValidatorManagementTask(_reportHash, _report.exitedValidators, _report.exitedValidatorsExitTimestamps, TaskType.ProcessNodeExit); + _enqueueValidatorManagementTask(_reportHash, _report.liquidityPoolValidatorsToExit, emptyTimestamps, TaskType.SendExitRequests); } function _handleWithdrawals(IEtherFiOracle.OracleReport calldata _report) internal { diff --git a/src/LiquidityPool.sol b/src/LiquidityPool.sol index e4f1fece5..e974615f7 100644 --- a/src/LiquidityPool.sol +++ b/src/LiquidityPool.sol @@ -395,7 +395,7 @@ contract LiquidityPool is Initializable, OwnableUpgradeable, UUPSUpgradeable, IL } /// @notice Send the exit requests as the T-NFT holder of the LiquidityPool validators - function sendExitRequests(uint256[] calldata _validatorIds) external { + function DEPRECATED_sendExitRequests(uint256[] calldata _validatorIds) external { if (!roleRegistry.hasRole(LIQUIDITY_POOL_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); for (uint256 i = 0; i < _validatorIds.length; i++) { From c820841928a25ac270e5b31058e858e6804ed9b1 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 5 Jun 2025 10:04:49 -0600 Subject: [PATCH 30/47] [M-03] audit fixes --- src/EtherFiAdmin.sol | 3 +-- src/EtherFiOracle.sol | 6 ++---- src/interfaces/ILiquidityPool.sol | 2 +- test/LiquidityPool.t.sol | 6 +++--- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index ee12150a7..126c6b79b 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -220,7 +220,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { if (taskType == TaskType.ValidatorApproval) { liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures); } else if (taskType == TaskType.SendExitRequests) { - liquidityPool.DEPRECATED_sendExitRequests(_validators); + // nothing to do anymore (v3 prelude upgrade) } else if (taskType == TaskType.ProcessNodeExit) { // nothing to do anymore (v3 prelude upgrade) } else if (taskType == TaskType.MarkBeingSlashed) { @@ -306,7 +306,6 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { function _handleValidators(bytes32 _reportHash, IEtherFiOracle.OracleReport calldata _report) internal { uint32[] memory emptyTimestamps = new uint32[](0); _enqueueValidatorManagementTask(_reportHash, _report.validatorsToApprove, emptyTimestamps, TaskType.ValidatorApproval); - _enqueueValidatorManagementTask(_reportHash, _report.liquidityPoolValidatorsToExit, emptyTimestamps, TaskType.SendExitRequests); } function _handleWithdrawals(IEtherFiOracle.OracleReport calldata _report) internal { diff --git a/src/EtherFiOracle.sol b/src/EtherFiOracle.sol index 827bee799..c2402a350 100644 --- a/src/EtherFiOracle.sol +++ b/src/EtherFiOracle.sol @@ -210,9 +210,7 @@ contract EtherFiOracle is Initializable, OwnableUpgradeable, PausableUpgradeable bytes32 chunk2 = keccak256( abi.encode( - _report.validatorsToApprove, - _report.exitedValidators, - _report.exitedValidatorsExitTimestamps + _report.validatorsToApprove ) ); @@ -330,4 +328,4 @@ contract EtherFiOracle is Initializable, OwnableUpgradeable, PausableUpgradeable require(admins[msg.sender] || msg.sender == owner(), "EtherFiAdmin: not an admin"); _; } -} \ No newline at end of file +} diff --git a/src/interfaces/ILiquidityPool.sol b/src/interfaces/ILiquidityPool.sol index 13900da2f..85f6b537d 100644 --- a/src/interfaces/ILiquidityPool.sol +++ b/src/interfaces/ILiquidityPool.sol @@ -64,7 +64,7 @@ interface ILiquidityPool { function batchRegister(IStakingManager.DepositData[] calldata _depositData, uint256[] calldata _bidIds, address _etherFiNode) external; function batchApproveRegistration(uint256[] memory _validatorIds, bytes[] calldata _pubkeys, bytes[] calldata _signatures) external; function confirmAndFundBeaconValidators(IStakingManager.DepositData[] calldata depositData, uint256 validatorSizeWei) external; - function sendExitRequests(uint256[] calldata _validatorIds) external; + function DEPRECATED_sendExitRequests(uint256[] calldata _validatorIds) external; function registerValidatorSpawner(address _user) external; function unregisterValidatorSpawner(address _user) external; diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index 8d4e61bd7..315304895 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -322,7 +322,7 @@ contract LiquidityPoolTest is TestSetup { uint256[] memory newValidators = new uint256[](10); vm.expectRevert(LiquidityPool.IncorrectRole.selector); vm.prank(elvis); - liquidityPoolInstance.sendExitRequests(newValidators); + liquidityPoolInstance.DEPRECATED_sendExitRequests(newValidators); } // TODO(dave): update when v3 changes finalized @@ -459,10 +459,10 @@ contract LiquidityPoolTest is TestSetup { vm.warp(1681075815 - 7 * 24 * 3600); // Sun Apr 02 2023 21:30:15 UTC vm.expectRevert(LiquidityPool.IncorrectRole.selector); - liquidityPoolInstance.sendExitRequests(newValidators); + liquidityPoolInstance.DEPRECATED_sendExitRequests(newValidators); vm.prank(alice); - liquidityPoolInstance.sendExitRequests(newValidators); + liquidityPoolInstance.DEPRECATED_sendExitRequests(newValidators); uint32[] memory exitRequestTimestamps = new uint32[](2); exitRequestTimestamps[0] = uint32(block.timestamp - 1000); From 4f59539aa80995cf6b98f01ccdb24b695b78a0f8 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 6 Jun 2025 15:43:45 -0600 Subject: [PATCH 31/47] permission tests for v3 prelude functions --- src/EtherFiNodesManager.sol | 2 +- src/StakingManager.sol | 3 +- src/interfaces/IEtherFiNodesManager.sol | 2 +- test/prelude.t.sol | 166 +++++++++++++++++++++++- 4 files changed, 169 insertions(+), 4 deletions(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index ca8d64703..b883062e5 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -113,7 +113,7 @@ contract EtherFiNodesManager is IEtherFiNode(etherfiNodeAddress(id)).queueWithdrawals(params); } - function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyEigenlayerAdmin whenNotPaused { + function completeQueuedWithdrawals(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external onlyEigenlayerAdmin whenNotPaused { IEtherFiNode(etherfiNodeAddress(id)).completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); } diff --git a/src/StakingManager.sol b/src/StakingManager.sol index d5ac8b762..565640e88 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -159,7 +159,8 @@ contract StakingManager is /// @notice Upgrades the etherfi node /// @param _newImplementation The new address of the etherfi node - function upgradeEtherFiNode(address _newImplementation) public onlyOwner { + function upgradeEtherFiNode(address _newImplementation) external { + roleRegistry.onlyProtocolUpgrader(msg.sender); if (_newImplementation == address(0)) revert InvalidUpgrade(); etherFiNodeBeacon.upgradeTo(_newImplementation); diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index c398eecbe..9a9cae634 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -23,7 +23,7 @@ interface IEtherFiNodesManager { function queueETHWithdrawal(uint256 id, uint256 amount) external returns (bytes32 withdrawalRoot); function completeQueuedETHWithdrawals(uint256 id, bool receiveAsTokens) external; function queueWithdrawals(uint256 id, IDelegationManager.QueuedWithdrawalParams[] calldata params) external; - function completeQueuedWithdrawal(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external; + function completeQueuedWithdrawals(uint256 id, IDelegationManager.Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, bool[] calldata receiveAsTokens) external; function sweepFunds(uint256 id) external; // call forwarding diff --git a/test/prelude.t.sol b/test/prelude.t.sol index c68052325..6fe134b0a 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -23,6 +23,7 @@ contract PreludeTest is Test, ArrayTestHelper { ILiquidityPool liquidityPool; EtherFiNodesManager etherFiNodesManager; AuctionManager auctionManager; + EtherFiNode etherFiNodeImpl; ITNFT tnft; IBNFT bnft; NodeOperatorManager nodeOperatorManager = NodeOperatorManager(0xd5edf7730ABAd812247F6F54D7bd31a52554e35E); @@ -35,6 +36,11 @@ contract PreludeTest is Test, ArrayTestHelper { address etherFiNodeBeacon = address(0x3c55986Cfee455E2533F4D29006634EcF9B7c03F); RoleRegistry roleRegistry = RoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + // role users + address eigenlayerAdmin = vm.addr(0xABABAB); + address callForwarder = vm.addr(0xCDCDCD); + address user = vm.addr(0xEFEFEF); + function setUp() public { vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); @@ -57,7 +63,7 @@ contract PreludeTest is Test, ArrayTestHelper { stakingManager.upgradeTo(address(stakingManagerImpl)); // upgrade etherFiNode impl - EtherFiNode etherFiNodeImpl = new EtherFiNode( + etherFiNodeImpl = new EtherFiNode( address(liquidityPool), address(etherFiNodesManager), eigenPodManager, @@ -79,8 +85,10 @@ contract PreludeTest is Test, ArrayTestHelper { vm.startPrank(roleRegistry.owner()); roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), address(etherFiNodesManager)); roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), address(stakingManager)); + roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), eigenlayerAdmin); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_ADMIN_ROLE(), admin); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), forwarder); + roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE(), eigenlayerAdmin); roleRegistry.grantRole(stakingManager.STAKING_MANAGER_NODE_CREATOR_ROLE(), admin); vm.stopPrank(); @@ -188,4 +196,160 @@ contract PreludeTest is Test, ArrayTestHelper { vm.prank(forwarder); etherFiNodesManager.completeQueuedETHWithdrawals(uint256(pubkeyHash), true); } + + function test_EtherFiNodePermissions() public { + + // create a node + vm.prank(admin); + IEtherFiNode etherFiNode = IEtherFiNode(stakingManager.instantiateEtherFiNode(true)); + + vm.startPrank(user); + + // Normal user should fail for all eigenlayer functions + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.createEigenPod(); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.setProofSubmitter(address(0)); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.startCheckpoint(); + + BeaconChainProofs.BalanceProof[] memory balanceProofs = new BeaconChainProofs.BalanceProof[](1); + BeaconChainProofs.BalanceContainerProof memory containerProof = BeaconChainProofs.BalanceContainerProof({ + balanceContainerRoot: bytes32(uint256(1)), + proof: "" + }); + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.verifyCheckpointProofs(containerProof, balanceProofs); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.queueETHWithdrawal(1 ether); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.completeQueuedETHWithdrawals(true); + + IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.queueWithdrawals(params); + + IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](1); + IERC20[][] memory tokens = new IERC20[][](1); + bool[] memory receiveAsTokens = new bool[](1); + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.sweepFunds(); + + // normal user should fail for all call forwarding + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.forwardEigenPodCall(""); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.forwardExternalCall(address(0), ""); + + vm.stopPrank(); + } + + + function test_EtherFiNodesManagerPermissions() public { + + uint256 nodeId = 1; + + // none of the EFNM roles should be allowed to upgrade + vm.expectRevert(IRoleRegistry.OnlyProtocolUpgrader.selector); + vm.prank(eigenlayerAdmin); + etherFiNodesManager.upgradeTo(address(0)); + vm.expectRevert(IRoleRegistry.OnlyProtocolUpgrader.selector); + vm.prank(user); + etherFiNodesManager.upgradeTo(address(0)); + vm.expectRevert(IRoleRegistry.OnlyProtocolUpgrader.selector); + vm.prank(callForwarder); + etherFiNodesManager.upgradeTo(address(0)); + + vm.startPrank(user); + + // Normal user should fail for all eigenlayer functions + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.setProofSubmitter(nodeId, address(0)); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.startCheckpoint(nodeId); + + BeaconChainProofs.BalanceProof[] memory balanceProofs = new BeaconChainProofs.BalanceProof[](1); + BeaconChainProofs.BalanceContainerProof memory containerProof = BeaconChainProofs.BalanceContainerProof({ + balanceContainerRoot: bytes32(uint256(1)), + proof: "" + }); + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.verifyCheckpointProofs(nodeId, containerProof, balanceProofs); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.queueETHWithdrawal(nodeId, 1 ether); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.completeQueuedETHWithdrawals(nodeId, true); + + IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.queueWithdrawals(nodeId, params); + + IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](1); + IERC20[][] memory tokens = new IERC20[][](1); + bool[] memory receiveAsTokens = new bool[](1); + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.completeQueuedWithdrawals(nodeId, withdrawals, tokens, receiveAsTokens); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.sweepFunds(nodeId); + + // normal user should fail for all call forwarding + uint256[] memory nodeIds = new uint256[](1); + bytes[] memory data = new bytes[](1); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.forwardEigenPodCall(nodeIds, data); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.forwardExternalCall(nodeIds, data, address(0)); + + vm.stopPrank(); + } + + function test_StakingManagerPermissions() public { + + // only liquidityPool can call createBeaconValidators + bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot("", "", "", 1 ether); + IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ + publicKey: "", + signature: "", + depositDataRoot: initialDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + uint256[] memory bidIds = new uint256[](1); + vm.prank(admin); + vm.expectRevert(IStakingManager.IncorrectRole.selector); + stakingManager.createBeaconValidators(toArray(initialDepositData), bidIds, address(0)); + + // only liquidityPool can call confirmAndFundBeaconValidators + bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot("", "", "", 0); + IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ + publicKey: "", + signature: "", + depositDataRoot: 0, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + vm.prank(address(admin)); + vm.expectRevert(IStakingManager.IncorrectRole.selector); + stakingManager.confirmAndFundBeaconValidators(toArray(confirmDepositData), 32 ether); + + // only protocolUpgrader can upgrade etherFiNode + vm.prank(admin); + vm.expectRevert(IRoleRegistry.OnlyProtocolUpgrader.selector); + stakingManager.upgradeEtherFiNode(address(1)); + + vm.prank(roleRegistry.owner()); + stakingManager.upgradeEtherFiNode(address(1)); + } } From 21129f1dc2255cb3c3e8aa6d492701ea7bd8fb35 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 9 Jun 2025 11:44:54 -0600 Subject: [PATCH 32/47] FIX [M-02] audit item --- src/EtherFiNode.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index ed72a9453..1841eb64c 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -121,7 +121,7 @@ contract EtherFiNode is IEtherFiNode { // skip this withdrawal if not enough time has passed uint32 slashableUntil = queuedWithdrawals[i].startBlock + EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS; - if (uint32(block.number) < slashableUntil) continue; + if (uint32(block.number) <= slashableUntil) continue; delegationManager.completeQueuedWithdrawal(queuedWithdrawals[i], tokens, receiveAsTokens); } From 08aa561c9c44deecebb60b21613b41dd358641f5 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 10 Jun 2025 11:01:25 -0600 Subject: [PATCH 33/47] [I-02] additional audit fixes --- src/EtherFiNodesManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index b883062e5..42f80407e 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -133,7 +133,7 @@ contract EtherFiNodesManager is } /// @dev associate the provided pubkey with particular EtherFiNode instance. - function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external { + function linkPubkeyToNode(bytes calldata pubkey, address nodeAddress, uint256 legacyId) external whenNotPaused { if (msg.sender != address(stakingManager)) revert InvalidCaller(); bytes32 pubkeyHash = calculateValidatorPubkeyHash(pubkey); if (address(etherFiNodeFromPubkeyHash[pubkeyHash]) != address(0)) revert AlreadyLinked(); From 8fad1ef7a26f0fb31cbc9ac0d012ca78e36ddd8e Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 10 Jun 2025 12:26:06 -0600 Subject: [PATCH 34/47] improve permissions for node call forwarding --- src/EtherFiNode.sol | 12 ++++ src/EtherFiNodesManager.sol | 15 +---- src/interfaces/IEtherFiNode.sol | 2 + src/interfaces/IEtherFiNodesManager.sol | 4 ++ test/prelude.t.sol | 84 ++++++++++++++++++++++++- 5 files changed, 103 insertions(+), 14 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index 1841eb64c..e719f3c70 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -163,11 +163,23 @@ contract EtherFiNode is IEtherFiNode { //-------------------------------------------------------------------------------------- function forwardEigenPodCall(bytes calldata data) external onlyCallForwarder returns (bytes memory) { + + // validate the call + if (data.length < 4) revert InvalidForwardedCall(); + bytes4 selector = bytes4(data[:4]); + if (!etherFiNodesManager.allowedForwardedEigenpodCalls(selector)) revert ForwardedCallNotAllowed(); + // callContract will revert if targeting an EOA so it is safe if getEigenPod() returns the zero address return LibCall.callContract(address(getEigenPod()), 0, data); } function forwardExternalCall(address to, bytes calldata data) external onlyCallForwarder returns (bytes memory) { + + // validate the call + if (data.length < 4) revert InvalidForwardedCall(); + bytes4 selector = bytes4(data[:4]); + if (!etherFiNodesManager.allowedForwardedExternalCalls(selector, to)) revert ForwardedCallNotAllowed(); + return LibCall.callContract(to, 0, data); } diff --git a/src/EtherFiNodesManager.sol b/src/EtherFiNodesManager.sol index 42f80407e..606004ad4 100644 --- a/src/EtherFiNodesManager.sol +++ b/src/EtherFiNodesManager.sol @@ -189,9 +189,6 @@ contract EtherFiNodesManager is //-------------------------------- CALL FORWARDING ------------------------------------ //-------------------------------------------------------------------------------------- - error ForwardedCallNotAllowed(); - error InvalidForwardedCall(); - /// @notice Update the whitelist for external calls that can be executed by an EtherfiNode /// @param selector method selector /// @param target call target for forwarded call @@ -216,11 +213,7 @@ contract EtherFiNodesManager is returnData = new bytes[](ids.length); for (uint256 i = 0; i < ids.length; i++) { - // validate the call - if (data[i].length < 4) revert InvalidForwardedCall(); - bytes4 selector = bytes4(data[i][:4]); - if (!allowedForwardedExternalCalls[selector][target]) revert ForwardedCallNotAllowed(); - + // call validation + whitelist checks performed in node implementation IEtherFiNode node = IEtherFiNode(etherfiNodeAddress(ids[i])); returnData[i] = node.forwardExternalCall(target, data[i]); } @@ -234,11 +227,7 @@ contract EtherFiNodesManager is returnData = new bytes[](ids.length); for (uint256 i = 0; i < ids.length; i++) { - // validate the call - if (data[i].length < 4) revert InvalidForwardedCall(); - bytes4 selector = bytes4(data[i][:4]); - if (!allowedForwardedEigenpodCalls[selector]) revert ForwardedCallNotAllowed(); - + // call validation + whitelist checks performed in node implementation IEtherFiNode node = IEtherFiNode(etherfiNodeAddress(ids[i])); returnData[i] = node.forwardEigenPodCall(data[i]); } diff --git a/src/interfaces/IEtherFiNode.sol b/src/interfaces/IEtherFiNode.sol index 871d720b1..7d1e58b70 100644 --- a/src/interfaces/IEtherFiNode.sol +++ b/src/interfaces/IEtherFiNode.sol @@ -87,5 +87,7 @@ interface IEtherFiNode { error TransferFailed(); error IncorrectRole(); + error ForwardedCallNotAllowed(); + error InvalidForwardedCall(); } diff --git a/src/interfaces/IEtherFiNodesManager.sol b/src/interfaces/IEtherFiNodesManager.sol index 9a9cae634..8faaa2bae 100644 --- a/src/interfaces/IEtherFiNodesManager.sol +++ b/src/interfaces/IEtherFiNodesManager.sol @@ -31,6 +31,8 @@ interface IEtherFiNodesManager { function updateAllowedForwardedEigenpodCalls(bytes4 selector, bool allowed) external; function forwardExternalCall(uint256[] calldata ids, bytes[] calldata data, address target) external returns (bytes[] memory returnData); function forwardEigenPodCall(uint256[] calldata ids, bytes[] calldata data) external returns (bytes[] memory returnData); + function allowedForwardedEigenpodCalls(bytes4 selector) external returns (bool); + function allowedForwardedExternalCalls(bytes4 selector, address to) external returns (bool); // protocol function pauseContract() external; @@ -112,6 +114,8 @@ interface IEtherFiNodesManager { error LengthMismatch(); error InvalidCaller(); error IncorrectRole(); + error ForwardedCallNotAllowed(); + error InvalidForwardedCall(); } diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 6fe134b0a..d23fc0800 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -86,6 +86,7 @@ contract PreludeTest is Test, ArrayTestHelper { roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), address(etherFiNodesManager)); roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), address(stakingManager)); roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), eigenlayerAdmin); + roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_CALL_FORWARDER_ROLE(), address(etherFiNodesManager)); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_ADMIN_ROLE(), admin); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), forwarder); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE(), eigenlayerAdmin); @@ -94,6 +95,87 @@ contract PreludeTest is Test, ArrayTestHelper { } + function test_forwardingWhitelist() public { + + // create a node + pod + vm.prank(admin); + IEtherFiNode etherFiNode = IEtherFiNode(stakingManager.instantiateEtherFiNode(true /*createEigenPod*/)); + + // link it to an arbitrary id + uint256 legacyID = 10885; + bytes memory pubkey = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; + vm.prank(admin); + etherFiNodesManager.linkLegacyValidatorIds(toArray_u256(legacyID), toArray_bytes(pubkey)); + + // user with no role should not be able to forward calls + vm.startPrank(user); + { + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.forwardEigenPodCall(""); + + vm.expectRevert(IEtherFiNode.IncorrectRole.selector); + etherFiNode.forwardExternalCall(address(0), ""); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.forwardEigenPodCall(toArray_u256(legacyID), toArray_bytes("")); + + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.forwardExternalCall(toArray_u256(legacyID), toArray_bytes(""), address(0)); + + } + vm.stopPrank(); + + // grant roles + vm.startPrank(roleRegistry.owner()); + { + roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), user); + roleRegistry.grantRole(EtherFiNode(payable(address(etherFiNode))).ETHERFI_NODE_CALL_FORWARDER_ROLE(), user); + } + vm.stopPrank(); + + bytes4 decimalsSelector = hex"313ce567"; // ERC20 decimals() + bytes4 checkpointSelector = hex"47d28372"; // eigenpod currentCheckpoint() + bytes memory data = hex"313ce567"; + bytes memory checkpointData = hex"47d28372" ; + address target = 0xFe0c30065B384F05761f15d0CC899D4F9F9Cc0eB; // ethfi + + // user should fail due to calls not being whitelisted + vm.startPrank(user); + { + vm.expectRevert(IEtherFiNode.ForwardedCallNotAllowed.selector); + etherFiNode.forwardEigenPodCall(checkpointData); + + vm.expectRevert(IEtherFiNode.ForwardedCallNotAllowed.selector); + etherFiNode.forwardExternalCall(address(0), data); + + vm.expectRevert(IEtherFiNodesManager.ForwardedCallNotAllowed.selector); + etherFiNodesManager.forwardEigenPodCall(toArray_u256(legacyID), toArray_bytes(checkpointData)); + + vm.expectRevert(IEtherFiNode.ForwardedCallNotAllowed.selector); + etherFiNodesManager.forwardExternalCall(toArray_u256(legacyID), toArray_bytes(data), target); + } + vm.stopPrank(); + + // whitelist calls + vm.startPrank(admin); + { + etherFiNodesManager.updateAllowedForwardedExternalCalls(decimalsSelector, target, true); + etherFiNodesManager.updateAllowedForwardedEigenpodCalls(checkpointSelector, true); + } + vm.stopPrank(); + + // calls should succeed after being whitelisted + vm.startPrank(user); + { + etherFiNode.forwardEigenPodCall(checkpointData); + etherFiNode.forwardExternalCall(target, data); + etherFiNodesManager.forwardEigenPodCall(toArray_u256(legacyID), toArray_bytes(checkpointData)); + etherFiNodesManager.forwardExternalCall(toArray_u256(legacyID), toArray_bytes(data), target); + } + vm.stopPrank(); + + } + function test_StakingManagerUpgradePermissions() public { // deploy new staking manager implementation @@ -134,7 +216,7 @@ contract PreludeTest is Test, ArrayTestHelper { bytes memory signature = hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df"; vm.prank(admin); - address etherFiNode = stakingManager.instantiateEtherFiNode(true); + address etherFiNode = stakingManager.instantiateEtherFiNode(true /*createEigenPod*/); address eigenPod = address(IEtherFiNode(etherFiNode).getEigenPod()); bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( From c850dca3551ddb2b07373ddc4ec9cb44ed3f005f Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 10 Jun 2025 13:04:26 -0600 Subject: [PATCH 35/47] skip withdrawals that are not simple beacon eth withdrawals --- src/EtherFiNode.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index e719f3c70..0492c8a91 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -119,9 +119,11 @@ contract EtherFiNode is IEtherFiNode { (IDelegationManager.Withdrawal[] memory queuedWithdrawals, ) = delegationManager.getQueuedWithdrawals(address(this)); for (uint256 i = 0; i < queuedWithdrawals.length; i++) { - // skip this withdrawal if not enough time has passed + // skip this withdrawal if not enough time has passed or if it is not a simple beaconETH withdrawal uint32 slashableUntil = queuedWithdrawals[i].startBlock + EIGENLAYER_WITHDRAWAL_DELAY_BLOCKS; if (uint32(block.number) <= slashableUntil) continue; + if (queuedWithdrawals[i].strategies.length != 1) continue; + if (queuedWithdrawals[i].strategies[0] != IStrategy(address(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0))) continue; delegationManager.completeQueuedWithdrawal(queuedWithdrawals[i], tokens, receiveAsTokens); } @@ -149,7 +151,6 @@ contract EtherFiNode is IEtherFiNode { delegationManager.completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); } - // @notice transfers any funds held by the node to the liquidity pool. // @dev under normal operations it is not expected for eth to accumulate in the nodes, // this is just to handle any exceptional cases such as someone sending directly to the node. From feaf4dfd8959645d125097435ab0e66b242a9524 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 10 Jun 2025 13:09:18 -0600 Subject: [PATCH 36/47] test cleanup --- test/prelude.t.sol | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/test/prelude.t.sol b/test/prelude.t.sol index d23fc0800..c1bb6be0b 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -189,7 +189,7 @@ contract PreludeTest is Test, ArrayTestHelper { ); // only owner should be able to upgrade - vm.expectRevert("Ownable: caller is not the owner"); + vm.expectRevert(IRoleRegistry.OnlyProtocolUpgrader.selector); stakingManager.upgradeTo(address(stakingManagerImpl)); // should succeed when called by owner @@ -267,16 +267,21 @@ contract PreludeTest is Test, ArrayTestHelper { vm.prank(admin); etherFiNodesManager.linkLegacyValidatorIds(toArray_u256(legacyID), toArray_bytes(validatorPubkey)); - vm.prank(forwarder); + vm.prank(eigenlayerAdmin); etherFiNodesManager.queueETHWithdrawal(uint256(pubkeyHash), 1 ether); // poke some withdrawable funds into the restakedExecutionLayerGwei storage slot of the eigenpod address eigenpod = etherFiNodesManager.getEigenPod(uint256(pubkeyHash)); vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(50 ether / 1 gwei))); + uint256 startingBalance = address(liquidityPool).balance; + vm.roll(block.number + (7200 * 15)); - vm.prank(forwarder); + vm.prank(eigenlayerAdmin); etherFiNodesManager.completeQueuedETHWithdrawals(uint256(pubkeyHash), true); + + // liquidity pool should have received withdrawal + assertEq(address(liquidityPool).balance, startingBalance + 1 ether); } function test_EtherFiNodePermissions() public { @@ -401,37 +406,42 @@ contract PreludeTest is Test, ArrayTestHelper { function test_StakingManagerPermissions() public { + bytes memory pubkey = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; + bytes memory signature = hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3df"; + // only liquidityPool can call createBeaconValidators - bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot("", "", "", 1 ether); + bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot(pubkey, signature, "", 1 ether); IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ - publicKey: "", - signature: "", + publicKey: pubkey, + signature: signature, depositDataRoot: initialDepositRoot, ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" }); uint256[] memory bidIds = new uint256[](1); vm.prank(admin); - vm.expectRevert(IStakingManager.IncorrectRole.selector); + vm.expectRevert(IStakingManager.InvalidCaller.selector); stakingManager.createBeaconValidators(toArray(initialDepositData), bidIds, address(0)); // only liquidityPool can call confirmAndFundBeaconValidators - bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot("", "", "", 0); + bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot(pubkey, signature, "", 31 ether); IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ - publicKey: "", - signature: "", + publicKey: pubkey, + signature: signature, depositDataRoot: 0, ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" }); vm.prank(address(admin)); - vm.expectRevert(IStakingManager.IncorrectRole.selector); + vm.expectRevert(IStakingManager.InvalidCaller.selector); stakingManager.confirmAndFundBeaconValidators(toArray(confirmDepositData), 32 ether); + // only protocolUpgrader can upgrade etherFiNode + EtherFiNode nodeImpl = new EtherFiNode(address(0), address(0), address(0), address(0), address(0)); vm.prank(admin); vm.expectRevert(IRoleRegistry.OnlyProtocolUpgrader.selector); - stakingManager.upgradeEtherFiNode(address(1)); + stakingManager.upgradeEtherFiNode(address(nodeImpl)); vm.prank(roleRegistry.owner()); - stakingManager.upgradeEtherFiNode(address(1)); + stakingManager.upgradeEtherFiNode(address(nodeImpl)); } } From ad70513c013912cba46026f7ce22f2fb1920012d Mon Sep 17 00:00:00 2001 From: Vaibhav Valecha Date: Mon, 16 Jun 2025 17:40:13 -0400 Subject: [PATCH 37/47] deprecate non validator spinup tasks --- src/EtherFiAdmin.sol | 72 ++++------- src/interfaces/IEtherFiOracle.sol | 2 - test/EtherFiAdminUpgrade.t.sol | 198 ------------------------------ test/TestSetup.sol | 24 ++-- 4 files changed, 37 insertions(+), 259 deletions(-) delete mode 100644 test/EtherFiAdminUpgrade.t.sol diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index 126c6b79b..256331c04 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -21,17 +21,10 @@ interface IEtherFiPausable { contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { - enum TaskType { - ValidatorApproval, - SendExitRequests, - ProcessNodeExit, - MarkBeingSlashed - } struct TaskStatus { bool completed; bool exists; - TaskType taskType; } IEtherFiOracle public etherFiOracle; @@ -55,7 +48,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { mapping(address => bool) public DEPRECATED_pausers; - mapping(bytes32 => TaskStatus) public validatorManagementTaskStatus; + mapping(bytes32 => TaskStatus) public validatorApprovalTaskStatus; uint16 validatorTaskBatchSize; RoleRegistry public roleRegistry; @@ -66,9 +59,9 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { event AdminUpdated(address _address, bool _isAdmin); event AdminOperationsExecuted(address indexed _address, bytes32 indexed _reportHash); - event ValidatorManagementTaskCreated(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators, uint32[] _timestamps, TaskType _taskType); - event ValidatorManagementTaskCompleted(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators, uint32[] _timestamps, TaskType _taskType); - event ValidatorManagementTaskInvalidated(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators, uint32[] _timestamps,TaskType _taskType); + event ValidatorApprovalTaskCreated(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators); + event ValidatorApprovalTaskCompleted(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators); + event ValidatorApprovalTaskInvalidated(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators); error IncorrectRole(); @@ -205,38 +198,27 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit AdminOperationsExecuted(msg.sender, reportHash); } - //_timestamp will only be used for TaskType.ProcessNodeExit and pubkeys and signatures will only be used for TaskType.ValidatorApproval - function executeValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, uint32[] calldata _timestamps, bytes[] calldata _pubKeys, bytes[] calldata _signatures) external { + function executeValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, bytes[] calldata _pubKeys, bytes[] calldata _signatures) external { if (!roleRegistry.hasRole(ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE, msg.sender)) revert IncorrectRole(); require(etherFiOracle.isConsensusReached(_reportHash), "EtherFiAdmin: report didn't reach consensus"); - bytes32 taskHash = keccak256(abi.encode(_reportHash, _validators, _timestamps)); - require(validatorManagementTaskStatus[taskHash].exists, "EtherFiAdmin: task doesn't exist"); - require(!validatorManagementTaskStatus[taskHash].completed, "EtherFiAdmin: task already completed"); - TaskType taskType = validatorManagementTaskStatus[taskHash].taskType; - - validatorManagementTaskStatus[taskHash].completed = true; - - if (taskType == TaskType.ValidatorApproval) { - liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures); - } else if (taskType == TaskType.SendExitRequests) { - // nothing to do anymore (v3 prelude upgrade) - } else if (taskType == TaskType.ProcessNodeExit) { - // nothing to do anymore (v3 prelude upgrade) - } else if (taskType == TaskType.MarkBeingSlashed) { - // nothing to do anymore (v3 prelude upgrade) - } - emit ValidatorManagementTaskCompleted(taskHash, _reportHash, _validators, _timestamps, taskType); + bytes32 taskHash = keccak256(abi.encode(_reportHash, _validators)); + require(validatorApprovalTaskStatus[taskHash].exists, "EtherFiAdmin: task doesn't exist"); + require(!validatorApprovalTaskStatus[taskHash].completed, "EtherFiAdmin: task already completed"); + + validatorApprovalTaskStatus[taskHash].completed = true; + liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures); + emit ValidatorApprovalTaskCompleted(taskHash, _reportHash, _validators); } - function invalidateValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, uint32[] calldata _timestamps) external { + function invalidateValidatorApprovalTask(bytes32 _reportHash, uint256[] calldata _validators) external { if (!roleRegistry.hasRole(ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE, msg.sender)) revert IncorrectRole(); - bytes32 taskHash = keccak256(abi.encode(_reportHash, _validators, _timestamps)); - require(validatorManagementTaskStatus[taskHash].exists, "EtherFiAdmin: task doesn't exist"); - require(!validatorManagementTaskStatus[taskHash].completed, "EtherFiAdmin: task already completed"); - validatorManagementTaskStatus[taskHash].exists = false; - emit ValidatorManagementTaskInvalidated(taskHash, _reportHash, _validators, _timestamps, validatorManagementTaskStatus[taskHash].taskType); + bytes32 taskHash = keccak256(abi.encode(_reportHash, _validators)); + require(validatorApprovalTaskStatus[taskHash].exists, "EtherFiAdmin: task doesn't exist"); + require(!validatorApprovalTaskStatus[taskHash].completed, "EtherFiAdmin: task already completed"); + validatorApprovalTaskStatus[taskHash].exists = false; + emit ValidatorApprovalTaskInvalidated(taskHash, _reportHash, _validators); } //protocol owns the eth that was distributed to NO and treasury in eigenpods and etherfinodes @@ -277,7 +259,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { membershipManager.rebase(_report.accruedRewards); } - function _enqueueValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, uint32[] memory _timestamps, TaskType taskType) internal { + function _enqueueValidatorApprovalTask(bytes32 _reportHash, uint256[] calldata _validators) internal { uint256 numBatches = (_validators.length + validatorTaskBatchSize - 1) / validatorTaskBatchSize; if(_validators.length == 0) { @@ -286,26 +268,20 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { for (uint256 i = 0; i < numBatches; i++) { uint256 start = i * validatorTaskBatchSize; uint256 end = (i + 1) * validatorTaskBatchSize > _validators.length ? _validators.length : (i + 1) * validatorTaskBatchSize; - uint256 timestampSize = taskType == TaskType.ProcessNodeExit ? end - start : 0; uint256[] memory batchValidators = new uint256[](end - start); - uint32[] memory batchTimestamps = new uint32[](timestampSize); for (uint256 j = start; j < end; j++) { batchValidators[j - start] = _validators[j]; - if(taskType == TaskType.ProcessNodeExit) { - batchTimestamps[j - start] = _timestamps[j]; - } } - bytes32 taskHash = keccak256(abi.encode(_reportHash, batchValidators, batchTimestamps)); - require(!validatorManagementTaskStatus[taskHash].exists, "Task already exists"); - validatorManagementTaskStatus[taskHash] = TaskStatus({completed: false, exists: true, taskType: taskType}); - emit ValidatorManagementTaskCreated(taskHash, _reportHash, batchValidators, batchTimestamps, taskType); + bytes32 taskHash = keccak256(abi.encode(_reportHash, batchValidators)); + require(!validatorApprovalTaskStatus[taskHash].exists, "Task already exists"); + validatorApprovalTaskStatus[taskHash] = TaskStatus({completed: false, exists: true}); + emit ValidatorApprovalTaskCreated(taskHash, _reportHash, batchValidators); } } function _handleValidators(bytes32 _reportHash, IEtherFiOracle.OracleReport calldata _report) internal { - uint32[] memory emptyTimestamps = new uint32[](0); - _enqueueValidatorManagementTask(_reportHash, _report.validatorsToApprove, emptyTimestamps, TaskType.ValidatorApproval); + _enqueueValidatorApprovalTask(_reportHash, _report.validatorsToApprove); } function _handleWithdrawals(IEtherFiOracle.OracleReport calldata _report) internal { diff --git a/src/interfaces/IEtherFiOracle.sol b/src/interfaces/IEtherFiOracle.sol index 0dc6e6d85..fd68b65f6 100644 --- a/src/interfaces/IEtherFiOracle.sol +++ b/src/interfaces/IEtherFiOracle.sol @@ -11,8 +11,6 @@ interface IEtherFiOracle { int128 accruedRewards; int128 protocolFees; uint256[] validatorsToApprove; - uint256[] exitedValidators; - uint32[] exitedValidatorsExitTimestamps; uint256[] withdrawalRequestsToInvalidate; uint32 lastFinalizedWithdrawalRequestId; uint128 finalizedWithdrawalAmount; diff --git a/test/EtherFiAdminUpgrade.t.sol b/test/EtherFiAdminUpgrade.t.sol deleted file mode 100644 index 44697a232..000000000 --- a/test/EtherFiAdminUpgrade.t.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import "./TestSetup.sol"; -import "forge-std/console.sol"; -import "../src/EtherFiAdmin.sol"; - -contract EtherFiAdminUpgradeTest is TestSetup { - bytes[] pubKeys; - bytes[] signatures; - IEtherFiOracle.OracleReport report; - uint16 batchSize; - uint16 alternativeBatchSize; - bytes32 reportHash; - uint256[] batchValidatorsToApprove; - uint32[] emptyTimestamps; - bytes32 approvalHash; - uint256 accruedRewards; - uint256 protocolFees; - - function setUp() public { - pubKeys = _getImperialPubkey(); - signatures = _getImperialSignature(); - report = _generateImperialReport(); - batchSize = 10; - alternativeBatchSize = 5; - accruedRewards = 12865299762487754752; - protocolFees = 1438401262268165688; - setUpTests(); - initializeRealisticForkWithBlock(MAINNET_FORK, 21796450); - (uint32 slotFrom, uint32 slotTo, uint32 blockFrom) = etherFiOracleInstance.blockStampForNextReport(); - - // upgrade the contact - upgradeContract(); - vm.startPrank(address(0x1a9AC2a6fC85A7234f9E21697C75D06B2b350864)); - etherFiOracleInstance.submitReport(report); - vm.startPrank(address(0x6d850af8e7AB3361CfF28b31C701647414b9C92b)); - etherFiOracleInstance.submitReport(report); - - reportHash = etherFiOracleInstance.generateReportHash(report); - batchValidatorsToApprove = _getValidatorToApprove(80920, batchSize); //52835 based on transaction - emptyTimestamps = new uint32[](0); - approvalHash = keccak256(abi.encode(reportHash, batchValidatorsToApprove, emptyTimestamps)); - } - - function upgradeContract() public { - - vm.startPrank(roleRegistryInstance.owner()); - - roleRegistryInstance.grantRole(liquidityPoolInstance.LIQUIDITY_POOL_ADMIN_ROLE(), address(etherFiAdminInstance)); - roleRegistryInstance.grantRole(etherFiAdminInstance.ETHERFI_ORACLE_EXECUTOR_ADMIN_ROLE(), committeeMember); - roleRegistryInstance.grantRole(etherFiAdminInstance.ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE(), committeeMember); - vm.startPrank(committeeMember); - etherFiAdminInstance.setValidatorTaskBatchSize(batchSize); - vm.stopPrank(); - } - - // example transactions for submitting and executing report (recreate with the same block number) - //0xc30a309d02917ae5edf27e441ca029c54b069336919439d342c2f4b7889c623d etherfioracle - //0x8b9a2df2b0df2c25d9e96dece8c3e473ba1797a9e764b90eea32e9839f6caa10 etheradmin - function _generateImperialReport() - internal - returns (IEtherFiOracle.OracleReport memory) - { - IEtherFiOracle.OracleReport memory report = IEtherFiOracle.OracleReport( - { - consensusVersion: 1, - refSlotFrom: 11009824, - refSlotTo: 11010463, - refBlockFrom: 21795511, - refBlockTo: 21796145, - accruedRewards: 12865299762487754752, - protocolFees: 1438401262268165688, - validatorsToApprove: new uint256[](10), - exitedValidators: new uint256[](0), - exitedValidatorsExitTimestamps: new uint32[](0), - withdrawalRequestsToInvalidate: new uint256[](0), - lastFinalizedWithdrawalRequestId: 57907, - finalizedWithdrawalAmount: 608440514420670619423 - }); - uint256 startId = 80920; - for (uint i = 0; i < 10; i++) { - report.validatorsToApprove[i] = startId + i; - } - return report; - } - - function _getValidatorToApprove(uint256 firstValidatorIndex, uint16 batchSize) internal returns (uint256[] memory) { - uint256[] memory batchValidatorsToApprove = new uint256[](batchSize); - for (uint i = 0; i < batchSize; i++) { - batchValidatorsToApprove[i] = firstValidatorIndex + i; - } - return batchValidatorsToApprove; - } - - function _getImperialSignature() internal returns (bytes[] memory) { - bytes[] memory _signature = new bytes[](10); - _signature[0] = hex"b5da060d1e9da3c9e7b2a9edfae5346caddf0e9fb03fa56ba5648b871b8c8cf23bcaa2a1e8e60992376c6a0cda8189841081fa27b0a0068d2c46de1fe0ab57c31dfcd76f11c31fe19e8b82cb3500aa0161bc879ae3fa2e188b81686aea1c3318"; - _signature[1] = hex"8cdb0f14e7003c0b14147e07a465bc0b9d4acdc3ae6f2b811d509b5f8d0fad15339d464abc2adf7936a287ff242598e80b947c54a5e10f926fd1d5b9865dfb505fd446d8c949fbaebd5651aa81cbf5589a79d9b5f9bc180cddf8e0e8ae4c81e5"; - _signature[2] = hex"a946bf00a561aa8152ec9ea9d04dc2caf1d88e568e0542f305062f9b1e49dbe42f6aec53686cfd00b096d82a698eca8c02f0c0de631868c1a5e24c34fc5b6c0254ee89588e02394ef073327f5d2115ec82b70f0d3baa9c931462cd5dbf864886"; - _signature[3] = hex"b9eedda7ac2c6949d1bb057b8d638c7d7b6179c15b8f9a987de0be65c697ba6d93659d1af41fbfc01aba616fb0af1fc7178d0ccd548ac42c7b92b26378cb698982797817eafe724845eda0886ce22bc94d02a248037cb26808eabec8ec1e9813"; - _signature[4] = hex"8c1745ecfa4d078361064b8a57801d20741ea39003cc761961c0464c8761fa8fbaab42abe613ee94c997997203f00a140e8fd28eb8da5fb0916cc6fa7da42005dfba2236c9aff471ab6373b0c96d18c4e384c539dc94aef60a7fc1b6bfb0bd16"; - _signature[5] = hex"ad907827693b0f144eae43964907c7da750043c9c4b255db0aa531520868d0fd02cce09850324ef55556250221998711045af33b6cb2d6a7fc3b7ed7a9618fbc5e8f5cc495d4859bf2e5701d2eda4571b2319389a24bbc4a75671b85aff589fb"; - _signature[6] = hex"88122a7a43402d719abd82bc8facf6dc7f1683d109b87546f7f61d8ef8e8777637d73110bd9879bf29b6b482c816592c1259a2e34aab179cc33baa8722a6b5c16d5a27b7bf151c01219bda0c6b702b2d59ee344129085891862a669ef5d84726"; - _signature[7] = hex"ac8d1e9691b4242b5e9b6029e732e86dff7acef34ba66dd6638956852f80b27b43c1cd081ac34b8cb337142750a5ba84109e91c514c5174d7b597229b076ab7d3960b9d2fe7aa7a7e567f01b398ecd67b7c646bcca1a7565b5a005f7b50d6bbd"; - _signature[8] = hex"b8c7fc64ce0fbd4b8ea6a089d0113a51fb477e48fda434e022e2f16f4474dfdbc5add06772fd0b50d537127aacea3de50510da621f9856c31c4f55ed3df91102bdf218404f12d4f5cfefb7f14a01a3eeb3f77f658b6aa6a3bbafdab0482bb362"; - _signature[9] = hex"88a22b55edd9be44cd27b25457751cd42241322ce24d63f55cc1d57d59841d2030944f5ebf762b177dc234c6768d6ad3108342ec02466f877649c5c94579598107cacfdbe0164311af06ea9adfcc9c48da9f3d680fbc75f2d278d6d6551bccbb"; - return _signature; - } - - function _getImperialPubkey() internal returns (bytes[] memory) { - bytes[] memory _pubKey = new bytes[](10); - _pubKey[0] = hex"acccb5d1ee2c39c67184c7a5b830fcbd157792c1bc9225ad007100cb83005723807f6ff53c70ae1bfd750167f29df2a7"; - _pubKey[1] = hex"94afd93f86ae1c4c3209a7d4917a2f9d3c50211e85499c5bc20340c6655bc2dfa16673e2afbd79eb98c64f4f690b8c73"; - _pubKey[2] = hex"8e3f9f0d20c570bc2175f7e00a1593ff5a344916eb96af3307cc8899ab592f4e2d96eafdd8083f426440bd687f9d591f"; - _pubKey[3] = hex"b36f0e692767d3d899125c8ff8507e08a06944d61121ec111a1ccd06fd15f7f6fde1a96a51924110946ead1528aaa32f"; - _pubKey[4] = hex"aa18b9aedc1600cfa86f98eeaf3e1472959af1e54dbcad9b26d1ef2496364e53d27cdc2c5c869df541f357f42141538d"; - _pubKey[5] = hex"817e3bf994a05442d7f4fb1ab1177cb2838f375a9492105e416a40a08f3296d75a9d35f414fb8a84097ace74a2a555c0"; - _pubKey[6] = hex"8a8c717dcc4e8bcf6c2ca5da7e86784f8ba1311bb8a2bd4a88a8fb456f7d0542ae1dbfb4f6b247c668273fedff8c6875"; - _pubKey[7] = hex"8a07585715b3917059b50228a5e81b62e9b0b0f2ea53d71afaf5dd2b60d3d9cdd0a404071dfd50d5427877464d377312"; - _pubKey[8] = hex"ae91a0fd59d85db3b1cd8ffdff8b0692fe1cd20bbc1ac09ee863c52d8cccb6c140f681c7b1659a08a818ac9e01c49194"; - _pubKey[9] = hex"a482419c3b9f5102e3281d96ce60b8699ee946c19348ed7202a6d293e496619c08b3812384029759d8ed4228cbb91bfc"; - return _pubKey; - } - - function test_executeTask() public { - skip(1800); - (bool preExecuteCompleted, bool preExecuteExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(approvalHash); - assertFalse(preExecuteCompleted); - assertFalse(preExecuteExists); - vm.startPrank(committeeMember); - etherFiAdminInstance.executeTasks(report); - } - - function test_changingBatchSize() public { - vm.startPrank(chad); - vm.expectRevert(); - etherFiAdminInstance.setValidatorTaskBatchSize(alternativeBatchSize); - vm.startPrank(committeeMember); - etherFiAdminInstance.setValidatorTaskBatchSize(alternativeBatchSize); - vm.stopPrank(); - } - - function test_invalidateReport() public { - test_executeTask(); - etherFiAdminInstance.invalidateValidatorManagementTask(reportHash, batchValidatorsToApprove, emptyTimestamps); - (bool postCompleted, bool postExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(approvalHash); - assertFalse(postCompleted); - assertFalse(postExists); - vm.expectRevert(); - etherFiAdminInstance.executeValidatorManagementTask(reportHash, batchValidatorsToApprove, emptyTimestamps, pubKeys, signatures); - } - - function test_validatorApprovalTasks() public { - test_executeTask(); - (bool preCompleted, bool preExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(approvalHash); - assertTrue(preExists); - assertFalse(preCompleted); - vm.startPrank(committeeMember); - etherFiAdminInstance.executeValidatorManagementTask(reportHash, batchValidatorsToApprove, emptyTimestamps, pubKeys, signatures); - (bool postCompleted, bool postExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(approvalHash); - assertTrue(postCompleted); - assertTrue(postExists); - vm.stopPrank(); - } - - function test_anotherBatchSize() public { - bytes[] memory newPubKeys = new bytes[](alternativeBatchSize); - bytes[] memory newSignatures = new bytes[](alternativeBatchSize); - uint32[] memory emptyTimestamps = new uint32[](0); - uint256[] memory alternativeBatchValidatorsToApprove = _getValidatorToApprove(80920, alternativeBatchSize); - bytes32 alternativeApprovalHash = keccak256(abi.encode(reportHash, alternativeBatchValidatorsToApprove, emptyTimestamps)); - - for (uint i = 0; i < alternativeBatchSize; i++) { - newPubKeys[i] = pubKeys[i]; - newSignatures[i] = signatures[i]; - } - test_changingBatchSize(); // - test_executeTask(); - (bool preCompleted, bool preExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(alternativeApprovalHash); - etherFiAdminInstance.executeValidatorManagementTask(reportHash, alternativeBatchValidatorsToApprove, emptyTimestamps, newPubKeys, newSignatures); - (bool postCompleted, bool postExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(alternativeApprovalHash); - assertTrue(postCompleted); - assertTrue(postExists); - vm.stopPrank(); - } - - function test_rebase_admin() public { - skip(3600); - uint256 preTotalPooledEth = liquidityPoolInstance.getTotalPooledEther(); - vm.startPrank(committeeMember); - etherFiAdminInstance.executeTasks(report); - (bool preExecuteCompleted, bool preExecuteExists, ) = etherFiAdminInstance.validatorManagementTaskStatus(approvalHash); - uint256 postTotalPooledEth = liquidityPoolInstance.getTotalPooledEther(); - uint256 rebaseAmount = preTotalPooledEth + accruedRewards + 6 ether / 100; - assert(postTotalPooledEth == (preTotalPooledEth + accruedRewards + protocolFees + 6 ether / 100)); - } -} diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 51b2c79a5..e2270c3bb 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -470,10 +470,12 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen auctionManagerProxy = new UUPSProxy(address(auctionImplementation), ""); auctionInstance = AuctionManager(address(auctionManagerProxy)); auctionInstance.initialize(address(nodeOperatorManagerInstance)); + console.log("auctionInstance", address(auctionInstance)); auctionInstance.updateAdmin(alice, true); - //stakingManagerImplementation = new StakingManager(); + stakingManagerImplementation = new StakingManager(address(liquidityPoolInstance), address(managerInstance), address(depositContractEth2), address(auctionInstance), address(node), address(roleRegistryInstance)); stakingManagerProxy = new UUPSProxy(address(stakingManagerImplementation), ""); + console.log("stakingManagerProxy", address(stakingManagerProxy)); stakingManagerInstance = StakingManager(address(stakingManagerProxy)); //stakingManagerInstance.initialize(address(auctionInstance), address(mockDepositContractEth2)); //stakingManagerInstance.updateAdmin(alice, true); @@ -850,15 +852,15 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen uint256[] memory exitedValidators = new uint256[](0); uint32[] memory exitTimestamps = new uint32[](0); uint256[] memory withdrawalRequestsToInvalidate = new uint256[](0); - reportAtPeriod2A = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtPeriod2B = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtPeriod2C = IEtherFiOracle.OracleReport(2, 0, 1024 - 1, 0, 1024 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtPeriod3 = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 2048 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtPeriod3A = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 3 * 1024 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtPeriod3B = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 1, 2 * 1024 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtPeriod4 = IEtherFiOracle.OracleReport(1, 2 * 1024, 1024 * 3 - 1, 2 * 1024, 3 * 1024 - 1, 0, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtSlot3071 = IEtherFiOracle.OracleReport(1, 2048, 3072 - 1, 2048, 3072 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); - reportAtSlot4287 = IEtherFiOracle.OracleReport(1, 3264, 4288 - 1, 3264, 4288 - 1, 1, 0, validatorsToApprove, exitedValidators, exitTimestamps, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod2A = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod2B = IEtherFiOracle.OracleReport(1, 0, 1024 - 1, 0, 1024 - 1, 1, 0,validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod2C = IEtherFiOracle.OracleReport(2, 0, 1024 - 1, 0, 1024 - 1, 1, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod3 = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 2048 - 1, 1, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod3A = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 0, 3 * 1024 - 1, 1, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod3B = IEtherFiOracle.OracleReport(1, 0, 2048 - 1, 1, 2 * 1024 - 1, 1, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtPeriod4 = IEtherFiOracle.OracleReport(1, 2 * 1024, 1024 * 3 - 1, 2 * 1024, 3 * 1024 - 1, 0, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtSlot3071 = IEtherFiOracle.OracleReport(1, 2048, 3072 - 1, 2048, 3072 - 1, 1, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); + reportAtSlot4287 = IEtherFiOracle.OracleReport(1, 3264, 4288 - 1, 3264, 4288 - 1, 1, 0, validatorsToApprove, withdrawalRequestsToInvalidate, 1, 0); } function _merkleSetup() internal { @@ -1089,7 +1091,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen uint256[] memory emptyVals = new uint256[](0); uint32[] memory emptyVals32 = new uint32[](0); uint32 consensusVersion = etherFiOracleInstance.consensusVersion(); - report = IEtherFiOracle.OracleReport(consensusVersion, 0, 0, 0, 0, 0, 0, emptyVals, emptyVals, emptyVals32, emptyVals, 0, 0); + report = IEtherFiOracle.OracleReport(consensusVersion, 0, 0, 0, 0, 0, 0, emptyVals, emptyVals, 0, 0); } function calculatePermitDigest(address _owner, address spender, uint256 value, uint256 nonce, uint256 deadline, bytes32 domainSeparator) public pure returns (bytes32) { From e0fe153fac3661172e04b42f64edd8f319f73260 Mon Sep 17 00:00:00 2001 From: Vaibhav Valecha Date: Mon, 16 Jun 2025 19:36:52 -0400 Subject: [PATCH 38/47] rename to validator approval instead of validatorManagement --- src/EtherFiAdmin.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EtherFiAdmin.sol b/src/EtherFiAdmin.sol index 256331c04..2e9883a04 100644 --- a/src/EtherFiAdmin.sol +++ b/src/EtherFiAdmin.sol @@ -198,7 +198,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable { emit AdminOperationsExecuted(msg.sender, reportHash); } - function executeValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, bytes[] calldata _pubKeys, bytes[] calldata _signatures) external { + function executeValidatorApprovalTask(bytes32 _reportHash, uint256[] calldata _validators, bytes[] calldata _pubKeys, bytes[] calldata _signatures) external { if (!roleRegistry.hasRole(ETHERFI_ORACLE_EXECUTOR_TASK_MANAGER_ROLE, msg.sender)) revert IncorrectRole(); require(etherFiOracle.isConsensusReached(_reportHash), "EtherFiAdmin: report didn't reach consensus"); From 6d5a033b7568256a4f87adbb314d0ead5ee29273 Mon Sep 17 00:00:00 2001 From: Vaibhav Valecha Date: Wed, 18 Jun 2025 14:19:04 -0400 Subject: [PATCH 39/47] add test to make sure the oracle changes work --- test/EtherFiOracle.t.sol | 18 ++++++++++++++++++ test/TestSetup.sol | 1 + 2 files changed, 19 insertions(+) diff --git a/test/EtherFiOracle.t.sol b/test/EtherFiOracle.t.sol index 468851897..d9dc160f9 100644 --- a/test/EtherFiOracle.t.sol +++ b/test/EtherFiOracle.t.sol @@ -204,6 +204,24 @@ contract EtherFiOracleTest is TestSetup { _executeAdminTasks(reportAtPeriod4); } + function test_approving_validators() public { + // Now it's period 2! + _moveClock(1024 + 2 * slotsPerEpoch); + reportAtPeriod2A.validatorsToApprove = new uint256[](1); + bytes32 reportHash = etherFiOracleInstance.generateReportHash(reportAtPeriod2A); + bytes[] memory emptyPubKeys = new bytes[](1); + bytes[] memory emptySignatures = new bytes[](1); + + _executeAdminTasks(reportAtPeriod2A); + //execute validator task + vm.prank(alice); + etherFiAdminInstance.executeValidatorApprovalTask(reportHash, reportAtPeriod2A.validatorsToApprove, emptyPubKeys, emptySignatures); + + (bool completed, bool exists) = etherFiAdminInstance.validatorApprovalTaskStatus(reportHash); + assertEq(completed, true); + assertEq(exists, true); + } + function test_report_submission_before_processing_last_published_one_fails() public { vm.prank(owner); etherFiOracleInstance.setQuorumSize(1); diff --git a/test/TestSetup.sol b/test/TestSetup.sol index e2270c3bb..4abd5ac36 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -720,6 +720,7 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen vm.startPrank(alice); etherFiAdminInstance.setValidatorTaskBatchSize(100); + liquidityPoolInstance.setValidatorSizeWei(32 ether); vm.stopPrank(); vm.startPrank(owner); // etherFiAdminInstance.updateAdmin(alice, true); From 539792d73d68359c465773a9eef721a07da68d45 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 18 Jun 2025 13:52:35 -0600 Subject: [PATCH 40/47] re-add removed event for compatibility --- src/StakingManager.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/StakingManager.sol b/src/StakingManager.sol index 565640e88..caed82903 100644 --- a/src/StakingManager.sol +++ b/src/StakingManager.sol @@ -115,6 +115,9 @@ contract StakingManager is bytes32 pubkeyHash = calculateValidatorPubkeyHash(depositData[i].publicKey); emit validatorCreated(pubkeyHash, etherFiNode, depositData[i].publicKey); emit linkLegacyValidatorId(pubkeyHash, bidIds[i]); // can remove this once we fully transition to pubkeys + + // legacy event for compatibility with existing tooling + emit ValidatorRegistered(auctionManager.getBidOwner(bidIds[i]), address(liquidityPool), address(liquidityPool), bidIds[i], depositData[i].publicKey, depositData[i].ipfsHashForEncryptedValidatorKey); } } From f45a63545fd68dba5e2365a9cbe3f117c600ba81 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 27 Jun 2025 16:42:55 -0600 Subject: [PATCH 41/47] wip better validator test setup --- test/prelude.t.sol | 151 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/test/prelude.t.sol b/test/prelude.t.sol index c1bb6be0b..2b9b37c13 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -29,7 +29,6 @@ contract PreludeTest is Test, ArrayTestHelper { NodeOperatorManager nodeOperatorManager = NodeOperatorManager(0xd5edf7730ABAd812247F6F54D7bd31a52554e35E); address admin = vm.addr(0x9876543210); - address forwarder = vm.addr(0x1234567890); address stakingDepositContract = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); address eigenPodManager = address(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); @@ -88,7 +87,7 @@ contract PreludeTest is Test, ArrayTestHelper { roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_EIGENLAYER_ADMIN_ROLE(), eigenlayerAdmin); roleRegistry.grantRole(etherFiNodeImpl.ETHERFI_NODE_CALL_FORWARDER_ROLE(), address(etherFiNodesManager)); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_ADMIN_ROLE(), admin); - roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), forwarder); + roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE(), callForwarder); roleRegistry.grantRole(etherFiNodesManager.ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE(), eigenlayerAdmin); roleRegistry.grantRole(stakingManager.STAKING_MANAGER_NODE_CREATOR_ROLE(), admin); vm.stopPrank(); @@ -444,4 +443,152 @@ contract PreludeTest is Test, ArrayTestHelper { vm.prank(roleRegistry.owner()); stakingManager.upgradeEtherFiNode(address(nodeImpl)); } + + function test_getEigenPod() public { + + // node that has existing eigenpod + IEtherFiNode node = IEtherFiNode(0xbD0BFF833DE891aDcFF6Ee5502B23f516bECBf6F); + address eigenPod = address(node.getEigenPod()); + assertEq(eigenPod, address(0x5E77861146AFACBa593dB976AD86BaB79675BC6F)); + + // new node that doesn't have existing eigenpod + // should return zero address + vm.prank(admin); + IEtherFiNode newNode = IEtherFiNode(stakingManager.instantiateEtherFiNode(/*createEigenPod=*/false)); + assertEq(address(newNode.getEigenPod()), address(0)); + } + + function test_createEigenPod() public { + + // create pod without eigenpod + vm.prank(admin); + IEtherFiNode newNode = IEtherFiNode(stakingManager.instantiateEtherFiNode(/*createEigenPod=*/false)); + assertEq(address(newNode.getEigenPod()), address(0)); + + // admin creates one and it should be connected + vm.prank(eigenlayerAdmin); + address newPod = newNode.createEigenPod(); + assert(newPod != address(0)); + assertEq(newPod, address(newNode.getEigenPod())); + } + + function test_setProofSubmitter() public { + + address newSubmitter = vm.addr(0xabc123); + IEtherFiNode node = IEtherFiNode(0xbD0BFF833DE891aDcFF6Ee5502B23f516bECBf6F); + + vm.prank(eigenlayerAdmin); + node.setProofSubmitter(newSubmitter); + + IEigenPod pod = node.getEigenPod(); + assertEq(pod.proofSubmitter(), newSubmitter); + + } + + struct TestValidatorParams { + address nodeOperator; // if none specified a new operator will be created + address etherFiNode; // if none specified a new node will be deployed + uint256 bidId; // if none specified a new bid will be placed + uint256 validatorSize; // default to 32 eth + bool withdrawable; // manually give the eigenpod "validatorSize" worth of withdrawable beacon shares + } + + struct TestValidator { + address etherFiNode; + address eigenPod; + uint256 legacyId; + address nodeOperator; + uint256 validatorSize; + uint256 nodeId; + } + + function helper_createWithdrawableValidator(TestValidatorParams memory params) public returns (TestValidator memory) { + + // configure a new operator if none provided + if (params.nodeOperator == address(0)) { + params.nodeOperator = vm.addr(0x123456); + + // register if not already + if (!nodeOperatorManager.registered(params.nodeOperator)) { + vm.prank(params.nodeOperator); + nodeOperatorManager.registerNodeOperator("test_ipfs_hash", 1000); + } + } + // create a new bid if none provided + if (params.bidId == 0) { + vm.deal(params.nodeOperator, 1 ether); + vm.prank(params.nodeOperator); + params.bidId = auctionManager.createBid{value: 0.1 ether}(1, 0.1 ether)[0]; + } + // create a new node if none provided + if (params.etherFiNode == address(0)) { + vm.prank(admin); + params.etherFiNode = stakingManager.instantiateEtherFiNode(/*createEigenPod=*/ true); + } + // default validator size if not provided + if (params.validatorSize == 0) { + params.validatorSize = 32 ether; + } + + bytes memory pubkey = vm.randomBytes(48); + bytes memory signature = vm.randomBytes(96); + + //bytes memory pubkey = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2b"; + //bytes memory signature = hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3de"; + + // initial deposit + address eigenPod = address(IEtherFiNode(params.etherFiNode).getEigenPod()); + bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( + pubkey, + signature, + etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), + 1 ether + ); + IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: initialDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + vm.deal(address(liquidityPool), 100 ether); + vm.prank(address(liquidityPool)); + stakingManager.createBeaconValidators{value: 1 ether}(toArray(initialDepositData), toArray_u256(params.bidId), params.etherFiNode); + + uint256 confirmAmount = params.validatorSize - 1 ether; + + // remaining deposit + bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot( + pubkey, + signature, + etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), + confirmAmount + ); + IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: confirmDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + vm.prank(address(liquidityPool)); + stakingManager.confirmAndFundBeaconValidators{value: confirmAmount}(toArray(confirmDepositData), params.validatorSize); + + if (params.withdrawable) { + // Poke some withdrawable funds into the restakedExecutionLayerGwei storage slot of the eigenpod. + // This is much easier than trying to do the full proof based workflow which relies on beacon state. + address eigenpod = etherFiNodesManager.getEigenPod(uint256(params.bidId)); + vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(params.validatorSize / 1 gwei))); + } + + + } + + function test_helper() public { + //TestValidatorParams cfg = TestValidatorParams({ + + //}); + helper_createWithdrawableValidator(TestValidatorParams(address(0),address(0),0,0,false)); + + } + } + From 78c8ac980e8442cdac07e71bf2307892a1f37123 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 27 Jun 2025 16:44:37 -0600 Subject: [PATCH 42/47] debugging tests --- test/EtherFiOracle.t.sol | 6 +- test/LiquidityPool.t.sol | 288 --------------------------------------- test/TestSetup.sol | 11 +- 3 files changed, 15 insertions(+), 290 deletions(-) diff --git a/test/EtherFiOracle.t.sol b/test/EtherFiOracle.t.sol index d9dc160f9..f50adda65 100644 --- a/test/EtherFiOracle.t.sol +++ b/test/EtherFiOracle.t.sol @@ -466,6 +466,10 @@ contract EtherFiOracleTest is TestSetup { _executeAdminTasks(report, "EtherFiAdmin: TVL changed too much"); } + function test_dave() public { + launch_validator(); + } + function test_huge_negative_rebaes() public { // TVL after `launch_validator` is 60 ETH // EtherFIAdmin limits the APR per rebase as 100 % == 10000 bps @@ -691,4 +695,4 @@ contract EtherFiOracleTest is TestSetup { report.accruedRewards = 90 ether; report.protocolFees = 10 ether; } -} \ No newline at end of file +} diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index 315304895..969ecf62a 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -260,63 +260,6 @@ contract LiquidityPoolTest is TestSetup { assertEq(eETHInstance.balanceOf(bob), 3 ether); } - /* - function test_batchCancelDepositAsBnftHolder1() public { - vm.deal(owner, 100 ether); - - IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - _executeAdminTasks(report); - - setUpBnftHolders(); - - vm.warp(976348625856); - - hoax(alice); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.2 ether}(2, 0.1 ether); - assertEq(bidIds.length, 2); - - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 0); - - startHoax(bob); - liquidityPoolInstance.deposit{value: 64 ether}(); - vm.stopPrank(); - - assertEq(address(liquidityPoolInstance).balance, 64 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 64 ether); - - uint256 aliceBalance = address(alice).balance; - vm.prank(alice); - uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, 2); - - assertEq(newValidators.length, 2); - assertEq(address(alice).balance, aliceBalance); - assertEq(address(liquidityPoolInstance).balance, 64 ether); - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 2); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 64 ether); - - // SD-1 "Anyone can call StakingManager.batchCancelDepositAsBnftHolder to cancel a deposit" - vm.prank(bob); - vm.expectRevert("INCORRECT_CALLER"); - assertEq(address(alice).balance, aliceBalance); - stakingManagerInstance.batchCancelDepositAsBnftHolder(newValidators, alice); - - assertEq(address(alice).balance, aliceBalance); - vm.prank(alice); - liquidityPoolInstance.batchCancelDeposit(newValidators); - assertEq(address(alice).balance, aliceBalance); - - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 0); - assertEq(liquidityPoolInstance.totalValueInLp(), 64 ether); - assertEq(address(alice).balance, aliceBalance); - assertEq(address(stakingManagerInstance).balance, 0 ether); - assertEq(address(liquidityPoolInstance).balance, 64 ether); - } - */ function test_sendExitRequestFails() public { uint256[] memory newValidators = new uint256[](10); @@ -326,170 +269,6 @@ contract LiquidityPoolTest is TestSetup { } // TODO(dave): update when v3 changes finalized - /* - function test_bnftFlowWithLiquidityPoolAsBnftHolder() public { - setUpBnftHolders(); - - vm.deal(alice, 1000 ether); - vm.startPrank(alice); - - // liquidityPoolInstance.updateBnftMode(true); - - uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether}(1, 0.1 ether); - - liquidityPoolInstance.deposit{value: 32 ether}(); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - uint256[] memory validatorIds = liquidityPoolInstance.batchDeposit(bidIds, 1); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 1); - - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(validatorIds); - - liquidityPoolInstance.batchRegister(zeroRoot, validatorIds, depositDataArray, depositDataRootsForApproval, sig); - - assertEq(liquidityPoolInstance.getTotalPooledEther(), 32 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 31 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 1 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - assertEq(BNFTInstance.ownerOf(validatorIds[0]), address(liquidityPoolInstance)); - assertEq(TNFTInstance.ownerOf(validatorIds[0]), address(liquidityPoolInstance)); - - liquidityPoolInstance.batchApproveRegistration(validatorIds, pubKey, sig); - assertEq(liquidityPoolInstance.totalValueInLp(), 0 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 32 ether); - assertEq(liquidityPoolInstance.numPendingDeposits(), 0); - - address etherfiNode = managerInstance.etherFiNodeFromId(validatorIds[0]); - vm.deal(address(etherfiNode), 1 ether); - managerInstance.batchPartialWithdraw(validatorIds); - - assertEq(liquidityPoolInstance.totalValueInLp(), 1 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 31 ether); - - vm.startPrank(address(membershipManagerInstance)); - liquidityPoolInstance.rebase(1 ether); - // The liquidity pool receives the rewards as B-NFT holder and T-NFT holder - assertEq((address(liquidityPoolInstance).balance), 1 ether); - assertEq(liquidityPoolInstance.totalValueInLp(), 1 ether); - assertEq(liquidityPoolInstance.totalValueOutOfLp(), 32 ether); - } - - function test_batchPartialWithdrawOptimized() internal { - uint256[] memory validatorIds = launch_validator(20, 0, false); - - uint256 totalTnftRewards = 0; - for (uint256 i = 0; i < validatorIds.length; i++) { - address etherfiNode = managerInstance.etherFiNodeFromId( - validatorIds[i] - ); - _transferTo(etherfiNode, 1 ether); - totalTnftRewards += (1 ether * 90 * 29) / (100 * 32); - } - uint256 lastBalance = address(liquidityPoolInstance).balance; - // managerInstance.batchPartialWithdrawOptimized(validatorIds); - assertEq(address(liquidityPoolInstance).balance, lastBalance + totalTnftRewards); - } - */ - - /* - function test_ProcessNodeExit() public { - vm.deal(owner, 100 ether); - - IEtherFiOracle.OracleReport memory report = _emptyOracleReport(); - _executeAdminTasks(report); - - setUpBnftHolders(); - - vm.prank(alice); - - assertEq(liquidityPoolInstance.getTotalPooledEther(), 0); - - hoax(alice); - uint256[] memory bidIds = auctionInstance.createBid{value: 0.2 ether}(2, 0.1 ether); - - startHoax(bob); - liquidityPoolInstance.deposit{value: 64 ether}(); - - assertEq(liquidityPoolInstance.getTotalPooledEther(), 64 ether); - vm.stopPrank(); - - vm.warp(1681075815 - 35 * 24 * 3600); // Sun March ... - vm.prank(henry); - uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, 2); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 64 ether); - - (IStakingManager.DepositData[] memory depositDataArray, bytes32[] memory depositDataRootsForApproval, bytes[] memory sig, bytes[] memory pubKey) = _prepareForValidatorRegistration(newValidators); - - vm.prank(henry); - liquidityPoolInstance.batchRegister(zeroRoot, newValidators, depositDataArray, depositDataRootsForApproval, sig); - - for (uint256 i = 0; i < newValidators.length; i++) { - address etherFiNode = managerInstance.etherFiNodeFromId( - newValidators[i] - ); - - assertEq(uint8(managerInstance.phase(newValidators[i])), uint8(IEtherFiNode.VALIDATOR_PHASE.WAITING_FOR_APPROVAL)); - } - vm.expectRevert(LiquidityPool.IncorrectRole.selector); - liquidityPoolInstance.batchApproveRegistration(newValidators, pubKey, sig); - - vm.prank(alice); - liquidityPoolInstance.batchApproveRegistration(newValidators, pubKey, sig); - for (uint256 i = 0; i < newValidators.length; i++) { - address etherFiNode = managerInstance.etherFiNodeFromId( - newValidators[i] - ); - - assertEq(uint8(managerInstance.phase(newValidators[i])), uint8(IEtherFiNode.VALIDATOR_PHASE.LIVE)); - } - - assertEq(liquidityPoolInstance.getTotalPooledEther(), 64 ether); - - uint256[] memory slashingPenalties = new uint256[](2); - slashingPenalties[0] = 0.5 ether; - slashingPenalties[1] = 0.5 ether; - - // The penalties are applied to the B-NFT holders which in our case is also the T-NFT holders - vm.prank(address(membershipManagerInstance)); - liquidityPoolInstance.rebase(-1 ether); - - vm.warp(1681075815 - 7 * 24 * 3600); // Sun Apr 02 2023 21:30:15 UTC - vm.expectRevert(LiquidityPool.IncorrectRole.selector); - liquidityPoolInstance.DEPRECATED_sendExitRequests(newValidators); - - vm.prank(alice); - liquidityPoolInstance.DEPRECATED_sendExitRequests(newValidators); - - uint32[] memory exitRequestTimestamps = new uint32[](2); - exitRequestTimestamps[0] = uint32(block.timestamp - 1000); - exitRequestTimestamps[1] = uint32(block.timestamp - 10000); - - vm.warp(1681351200 + 12 * 6); - - address etherfiNode1 = managerInstance.etherFiNodeFromId(newValidators[0]); - address etherfiNode2 = managerInstance.etherFiNodeFromId(newValidators[1]); - _transferTo(etherfiNode1, 32 ether - slashingPenalties[0]); - _transferTo(etherfiNode2, 32 ether - slashingPenalties[1]); - - // Process the node exit via nodeManager - vm.prank(alice); - managerInstance.processNodeExit(newValidators, exitRequestTimestamps); - assertEq(liquidityPoolInstance.getTotalPooledEther(), 63 ether); - assertTrue(managerInstance.phase(newValidators[0]) == IEtherFiNode.VALIDATOR_PHASE.EXITED); - assertTrue(managerInstance.phase(newValidators[1]) == IEtherFiNode.VALIDATOR_PHASE.EXITED); - - // Delist the node from the liquidity pool - vm.prank(henry); - managerInstance.batchFullWithdraw(newValidators); - - assertEq(liquidityPoolInstance.getTotalPooledEther(), 63 ether); - assertEq(address(liquidityPoolInstance).balance, 63 ether); - } - */ function test_fallback() public { assertEq(liquidityPoolInstance.getTotalPooledEther(), 0 ether); @@ -513,73 +292,6 @@ contract LiquidityPoolTest is TestSetup { assertEq(liquidityPoolInstance.getTotalPooledEther(), 103 ether); } - // TODO(dave): update after v3 changes - /* - function test_rebase_withdraw_flow() public { - - uint256[] memory validatorIds = launch_validator(); - - uint256[] memory tvls = new uint256[](4); - - for (uint256 i = 0; i < validatorIds.length; i++) { - // Beacon Balance < 32 ether means that the validator got slashed - uint256 beaconBalance = 16 ether * (i + 1) + 1 ether; - (uint256 toNodeOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) - = managerInstance.calculateTVL(validatorIds[i], beaconBalance); - tvls[0] += toNodeOperator; - tvls[1] += toTnft; - tvls[2] += toBnft; - tvls[3] += toTreasury; - } - uint256 eEthTVL = tvls[1] + tvls[2]; - - // Reflect the loss in TVL by rebasing - int128 lossInTVL = int128(uint128(eEthTVL)) - int128(uint128(64 ether)); - vm.prank(address(membershipManagerInstance)); - liquidityPoolInstance.rebase(lossInTVL); - - assertEq(address(liquidityPoolInstance).balance, 0 ether); - assertEq(eETHInstance.totalSupply(), eEthTVL); - assertEq(eETHInstance.balanceOf(bob), eEthTVL); - - // After a long period of time (after the auction fee vesting period completes) - skip(6 * 7 * 4 days); - - uint32[] memory exitRequestTimestamps = new uint32[](2); - exitRequestTimestamps[0] = uint32(block.timestamp); - exitRequestTimestamps[1] = uint32(block.timestamp); - - address etherfiNode1 = managerInstance.etherFiNodeFromId(validatorIds[0]); - address etherfiNode2 = managerInstance.etherFiNodeFromId(validatorIds[1]); - - _transferTo(etherfiNode1, 17 ether); - _transferTo(etherfiNode2, 33 ether); - - // Process the node exit via nodeManager - vm.prank(alice); - managerInstance.processNodeExit(validatorIds, exitRequestTimestamps); - managerInstance.batchFullWithdraw(validatorIds); - - assertEq(address(liquidityPoolInstance).balance, eEthTVL); - assertEq(eETHInstance.totalSupply(), eEthTVL); - assertEq(eETHInstance.balanceOf(bob), eEthTVL); - - vm.startPrank(bob); - eETHInstance.approve(address(liquidityPoolInstance), eEthTVL); - uint256 bobRequestId = liquidityPoolInstance.requestWithdraw(bob, eEthTVL); - vm.stopPrank(); - - _finalizeWithdrawalRequest(bobRequestId); - - vm.prank(bob); - withdrawRequestNFTInstance.claimWithdraw(bobRequestId); - - assertEq(address(liquidityPoolInstance).balance, 0); - assertEq(eETHInstance.totalSupply(), 0); - assertEq(eETHInstance.balanceOf(bob), 0); - } - */ - function test_RegisterAsBnftHolder() public { //Move past one week vm.warp(804650); diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 4abd5ac36..58007e6f6 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -1288,10 +1288,14 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } vm.stopPrank(); + console2.log("A"); + vm.startPrank(_nodeOperator); uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * _numValidators}(_numValidators, 0.1 ether); vm.stopPrank(); + console2.log("B"); + startHoax(bob); if (_isLpBnftHolder) { liquidityPoolInstance.deposit{value: 32 ether * _numValidators}(); @@ -1300,10 +1304,13 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } vm.stopPrank(); + console2.log("C"); // TODO(dave): rework test setup //vm.prank(_bnftStaker); //uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, _numValidators, _validatorIdToCoUseWithdrawalSafe); - uint256[] memory newValidators = new uint256[](_numValidators); + //uint256[] memory newValidators = new uint256[](_numValidators); + uint256[] memory newValidators = bidIds; + IStakingManager.DepositData[] memory depositDataArray = new IStakingManager.DepositData[](_numValidators); @@ -1342,6 +1349,8 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen pubKey[i] = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; } + console2.log("D"); + vm.startPrank(_bnftStaker); bytes32 depositRoot = zeroRoot; // TODO(Dave): fix for new deposit flow From 9d2e6fed756b90a763c8cea8cab40aed5687ec60 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 27 Jun 2025 17:44:11 -0600 Subject: [PATCH 43/47] update forge-std version --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index bb4ceea94..77041d2ce 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 77041d2ce690e692d6e03cc812b57d1ddaa4d505 From 7106834d92c488b6ed934d65419291ef49ecbb1d Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 27 Jun 2025 17:44:22 -0600 Subject: [PATCH 44/47] finish validator helper --- test/prelude.t.sol | 77 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 2b9b37c13..9d023f64f 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -40,6 +40,8 @@ contract PreludeTest is Test, ArrayTestHelper { address callForwarder = vm.addr(0xCDCDCD); address user = vm.addr(0xEFEFEF); + TestValidatorParams defaultTestValidatorParams; + function setUp() public { vm.selectFork(vm.createFork(vm.envString("MAINNET_RPC_URL"))); @@ -92,6 +94,14 @@ contract PreludeTest is Test, ArrayTestHelper { roleRegistry.grantRole(stakingManager.STAKING_MANAGER_NODE_CREATOR_ROLE(), admin); vm.stopPrank(); + defaultTestValidatorParams = TestValidatorParams({ + nodeOperator: address(0), // attach to a random node operator + etherFiNode: address(0), // create a new etherfiNode + bidId: 0, // create and claim a new bid + withdrawable: true, // simulate validator being ready to withdraw from pod + validatorSize: 32 ether + }); + } function test_forwardingWhitelist() public { @@ -497,12 +507,12 @@ contract PreludeTest is Test, ArrayTestHelper { address etherFiNode; address eigenPod; uint256 legacyId; + bytes32 pubkeyHash; address nodeOperator; uint256 validatorSize; - uint256 nodeId; } - function helper_createWithdrawableValidator(TestValidatorParams memory params) public returns (TestValidator memory) { + function helper_createValidator(TestValidatorParams memory params) public returns (TestValidator memory) { // configure a new operator if none provided if (params.nodeOperator == address(0)) { @@ -533,9 +543,6 @@ contract PreludeTest is Test, ArrayTestHelper { bytes memory pubkey = vm.randomBytes(48); bytes memory signature = vm.randomBytes(96); - //bytes memory pubkey = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2b"; - //bytes memory signature = hex"877bee8d83cac8bf46c89ce50215da0b5e370d282bb6c8599aabdbc780c33833687df5e1f5b5c2de8a6cd20b6572c8b0130b1744310a998e1079e3286ff03e18e4f94de8cdebecf3aaac3277b742adb8b0eea074e619c20d13a1dda6cba6e3de"; - // initial deposit address eigenPod = address(IEtherFiNode(params.etherFiNode).getEigenPod()); bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( @@ -550,7 +557,7 @@ contract PreludeTest is Test, ArrayTestHelper { depositDataRoot: initialDepositRoot, ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" }); - vm.deal(address(liquidityPool), 100 ether); + vm.deal(address(liquidityPool), 10000 ether); vm.prank(address(liquidityPool)); stakingManager.createBeaconValidators{value: 1 ether}(toArray(initialDepositData), toArray_u256(params.bidId), params.etherFiNode); @@ -577,16 +584,62 @@ contract PreludeTest is Test, ArrayTestHelper { // This is much easier than trying to do the full proof based workflow which relies on beacon state. address eigenpod = etherFiNodesManager.getEigenPod(uint256(params.bidId)); vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(params.validatorSize / 1 gwei))); + + // give the pod enough eth to fulfill that withdrawal + vm.deal(eigenpod, params.validatorSize); } - + TestValidator memory out = TestValidator({ + etherFiNode: params.etherFiNode, + eigenPod: eigenPod, + legacyId: params.bidId, + pubkeyHash: stakingManager.calculateValidatorPubkeyHash(pubkey), + nodeOperator: params.nodeOperator, + validatorSize: params.validatorSize + }); + return out; } - function test_helper() public { - //TestValidatorParams cfg = TestValidatorParams({ - - //}); - helper_createWithdrawableValidator(TestValidatorParams(address(0),address(0),0,0,false)); + function test_validatorHelper() public { + + // deploy 2 validators with default settings + TestValidator memory val = helper_createValidator(defaultTestValidatorParams); + console2.log("------------------------------------"); + console2.log("etherFiNode:", val.etherFiNode); + console2.log("eigenPod:", val.eigenPod); + console2.log("legacyId:", val.legacyId); + console2.log("pubkeyHash:", uint256(val.pubkeyHash)); + console2.log("nodeOperator:", val.nodeOperator); + console2.log("validatorSize:", val.validatorSize); + TestValidator memory val2 = helper_createValidator(defaultTestValidatorParams); + console2.log("------------------------------------"); + console2.log("etherFiNode:", val2.etherFiNode); + console2.log("eigenPod:", val2.eigenPod); + console2.log("legacyId:", val2.legacyId); + console2.log("pubkeyHash:", uint256(val2.pubkeyHash)); + console2.log("nodeOperator:", val2.nodeOperator); + console2.log("validatorSize:", val2.validatorSize); + + // attach to same node as the first one + TestValidatorParams memory params = defaultTestValidatorParams; + params.etherFiNode = val.etherFiNode; + TestValidator memory val3 = helper_createValidator(params); + assertEq(val.etherFiNode, val3.etherFiNode); + assertEq(val.eigenPod, val3.eigenPod); + + // create big validator + params = defaultTestValidatorParams; + params.validatorSize = 2000 ether; + params.withdrawable = true; + TestValidator memory val4 = helper_createValidator(params); + assertEq(2000 ether, val4.validatorSize); + assertGe(IEigenPod(val4.eigenPod).withdrawableRestakedExecutionLayerGwei(), (2000 ether / 1 gwei)); + + // should fail if try to claim already claimed bid + params = defaultTestValidatorParams; + params.bidId = val.legacyId; + vm.expectRevert(); + TestValidator memory val5 = helper_createValidator(params); } From 6d0806c96b67f65532cf507dcd4913bbd80ad8b1 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 27 Jun 2025 23:19:36 -0600 Subject: [PATCH 45/47] linking tests --- test/prelude.t.sol | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 9d023f64f..156a5e0f1 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -640,6 +640,73 @@ contract PreludeTest is Test, ArrayTestHelper { params.bidId = val.legacyId; vm.expectRevert(); TestValidator memory val5 = helper_createValidator(params); + } + + function test_pubkeyHashAndLegacyId() public { + + // create a new validator + TestValidator memory val = helper_createValidator(defaultTestValidatorParams); + + // both the legacyId and the pubkeyHash should be linked to the same node + address a1 = etherFiNodesManager.etherfiNodeAddress(val.legacyId); + address a2 = etherFiNodesManager.etherfiNodeAddress(uint256(val.pubkeyHash)); + assertEq(a1, a2); + assert(a1 != address(0)); + } + + function test_startCheckpoint() public { + + // create a new validator with an eigenpod + TestValidator memory val = helper_createValidator(defaultTestValidatorParams); + + // ensure no checkpoint currently active + assert(IEigenPod(val.eigenPod).currentCheckpointTimestamp() == 0); + + console2.log("eigenpod:", val.eigenPod); + console2.log("balance:", val.eigenPod.balance); + + // need to deal it some additional eth not already accounted for in its + // beacon shares or else it will revert since there is no point in checkpointing + vm.deal(val.eigenPod, 1 ether); + + // initiate a checkpoint + vm.prank(eigenlayerAdmin); + etherFiNodesManager.startCheckpoint(uint256(val.pubkeyHash)); + assert(IEigenPod(val.eigenPod).currentCheckpointTimestamp() != 0); + } + + function test_linkLegacyValidatorIds() public { + + // grab some legacyIds that existed before the upgrade + uint256[] memory legacyIds = new uint256[](3); + legacyIds[0] = 10270; + legacyIds[1] = 10271; + legacyIds[2] = 26606; + + // random pubkeys to attach + bytes[] memory pubkeys = new bytes[](3); + pubkeys[0] = vm.randomBytes(48); + pubkeys[1] = vm.randomBytes(48); + pubkeys[2] = vm.randomBytes(48); + + // should fail if not admin + vm.expectRevert(IEtherFiNodesManager.IncorrectRole.selector); + etherFiNodesManager.linkLegacyValidatorIds(legacyIds, pubkeys); + + vm.prank(admin); + etherFiNodesManager.linkLegacyValidatorIds(legacyIds, pubkeys); + + // should fail if attempt to re-link already linked ids + vm.expectRevert(IEtherFiNodesManager.AlreadyLinked.selector); + vm.prank(admin); + etherFiNodesManager.linkLegacyValidatorIds(legacyIds, pubkeys); + + // should fail if attempt to link unknown node + uint256 badId = 9999999; + bytes memory badPubkey = vm.randomBytes(48); + vm.expectRevert(IEtherFiNodesManager.UnknownNode.selector); + vm.prank(admin); + etherFiNodesManager.linkLegacyValidatorIds(toArray_u256(badId), toArray_bytes(badPubkey)); } From 4bb269c4414ef850020a3d4e604c53279ea85f04 Mon Sep 17 00:00:00 2001 From: dave Date: Sat, 28 Jun 2025 20:44:20 -0600 Subject: [PATCH 46/47] cleanup --- test/TestSetup.sol | 7 ----- test/prelude.t.sol | 69 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 58007e6f6..ae99026c2 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -1288,14 +1288,10 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } vm.stopPrank(); - console2.log("A"); - vm.startPrank(_nodeOperator); uint256[] memory bidIds = auctionInstance.createBid{value: 0.1 ether * _numValidators}(_numValidators, 0.1 ether); vm.stopPrank(); - console2.log("B"); - startHoax(bob); if (_isLpBnftHolder) { liquidityPoolInstance.deposit{value: 32 ether * _numValidators}(); @@ -1304,7 +1300,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen } vm.stopPrank(); - console2.log("C"); // TODO(dave): rework test setup //vm.prank(_bnftStaker); //uint256[] memory newValidators = liquidityPoolInstance.batchDeposit(bidIds, _numValidators, _validatorIdToCoUseWithdrawalSafe); @@ -1349,8 +1344,6 @@ contract TestSetup is Test, ContractCodeChecker, ArrayTestHelper, DepositDataGen pubKey[i] = hex"8f9c0aab19ee7586d3d470f132842396af606947a0589382483308fdffdaf544078c3be24210677a9c471ce70b3b4c2c"; } - console2.log("D"); - vm.startPrank(_bnftStaker); bytes32 depositRoot = zeroRoot; // TODO(Dave): fix for new deposit flow diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 156a5e0f1..66f3f0dea 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -34,6 +34,7 @@ contract PreludeTest is Test, ArrayTestHelper { address delegationManager = address(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); address etherFiNodeBeacon = address(0x3c55986Cfee455E2533F4D29006634EcF9B7c03F); RoleRegistry roleRegistry = RoleRegistry(0x62247D29B4B9BECf4BB73E0c722cf6445cfC7cE9); + IStrategy beaconStrategy = IStrategy(address(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0)); // role users address eigenlayerAdmin = vm.addr(0xABABAB); @@ -512,7 +513,10 @@ contract PreludeTest is Test, ArrayTestHelper { uint256 validatorSize; } - function helper_createValidator(TestValidatorParams memory params) public returns (TestValidator memory) { + function helper_createValidator(TestValidatorParams memory _params) public returns (TestValidator memory) { + + // create a copy or else successive calls of this method can mutate the input unexpectedly + TestValidatorParams memory params = TestValidatorParams(_params.nodeOperator, _params.etherFiNode, _params.bidId, _params.validatorSize, _params.withdrawable); // configure a new operator if none provided if (params.nodeOperator == address(0)) { @@ -585,6 +589,10 @@ contract PreludeTest is Test, ArrayTestHelper { address eigenpod = etherFiNodesManager.getEigenPod(uint256(params.bidId)); vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(params.validatorSize / 1 gwei))); + // grant shares via delegation manager so that withdrawals work + vm.prank(delegationManager); + IEigenPodManager(eigenPodManager).addShares(params.etherFiNode, beaconStrategy, params.validatorSize); + // give the pod enough eth to fulfill that withdrawal vm.deal(eigenpod, params.validatorSize); } @@ -710,5 +718,64 @@ contract PreludeTest is Test, ArrayTestHelper { } + function test_withdrawMultipleLargeValidators() public { + + // create a few large validators of different sizes + TestValidatorParams memory params = defaultTestValidatorParams; + params.validatorSize = 64 ether; + TestValidator memory val1 = helper_createValidator(params); + + // attach all of them to the same etherfi node + params.etherFiNode = val1.etherFiNode; + + params.validatorSize = 128 ether; + TestValidator memory val2 = helper_createValidator(params); + params.validatorSize = 1000 ether; + TestValidator memory val3 = helper_createValidator(params); + params.validatorSize = 2000 ether; + TestValidator memory val4 = helper_createValidator(params); + + uint256 startingLPBalance = address(liquidityPool).balance; + + // should be able to withdraw arbitrary amounts not tied to any particular validator + vm.prank(eigenlayerAdmin); + etherFiNodesManager.queueETHWithdrawal(uint256(val1.pubkeyHash), 1234 ether); + + // need to fast forward so that withdrawal is claimable + vm.roll(block.number + (7200 * 15)); + + vm.prank(eigenlayerAdmin); + etherFiNodesManager.completeQueuedETHWithdrawals(uint256(val1.pubkeyHash), /*receiveAsTokens=*/ true); + + // liquidity pool should have received the withdrawal + assertEq(address(liquidityPool).balance, startingLPBalance + 1234 ether); + + } + + function test_withdrawMultipleSimultaneousWithdrawals() public { + + TestValidatorParams memory params = defaultTestValidatorParams; + params.validatorSize = 64 ether; + TestValidator memory val = helper_createValidator(params); + + // queue up multiple withdrawals + vm.prank(eigenlayerAdmin); + etherFiNodesManager.queueETHWithdrawal(uint256(val.pubkeyHash), 1 ether); + vm.prank(eigenlayerAdmin); + etherFiNodesManager.queueETHWithdrawal(uint256(val.pubkeyHash), 1 ether); + vm.prank(eigenlayerAdmin); + etherFiNodesManager.queueETHWithdrawal(uint256(val.pubkeyHash), 1 ether); + + // need to fast forward so that withdrawal is claimable + vm.roll(block.number + (7200 * 15)); + + // all outstanding withdrawals should have been completed at once + uint256 startingLPBalance = address(liquidityPool).balance; + vm.prank(eigenlayerAdmin); + etherFiNodesManager.completeQueuedETHWithdrawals(uint256(val.pubkeyHash), /*receiveAsTokens=*/ true); + + assertEq(address(liquidityPool).balance, startingLPBalance + 3 ether); + } + } From 298c5cfc948f2b08e1a585bbbad5e964c3cb0eb0 Mon Sep 17 00:00:00 2001 From: dave Date: Sat, 28 Jun 2025 20:54:00 -0600 Subject: [PATCH 47/47] more test cleanup --- test/common/ArrayTestHelper.sol | 2 +- test/prelude.t.sol | 306 ++++++++++++++++---------------- 2 files changed, 155 insertions(+), 153 deletions(-) diff --git a/test/common/ArrayTestHelper.sol b/test/common/ArrayTestHelper.sol index 04b7fbcb6..27da6bb23 100644 --- a/test/common/ArrayTestHelper.sol +++ b/test/common/ArrayTestHelper.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.27; import "../../src/eigenlayer-interfaces/IDelegationManager.sol"; diff --git a/test/prelude.t.sol b/test/prelude.t.sol index 66f3f0dea..d8fdddcc9 100644 --- a/test/prelude.t.sol +++ b/test/prelude.t.sol @@ -105,6 +105,160 @@ contract PreludeTest is Test, ArrayTestHelper { } + struct TestValidatorParams { + address nodeOperator; // if none specified a new operator will be created + address etherFiNode; // if none specified a new node will be deployed + uint256 bidId; // if none specified a new bid will be placed + uint256 validatorSize; // if none specified default to 32 eth + bool withdrawable; // give the eigenpod "validatorSize" worth of withdrawable beacon shares + } + + struct TestValidator { + address etherFiNode; + address eigenPod; + uint256 legacyId; + bytes32 pubkeyHash; + address nodeOperator; + uint256 validatorSize; + } + + function helper_createValidator(TestValidatorParams memory _params) public returns (TestValidator memory) { + + // create a copy or else successive calls of this method can mutate the input unexpectedly + TestValidatorParams memory params = TestValidatorParams(_params.nodeOperator, _params.etherFiNode, _params.bidId, _params.validatorSize, _params.withdrawable); + + // configure a new operator if none provided + if (params.nodeOperator == address(0)) { + params.nodeOperator = vm.addr(0x123456); + + // register if not already + if (!nodeOperatorManager.registered(params.nodeOperator)) { + vm.prank(params.nodeOperator); + nodeOperatorManager.registerNodeOperator("test_ipfs_hash", 1000); + } + } + // create a new bid if none provided + if (params.bidId == 0) { + vm.deal(params.nodeOperator, 1 ether); + vm.prank(params.nodeOperator); + params.bidId = auctionManager.createBid{value: 0.1 ether}(1, 0.1 ether)[0]; + } + // create a new node if none provided + if (params.etherFiNode == address(0)) { + vm.prank(admin); + params.etherFiNode = stakingManager.instantiateEtherFiNode(/*createEigenPod=*/ true); + } + // default validator size if not provided + if (params.validatorSize == 0) { + params.validatorSize = 32 ether; + } + + bytes memory pubkey = vm.randomBytes(48); + bytes memory signature = vm.randomBytes(96); + + // initial deposit + address eigenPod = address(IEtherFiNode(params.etherFiNode).getEigenPod()); + bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( + pubkey, + signature, + etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), + 1 ether + ); + IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: initialDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + vm.deal(address(liquidityPool), 10000 ether); + vm.prank(address(liquidityPool)); + stakingManager.createBeaconValidators{value: 1 ether}(toArray(initialDepositData), toArray_u256(params.bidId), params.etherFiNode); + + uint256 confirmAmount = params.validatorSize - 1 ether; + + // remaining deposit + bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot( + pubkey, + signature, + etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), + confirmAmount + ); + IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ + publicKey: pubkey, + signature: signature, + depositDataRoot: confirmDepositRoot, + ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" + }); + vm.prank(address(liquidityPool)); + stakingManager.confirmAndFundBeaconValidators{value: confirmAmount}(toArray(confirmDepositData), params.validatorSize); + + if (params.withdrawable) { + // Poke some withdrawable funds into the restakedExecutionLayerGwei storage slot of the eigenpod. + // This is much easier than trying to do the full proof based workflow which relies on beacon state. + address eigenpod = etherFiNodesManager.getEigenPod(uint256(params.bidId)); + vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(params.validatorSize / 1 gwei))); + + // grant shares via delegation manager so that withdrawals work + vm.prank(delegationManager); + IEigenPodManager(eigenPodManager).addShares(params.etherFiNode, beaconStrategy, params.validatorSize); + + // give the pod enough eth to fulfill that withdrawal + vm.deal(eigenpod, params.validatorSize); + } + + TestValidator memory out = TestValidator({ + etherFiNode: params.etherFiNode, + eigenPod: eigenPod, + legacyId: params.bidId, + pubkeyHash: stakingManager.calculateValidatorPubkeyHash(pubkey), + nodeOperator: params.nodeOperator, + validatorSize: params.validatorSize + }); + return out; + } + + function test_validatorHelper() public { + + // deploy 2 validators with default settings + TestValidator memory val = helper_createValidator(defaultTestValidatorParams); + console2.log("------------------------------------"); + console2.log("etherFiNode:", val.etherFiNode); + console2.log("eigenPod:", val.eigenPod); + console2.log("legacyId:", val.legacyId); + console2.log("pubkeyHash:", uint256(val.pubkeyHash)); + console2.log("nodeOperator:", val.nodeOperator); + console2.log("validatorSize:", val.validatorSize); + TestValidator memory val2 = helper_createValidator(defaultTestValidatorParams); + console2.log("------------------------------------"); + console2.log("etherFiNode:", val2.etherFiNode); + console2.log("eigenPod:", val2.eigenPod); + console2.log("legacyId:", val2.legacyId); + console2.log("pubkeyHash:", uint256(val2.pubkeyHash)); + console2.log("nodeOperator:", val2.nodeOperator); + console2.log("validatorSize:", val2.validatorSize); + + // attach to same node as the first one + TestValidatorParams memory params = defaultTestValidatorParams; + params.etherFiNode = val.etherFiNode; + TestValidator memory val3 = helper_createValidator(params); + assertEq(val.etherFiNode, val3.etherFiNode); + assertEq(val.eigenPod, val3.eigenPod); + + // create big validator + params = defaultTestValidatorParams; + params.validatorSize = 2000 ether; + params.withdrawable = true; + TestValidator memory val4 = helper_createValidator(params); + assertEq(2000 ether, val4.validatorSize); + assertGe(IEigenPod(val4.eigenPod).withdrawableRestakedExecutionLayerGwei(), (2000 ether / 1 gwei)); + + // should fail if try to claim already claimed bid + params = defaultTestValidatorParams; + params.bidId = val.legacyId; + vm.expectRevert(); + TestValidator memory val5 = helper_createValidator(params); + } + function test_forwardingWhitelist() public { // create a node + pod @@ -496,159 +650,7 @@ contract PreludeTest is Test, ArrayTestHelper { } - struct TestValidatorParams { - address nodeOperator; // if none specified a new operator will be created - address etherFiNode; // if none specified a new node will be deployed - uint256 bidId; // if none specified a new bid will be placed - uint256 validatorSize; // default to 32 eth - bool withdrawable; // manually give the eigenpod "validatorSize" worth of withdrawable beacon shares - } - - struct TestValidator { - address etherFiNode; - address eigenPod; - uint256 legacyId; - bytes32 pubkeyHash; - address nodeOperator; - uint256 validatorSize; - } - - function helper_createValidator(TestValidatorParams memory _params) public returns (TestValidator memory) { - - // create a copy or else successive calls of this method can mutate the input unexpectedly - TestValidatorParams memory params = TestValidatorParams(_params.nodeOperator, _params.etherFiNode, _params.bidId, _params.validatorSize, _params.withdrawable); - - // configure a new operator if none provided - if (params.nodeOperator == address(0)) { - params.nodeOperator = vm.addr(0x123456); - - // register if not already - if (!nodeOperatorManager.registered(params.nodeOperator)) { - vm.prank(params.nodeOperator); - nodeOperatorManager.registerNodeOperator("test_ipfs_hash", 1000); - } - } - // create a new bid if none provided - if (params.bidId == 0) { - vm.deal(params.nodeOperator, 1 ether); - vm.prank(params.nodeOperator); - params.bidId = auctionManager.createBid{value: 0.1 ether}(1, 0.1 ether)[0]; - } - // create a new node if none provided - if (params.etherFiNode == address(0)) { - vm.prank(admin); - params.etherFiNode = stakingManager.instantiateEtherFiNode(/*createEigenPod=*/ true); - } - // default validator size if not provided - if (params.validatorSize == 0) { - params.validatorSize = 32 ether; - } - - bytes memory pubkey = vm.randomBytes(48); - bytes memory signature = vm.randomBytes(96); - - // initial deposit - address eigenPod = address(IEtherFiNode(params.etherFiNode).getEigenPod()); - bytes32 initialDepositRoot = depositRootGenerator.generateDepositRoot( - pubkey, - signature, - etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), - 1 ether - ); - IStakingManager.DepositData memory initialDepositData = IStakingManager.DepositData({ - publicKey: pubkey, - signature: signature, - depositDataRoot: initialDepositRoot, - ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" - }); - vm.deal(address(liquidityPool), 10000 ether); - vm.prank(address(liquidityPool)); - stakingManager.createBeaconValidators{value: 1 ether}(toArray(initialDepositData), toArray_u256(params.bidId), params.etherFiNode); - - uint256 confirmAmount = params.validatorSize - 1 ether; - - // remaining deposit - bytes32 confirmDepositRoot = depositRootGenerator.generateDepositRoot( - pubkey, - signature, - etherFiNodesManager.addressToWithdrawalCredentials(eigenPod), - confirmAmount - ); - IStakingManager.DepositData memory confirmDepositData = IStakingManager.DepositData({ - publicKey: pubkey, - signature: signature, - depositDataRoot: confirmDepositRoot, - ipfsHashForEncryptedValidatorKey: "test_ipfs_hash" - }); - vm.prank(address(liquidityPool)); - stakingManager.confirmAndFundBeaconValidators{value: confirmAmount}(toArray(confirmDepositData), params.validatorSize); - - if (params.withdrawable) { - // Poke some withdrawable funds into the restakedExecutionLayerGwei storage slot of the eigenpod. - // This is much easier than trying to do the full proof based workflow which relies on beacon state. - address eigenpod = etherFiNodesManager.getEigenPod(uint256(params.bidId)); - vm.store(eigenpod, bytes32(uint256(52)) /*slot*/, bytes32(uint256(params.validatorSize / 1 gwei))); - - // grant shares via delegation manager so that withdrawals work - vm.prank(delegationManager); - IEigenPodManager(eigenPodManager).addShares(params.etherFiNode, beaconStrategy, params.validatorSize); - - // give the pod enough eth to fulfill that withdrawal - vm.deal(eigenpod, params.validatorSize); - } - - TestValidator memory out = TestValidator({ - etherFiNode: params.etherFiNode, - eigenPod: eigenPod, - legacyId: params.bidId, - pubkeyHash: stakingManager.calculateValidatorPubkeyHash(pubkey), - nodeOperator: params.nodeOperator, - validatorSize: params.validatorSize - }); - return out; - } - - function test_validatorHelper() public { - - // deploy 2 validators with default settings - TestValidator memory val = helper_createValidator(defaultTestValidatorParams); - console2.log("------------------------------------"); - console2.log("etherFiNode:", val.etherFiNode); - console2.log("eigenPod:", val.eigenPod); - console2.log("legacyId:", val.legacyId); - console2.log("pubkeyHash:", uint256(val.pubkeyHash)); - console2.log("nodeOperator:", val.nodeOperator); - console2.log("validatorSize:", val.validatorSize); - TestValidator memory val2 = helper_createValidator(defaultTestValidatorParams); - console2.log("------------------------------------"); - console2.log("etherFiNode:", val2.etherFiNode); - console2.log("eigenPod:", val2.eigenPod); - console2.log("legacyId:", val2.legacyId); - console2.log("pubkeyHash:", uint256(val2.pubkeyHash)); - console2.log("nodeOperator:", val2.nodeOperator); - console2.log("validatorSize:", val2.validatorSize); - - // attach to same node as the first one - TestValidatorParams memory params = defaultTestValidatorParams; - params.etherFiNode = val.etherFiNode; - TestValidator memory val3 = helper_createValidator(params); - assertEq(val.etherFiNode, val3.etherFiNode); - assertEq(val.eigenPod, val3.eigenPod); - - // create big validator - params = defaultTestValidatorParams; - params.validatorSize = 2000 ether; - params.withdrawable = true; - TestValidator memory val4 = helper_createValidator(params); - assertEq(2000 ether, val4.validatorSize); - assertGe(IEigenPod(val4.eigenPod).withdrawableRestakedExecutionLayerGwei(), (2000 ether / 1 gwei)); - // should fail if try to claim already claimed bid - params = defaultTestValidatorParams; - params.bidId = val.legacyId; - vm.expectRevert(); - TestValidator memory val5 = helper_createValidator(params); - } function test_pubkeyHashAndLegacyId() public {