diff --git a/src/EtherFiNode.sol b/src/EtherFiNode.sol index a60a2ed3..76099062 100644 --- a/src/EtherFiNode.sol +++ b/src/EtherFiNode.sol @@ -318,11 +318,12 @@ contract EtherFiNode is IEtherFiNode, IERC1271 { if (isRestakingEnabled) { IDelegationManager delegationManager = IEtherFiNodesManager(etherFiNodesManager).delegationManager(); + IStrategy beaconStrategy = delegationManager.beaconChainETHStrategy(); // get the shares locked in the EigenPod // - `withdrawableShares` reflects the slashing on 'depositShares' (uint256[] memory withdrawableShares, uint256[] memory depositShares) = delegationManager.getWithdrawableShares(address(this), getStrategies()); - _eigenPod = withdrawableShares[0]; + _eigenPod = beaconStrategy.sharesToUnderlyingView(withdrawableShares[0]); // get the shares locked in the DelegationManager // - `shares` reflects the slashing. but it can change over time while in the queue until the slashing completion @@ -333,6 +334,7 @@ contract EtherFiNode is IEtherFiNode, IERC1271 { _withdrawal_queue += shares[i][j]; } } + _withdrawal_queue = beaconStrategy.sharesToUnderlyingView(_withdrawal_queue); } return (_withdrawalSafe, _eigenPod, _withdrawal_queue); } @@ -603,11 +605,22 @@ contract EtherFiNode is IEtherFiNode, IERC1271 { IStrategy[] memory strategies = getStrategies(); (uint256[] memory withdrawableShares, uint256[] memory depositShares) = delegationManager.getWithdrawableShares(address(this), strategies); - // calculate the amount to withdraw - // 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 + // 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 = Math.min(depositShares[0], 32 ether); + uint256 depositSharesToWithdraw; + if (numAssociatedValidators() == 1) { + require(IEigenPod(eigenPod).activeValidatorCount() == 0, "ACTIVE_VALIDATOR_EXISTS"); + depositSharesToWithdraw = depositShares[0]; + } else { + uint256 eigenLayerBeaconStrategyShare = delegationManager.beaconChainETHStrategy().underlyingToShares(32 ether); + depositSharesToWithdraw = Math.min(depositShares[0], eigenLayerBeaconStrategyShare); + } // Queue the withdrawal // Note that the actual withdarwal amount can change if the slashing happens diff --git a/src/EtherFiRestaker.sol b/src/EtherFiRestaker.sol index 27b9ae94..64668496 100644 --- a/src/EtherFiRestaker.sol +++ b/src/EtherFiRestaker.sol @@ -15,6 +15,7 @@ import "./LiquidityPool.sol"; import "./eigenlayer-interfaces/IStrategyManager.sol"; import "./eigenlayer-interfaces/IDelegationManager.sol"; +import "./eigenlayer-interfaces/IRewardsCoordinator.sol"; contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable { using SafeERC20 for IERC20; @@ -26,6 +27,8 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, uint256 elSharesInPendingForWithdrawals; } + IRewardsCoordinator public immutable rewardsCoordinator; + LiquidityPool public liquidityPool; Liquifier public liquifier; ILidoWithdrawalQueue public lidoWithdrawalQueue; @@ -39,7 +42,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, mapping(address => TokenInfo) public tokenInfos; EnumerableSet.Bytes32Set private withdrawalRootsSet; - mapping(bytes32 => IDelegationManager.Withdrawal) public withdrawalRootToWithdrawal; + mapping(bytes32 => IDelegationManager.Withdrawal) public DEPRECATED_withdrawalRootToWithdrawal; event QueuedStEthWithdrawals(uint256[] _reqIds); @@ -56,7 +59,8 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, error IncorrectCaller(); /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { + constructor(address _rewardsCoordinator) { + rewardsCoordinator = IRewardsCoordinator(_rewardsCoordinator); _disableInitializers(); } @@ -137,6 +141,11 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, // |--------------------------------------------------------------------------------------------| // | EigenLayer Restaking | // |--------------------------------------------------------------------------------------------| + + /// Set the claimer of the restaking rewards of this contract + function setRewardsClaimer(address _claimer) external onlyAdmin { + rewardsCoordinator.setClaimerFor(_claimer); + } // delegate to an AVS operator function delegateTo(address operator, IDelegationManager.SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt) external onlyAdmin { @@ -145,19 +154,13 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, // undelegate from the current AVS operator & un-restake all function undelegate() external onlyAdmin returns (bytes32[] memory) { - revert("FIX BELOW"); - - // Un-restake all assets - // Currently, only stETH is supported - // TokenInfo memory info = tokenInfos[address(lido)]; - // uint256 shares = eigenLayerStrategyManager.stakerStrategyShares(address(this), info.elStrategy); - - // _queueWithdrawlsByShares(address(lido), shares); + bytes32[] memory withdrawalRoots = eigenLayerDelegationManager.undelegate(address(this)); - // bytes32[] memory withdrawalRoots = eigenLayerDelegationManager.undelegate(address(this)); - // assert(withdrawalRoots.length == 0); - - // return withdrawalRoots; + for (uint256 i = 0; i < withdrawalRoots.length; i++) { + withdrawalRootsSet.add(withdrawalRoots[i]); + } + + return withdrawalRoots; } // deposit the token in holding into the restaking strategy @@ -176,115 +179,41 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, /// @param amount the amount of token to withdraw function queueWithdrawals(address token, uint256 amount) public onlyAdmin returns (bytes32[] memory) { uint256 shares = getEigenLayerRestakingStrategy(token).underlyingToSharesView(amount); - return _queueWithdrawlsByShares(token, shares); + return _queueWithdrawalsByShares(token, shares); } /// Advanced version - function queueWithdrawals(IDelegationManager.QueuedWithdrawalParams[] memory queuedWithdrawalParams) public onlyAdmin returns (bytes32[] memory) { - revert("FIX BELOW"); - // uint256 currentNonce = eigenLayerDelegationManager.cumulativeWithdrawalsQueued(address(this)); - - // bytes32[] memory withdrawalRoots = eigenLayerDelegationManager.queueWithdrawals(queuedWithdrawalParams); - // IDelegationManager.Withdrawal[] memory withdrawals = new IDelegationManager.Withdrawal[](queuedWithdrawalParams.length); - - // for (uint256 i = 0; i < queuedWithdrawalParams.length; i++) { - // withdrawals[i] = IDelegationManager.Withdrawal({ - // staker: address(this), - // delegatedTo: eigenLayerDelegationManager.delegatedTo(address(this)), - // withdrawer: address(this), - // nonce: currentNonce + i, - // startBlock: uint32(block.number), - // strategies: queuedWithdrawalParams[i].strategies, - // shares: queuedWithdrawalParams[i].shares - // }); - - // require(eigenLayerDelegationManager.calculateWithdrawalRoot(withdrawals[i]) == withdrawalRoots[i], "INCORRECT_WITHDRAWAL_ROOT"); - // require(eigenLayerDelegationManager.pendingWithdrawals(withdrawalRoots[i]), "WITHDRAWAL_NOT_PENDING"); - - // for (uint256 j = 0; j < queuedWithdrawalParams[i].strategies.length; j++) { - // address token = address(queuedWithdrawalParams[i].strategies[j].underlyingToken()); - // tokenInfos[token].elSharesInPendingForWithdrawals += queuedWithdrawalParams[i].shares[j]; - // } - - // withdrawalRootToWithdrawal[withdrawalRoots[i]] = withdrawals[i]; - // withdrawalRootsSet.add(withdrawalRoots[i]); - // } - - // return withdrawalRoots; - } - - /// @notice Complete the queued withdrawals that are ready to be withdrawn - /// @param max_cnt the maximum number of withdrawals to complete - function completeQueuedWithdrawals(uint256 max_cnt) external onlyAdmin { - revert("FIX BELOW"); - // bytes32[] memory withdrawalRoots = pendingWithdrawalRoots(); - - // // process the first `max_cnt` withdrawals - // uint256 num_to_process = _min(max_cnt, withdrawalRoots.length); - - // IDelegationManager.Withdrawal[] memory _queuedWithdrawals = new IDelegationManager.Withdrawal[](num_to_process); - // IERC20[][] memory _tokens = new IERC20[][](num_to_process); - // uint256[] memory _middlewareTimesIndexes = new uint256[](num_to_process); + function queueWithdrawalsWithParams(IDelegationManagerTypes.QueuedWithdrawalParams[] memory params) public onlyAdmin returns (bytes32[] memory) { + bytes32[] memory withdrawalRoots = eigenLayerDelegationManager.queueWithdrawals(params); - // uint256 cnt = 0; - // for (uint256 i = 0; i < num_to_process; i++) { - // IDelegationManager.Withdrawal memory withdrawal = withdrawalRootToWithdrawal[withdrawalRoots[i]]; - - // uint256 withdrawalDelay = eigenLayerDelegationManager.getWithdrawalDelay(withdrawal.strategies); - - // if (withdrawal.startBlock + withdrawalDelay <= block.number) { - // IERC20[] memory tokens = new IERC20[](withdrawal.strategies.length); - // for (uint256 j = 0; j < withdrawal.strategies.length; j++) { - // tokens[j] = withdrawal.strategies[j].underlyingToken(); - - // assert(tokenInfos[address(tokens[j])].elStrategy == withdrawal.strategies[j]); - - // tokenInfos[address(tokens[j])].elSharesInPendingForWithdrawals -= withdrawal.shares[j]; - // } - - // _queuedWithdrawals[cnt] = withdrawal; - // _tokens[cnt] = tokens; - // _middlewareTimesIndexes[cnt] = 0; - // cnt += 1; - // } - // } - - // if (cnt == 0) return; - - // assembly { - // mstore(_queuedWithdrawals, cnt) - // mstore(_tokens, cnt) - // mstore(_middlewareTimesIndexes, cnt) - // } - - // completeQueuedWithdrawals(_queuedWithdrawals, _tokens, _middlewareTimesIndexes); + for (uint256 i = 0; i < withdrawalRoots.length; i++) { + withdrawalRootsSet.add(withdrawalRoots[i]); + } + return withdrawalRoots; } /// Advanced version /// @notice Used to complete the specified `queuedWithdrawals`. The function caller must match `queuedWithdrawals[...].withdrawer` /// @param _queuedWithdrawals The QueuedWithdrawals to complete. /// @param _tokens Array of tokens for each QueuedWithdrawal. See `completeQueuedWithdrawal` for the usage of a single array. - /// @param _middlewareTimesIndexes One index to reference per QueuedWithdrawal. See `completeQueuedWithdrawal` for the usage of a single index. - /// @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw` - function completeQueuedWithdrawals(IDelegationManager.Withdrawal[] memory _queuedWithdrawals, IERC20[][] memory _tokens, uint256[] memory _middlewareTimesIndexes) public onlyAdmin { - revert("FIX BELOW"); - // uint256 num = _queuedWithdrawals.length; - // bool[] memory receiveAsTokens = new bool[](num); - // for (uint256 i = 0; i < num; i++) { - // bytes32 withdrawalRoot = eigenLayerDelegationManager.calculateWithdrawalRoot(_queuedWithdrawals[i]); - // emit CompletedQueuedWithdrawal(withdrawalRoot); - - // /// so that the shares withdrawn from the specified strategies are sent to the caller - // receiveAsTokens[i] = true; - // withdrawalRootsSet.remove(withdrawalRoot); - // } + function completeQueuedWithdrawals(IDelegationManager.Withdrawal[] memory _queuedWithdrawals, IERC20[][] memory _tokens) external onlyAdmin { + uint256 num = _queuedWithdrawals.length; + bool[] memory receiveAsTokens = new bool[](num); + for (uint256 i = 0; i < num; i++) { + bytes32 withdrawalRoot = eigenLayerDelegationManager.calculateWithdrawalRoot(_queuedWithdrawals[i]); + emit CompletedQueuedWithdrawal(withdrawalRoot); + + /// so that the shares withdrawn from the specified strategies are sent to the caller + receiveAsTokens[i] = true; + require(withdrawalRootsSet.remove(withdrawalRoot), "WITHDRAWAL_ROOT_NOT_FOUND"); + } - // /// it will update the erc20 balances of this contract - // eigenLayerDelegationManager.completeQueuedWithdrawals(_queuedWithdrawals, _tokens, _middlewareTimesIndexes, receiveAsTokens); + /// it will update the erc20 balances of this contract + eigenLayerDelegationManager.completeQueuedWithdrawals(_queuedWithdrawals, _tokens, receiveAsTokens); } /// Enumerate the pending withdrawal roots - function pendingWithdrawalRoots() public view returns (bytes32[] memory) { + function pendingWithdrawalRoots() external view returns (bytes32[] memory) { return withdrawalRootsSet.values(); } @@ -293,11 +222,10 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, return withdrawalRootsSet.contains(_withdrawalRoot); } - // |--------------------------------------------------------------------------------------------| - // | VIEW functions | + // | VIEW functions | // |--------------------------------------------------------------------------------------------| - function getTotalPooledEther() public view returns (uint256 total) { + function getTotalPooledEther() external view returns (uint256 total) { total = address(this).balance + getTotalPooledEther(address(lido)); } @@ -307,11 +235,18 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, } function getRestakedAmount(address _token) public view returns (uint256) { - revert("FIX BELOW"); - // TokenInfo memory info = tokenInfos[_token]; - // uint256 shares = eigenLayerStrategyManager.stakerStrategyShares(address(this), info.elStrategy); - // uint256 restaked = info.elStrategy.sharesToUnderlyingView(shares); - // return restaked; + TokenInfo memory info = tokenInfos[_token]; + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = info.elStrategy; + + // get the shares locked in the EigenPod + // - `withdrawableShares` reflects the slashing on 'depositShares' + (uint256[] memory withdrawableShares, ) = eigenLayerDelegationManager.getWithdrawableShares(address(this), strategies); + + // convert the share amount to the token's balance amount + uint256 restaked = info.elStrategy.sharesToUnderlyingView(withdrawableShares[0]); + + return restaked; } function getEigenLayerRestakingStrategy(address _token) public view returns (IStrategy) { @@ -326,21 +261,24 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, TokenInfo memory info = tokenInfos[_token]; if (info.elStrategy != IStrategy(address(0))) { uint256 restakedTokenAmount = getRestakedAmount(_token); - restaked = liquifier.quoteByFairValue(_token, restakedTokenAmount); /// restaked & pending for withdrawals - unrestaking = getEthAmountInEigenLayerPendingForWithdrawals(_token); + uint256 unrestakingTokenAmount = getAmountInEigenLayerPendingForWithdrawals(_token); + restaked = liquifier.quoteByFairValue(_token, restakedTokenAmount); // restaked & pending for withdrawals + unrestaking = liquifier.quoteByFairValue(_token, unrestakingTokenAmount); // restaked & pending for withdrawals } holding = liquifier.quoteByFairValue(_token, IERC20(_token).balanceOf(address(this))); /// eth value for erc20 holdings - pendingForWithdrawals = getEthAmountPendingForRedemption(_token); + pendingForWithdrawals = liquifier.quoteByFairValue(_token, getAmountPendingForRedemption(_token)); } - function getEthAmountInEigenLayerPendingForWithdrawals(address _token) public view returns (uint256) { + // get the amount of token restaked in EigenLayer pending for withdrawals + function getAmountInEigenLayerPendingForWithdrawals(address _token) public view returns (uint256) { TokenInfo memory info = tokenInfos[_token]; if (info.elStrategy == IStrategy(address(0))) return 0; uint256 amount = info.elStrategy.sharesToUnderlyingView(info.elSharesInPendingForWithdrawals); return amount; } - function getEthAmountPendingForRedemption(address _token) public view returns (uint256) { + // get the amount of token pending for redemption. e.g., pending in Lido's withdrawal queue + function getAmountPendingForRedemption(address _token) public view returns (uint256) { uint256 total = 0; if (_token == address(lido)) { uint256[] memory stEthWithdrawalRequestIds = lidoWithdrawalQueue.getWithdrawalRequests(address(this)); @@ -373,22 +311,20 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, } // INTERNAL functions - function _queueWithdrawlsByShares(address token, uint256 shares) internal returns (bytes32[] memory) { - revert("FIX BELOW"); - // IStrategy strategy = tokenInfos[token].elStrategy; - // IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - // IStrategy[] memory strategies = new IStrategy[](1); - // strategies[0] = strategy; - // uint256[] memory sharesArr = new uint256[](1); - // sharesArr[0] = shares; - - // params[0] = IDelegationManager.QueuedWithdrawalParams({ - // strategies: strategies, - // shares: sharesArr, - // withdrawer: address(this) - // }); + function _queueWithdrawalsByShares(address _token, uint256 _shares) internal returns (bytes32[] memory) { + IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManagerTypes.QueuedWithdrawalParams[](1); + IStrategy[] memory strategies = new IStrategy[](1); + uint256[] memory shares = new uint256[](1); + + strategies[0] = tokenInfos[_token].elStrategy; + shares[0] = _shares; + params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ + strategies: strategies, + depositShares: shares, + withdrawer: address(this) + }); - // return queueWithdrawals(params); + return queueWithdrawalsWithParams(params); } function _min(uint256 _a, uint256 _b) internal pure returns (uint256) { @@ -415,4 +351,4 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, _requirePauser(); _; } -} \ No newline at end of file +} diff --git a/test/EtherFiNode.t.sol b/test/EtherFiNode.t.sol index 76132842..d251e59a 100644 --- a/test/EtherFiNode.t.sol +++ b/test/EtherFiNode.t.sol @@ -130,37 +130,7 @@ contract EtherFiNodeTest is TestSetup { } - function test_claimMixedSafeAndPodFunds() public { - initializeTestingFork(MAINNET_FORK); - - uint256 bidId = depositAndRegisterValidator(true); - safeInstance = EtherFiNode(payable(managerInstance.etherfiNodeAddress(bidId))); - - // simulate 1 eth of already claimed staking rewards and 1 eth of unclaimed restaked rewards - _transferTo(address(safeInstance.eigenPod()), 1 ether); - _transferTo(address(safeInstance), 1 ether); - - assertEq(address(safeInstance).balance, 1 ether); - assertEq(address(safeInstance.eigenPod()).balance, 1 ether); - - // claim the restaked rewards - // safeInstance.queueRestakedWithdrawal(); - uint256[] memory validatorIds = new uint256[](1); - validatorIds[0] = bidId; - vm.prank(alice); // alice is admin - managerInstance.batchQueueRestakedWithdrawal(validatorIds); - - vm.roll(block.number + (50400) + 1); - - revert("FIX BELOW"); - // safeInstance.DEPRECATED_claimDelayedWithdrawalRouterWithdrawals(); - - // assertEq(address(safeInstance).balance, 2 ether); - // assertEq(address(safeInstance.eigenPod()).balance, 0 ether); - } - function test_splitBalanceInExecutionLayer() public { - initializeTestingFork(MAINNET_FORK); uint256 validatorId = depositAndRegisterValidator(true); @@ -222,8 +192,6 @@ contract EtherFiNodeTest is TestSetup { } function test_FullWithdrawWhenBalanceBelow16EthFails() public { - initializeTestingFork(MAINNET_FORK); - // create a restaked validator uint256 validatorId = depositAndRegisterValidator(false); EtherFiNode node = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validatorId))); @@ -354,9 +322,7 @@ contract EtherFiNodeTest is TestSetup { managerInstance.forwardEigenpodCall(validatorIds, data); } - function testFullWithdrawBurnsTNFT() public { - initializeTestingFork(MAINNET_FORK); - + function test_FullWithdrawBurnsTNFT() public { uint256 validatorId = depositAndRegisterValidator(false); safeInstance = EtherFiNode(payable(managerInstance.etherfiNodeAddress(validatorId))); @@ -2147,28 +2113,6 @@ contract EtherFiNodeTest is TestSetup { } } - function test_ForcedPartialWithdrawal_succeeds() 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); - - // Earned >= 16 ether - _transferTo(etherfiNode, 16 ether); - - vm.expectRevert(EtherFiNodesManager.NotAdmin.selector); - managerInstance.partialWithdraw(validatorId); - - vm.prank(alice); - managerInstance.partialWithdraw(validatorId); - } - function test_lp_as_bnft_holders_cant_mix_up_1() public { uint256[] memory validatorIds = launch_validator(1, 0, false); uint256 validatorId = validatorIds[0]; @@ -2401,25 +2345,6 @@ contract EtherFiNodeTest is TestSetup { vm.stopPrank(); } - function _perform_pepe_upgrade() internal { - address eigenlayerAdmin = address(0xBE1685C81aA44FF9FB319dD389addd9374383e90); - ITimelock eigenlayerTimelock = ITimelock(0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF); - - - // wait until timelock is finished and perform upgrade - vm.warp(block.timestamp + (60 * 60 * 24 * 8)); - vm.roll(block.number + (7200*8)); - - address target = 0x369e6F597e22EaB55fFb173C6d9cD234BD699111; - uint256 value = 0; - string memory signature = ""; - bytes memory data = hex"6A76120200000000000000000000000040A2ACCBD92BCA938B02010E17A5B8929B49130D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E000000000000000000000000000000000000000000000000000000000000001648D80FF0A00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000112005A2A4F2F3C18F09179B6703E63D9EDD165909073000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000243659CFE60000000000000000000000006D225E974FA404D25FFB84ED6E242FFA18EF6430008B9566ADA63B64D1E1DCF1418B43FD1433B724440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004499A88EC400000000000000000000000091E677B07F7AF907EC9A428AAFA9FC14A0D3A338000000000000000000000000731A0AD160E407393FF662231ADD6DD145AD3FEA0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041000000000000000000000000A6DB1A8C5A981D1536266D2A393C5F8DDB210EAF00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000"; - uint256 eta = 1725458400; - - vm.prank(eigenlayerAdmin); - eigenlayerTimelock.executeTransaction(target, value, signature, data, eta); - } - function test_mainnet_pepe_calculateTVL() public { initializeRealisticFork(MAINNET_FORK); @@ -2429,7 +2354,6 @@ contract EtherFiNodeTest is TestSetup { (uint256 toOperator, uint256 toTnft, uint256 toBnft, uint256 toTreasury) = managerInstance.calculateTVL(validatorId, 32 ether); - _perform_pepe_upgrade(); _upgrade_etherfi_node_contract(); _upgrade_etherfi_nodes_manager_contract(); @@ -2438,7 +2362,7 @@ contract EtherFiNodeTest is TestSetup { // 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); + _transferTo(eigenPod, 32 ether); + (toOperator, toTnft, toBnft, toTreasury) = managerInstance.calculateTVL(validatorId, 0 ether); } } diff --git a/test/EtherFiRestaker.t.sol b/test/EtherFiRestaker.t.sol index 2361e239..f1fff14e 100644 --- a/test/EtherFiRestaker.t.sol +++ b/test/EtherFiRestaker.t.sol @@ -64,14 +64,14 @@ contract EtherFiRestakerTest is TestSetup { _deposit_stEth(amount); - assertEq(etherFiRestakerInstance.getEthAmountPendingForRedemption(address(stEth)), 0); + assertEq(etherFiRestakerInstance.getAmountPendingForRedemption(address(stEth)), 0); vm.startPrank(alice); uint256 stEthBalance = stEth.balanceOf(address(etherFiRestakerInstance)); uint256[] memory reqIds = etherFiRestakerInstance.stEthRequestWithdrawal(stEthBalance); vm.stopPrank(); - assertApproxEqAbs(etherFiRestakerInstance.getEthAmountPendingForRedemption(address(stEth)), amount, 2 wei); + assertApproxEqAbs(etherFiRestakerInstance.getAmountPendingForRedemption(address(stEth)), amount, 2 wei); assertApproxEqAbs(etherFiRestakerInstance.getTotalPooledEther(), amount, 2 wei); assertApproxEqAbs(liquidityPoolInstance.getTotalPooledEther(), lpTvl + amount, 2 wei); @@ -85,7 +85,7 @@ contract EtherFiRestakerTest is TestSetup { etherFiRestakerInstance.lidoWithdrawalQueue().finalize(reqIds[reqIds.length-1], currentRate); vm.stopPrank(); - assertApproxEqAbs(etherFiRestakerInstance.getEthAmountPendingForRedemption(address(stEth)), amount, 2 wei); + assertApproxEqAbs(etherFiRestakerInstance.getAmountPendingForRedemption(address(stEth)), amount, 2 wei); assertApproxEqAbs(etherFiRestakerInstance.getTotalPooledEther(), amount, 2 wei); assertApproxEqAbs(liquidityPoolInstance.getTotalPooledEther(), lpTvl + amount, 2 wei); @@ -96,7 +96,7 @@ contract EtherFiRestakerTest is TestSetup { etherFiRestakerInstance.stEthClaimWithdrawals(reqIds, hints); // the cycle completes - assertApproxEqAbs(etherFiRestakerInstance.getEthAmountPendingForRedemption(address(stEth)), 0, 2 wei); + assertApproxEqAbs(etherFiRestakerInstance.getAmountPendingForRedemption(address(stEth)), 0, 2 wei); assertApproxEqAbs(etherFiRestakerInstance.getTotalPooledEther(), 0, 2 wei); assertApproxEqAbs(address(etherFiRestakerInstance).balance, 0, 2); @@ -125,40 +125,40 @@ contract EtherFiRestakerTest is TestSetup { } function test_queueWithdrawals_2() public returns (bytes32[] memory) { - revert("FIX BELOW"); - // test_restake_stEth(); + test_restake_stEth(); - // IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - // IStrategy[] memory strategies = new IStrategy[](1); - // strategies[0] = etherFiRestakerInstance.getEigenLayerRestakingStrategy(address(stEth)); - // uint256[] memory shares = new uint256[](1); - // shares[0] = eigenLayerStrategyManager.stakerStrategyShares(address(etherFiRestakerInstance), strategies[0]); + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = etherFiRestakerInstance.getEigenLayerRestakingStrategy(address(stEth)); + (uint256[] memory withdrawableShares, ) = eigenLayerDelegationManager.getWithdrawableShares(address(this), strategies); - // params[0] = IDelegationManager.QueuedWithdrawalParams({ - // strategies: strategies, - // shares: shares, - // withdrawer: address(etherFiRestakerInstance) - // }); - - // vm.prank(etherfiOperatingAdmin); - // return etherFiRestakerInstance.queueWithdrawals(params); + IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = new IDelegationManagerTypes.QueuedWithdrawalParams[](1); + params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ + strategies: strategies, + depositShares: withdrawableShares, + withdrawer: address(etherFiRestakerInstance) + }); + + vm.prank(etherfiOperatingAdmin); + return etherFiRestakerInstance.queueWithdrawalsWithParams(params); } function test_completeQueuedWithdrawals_1() public { bytes32[] memory withdrawalRoots = test_queueWithdrawals_1(); assertTrue(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots[0])); + revert("FIX BELOW"); + vm.startPrank(etherfiOperatingAdmin); - // It won't complete the withdrawal because the withdrawal is still pending - etherFiRestakerInstance.completeQueuedWithdrawals(1000); - assertTrue(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots[0])); - assertApproxEqAbs(etherFiRestakerInstance.getEthAmountInEigenLayerPendingForWithdrawals(address(stEth)), 5 ether, 2 wei); + // // It won't complete the withdrawal because the withdrawal is still pending + // etherFiRestakerInstance.completeQueuedWithdrawals(1000); + // assertTrue(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots[0])); + // assertApproxEqAbs(etherFiRestakerInstance.getEthAmountInEigenLayerPendingForWithdrawals(address(stEth)), 5 ether, 2 wei); - vm.roll(block.number + 50400); + // vm.roll(block.number + 50400); - etherFiRestakerInstance.completeQueuedWithdrawals(1000); - assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots[0])); - assertApproxEqAbs(etherFiRestakerInstance.getEthAmountInEigenLayerPendingForWithdrawals(address(stEth)), 0, 2 wei); + // etherFiRestakerInstance.completeQueuedWithdrawals(1000); + // assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots[0])); + // assertApproxEqAbs(etherFiRestakerInstance.getEthAmountInEigenLayerPendingForWithdrawals(address(stEth)), 0, 2 wei); vm.stopPrank(); } @@ -174,20 +174,22 @@ contract EtherFiRestakerTest is TestSetup { vm.roll(block.number + 50400 / 2); + revert("FIX BELOW"); + // The first withdrawal is completed // But, the second withdrawal is still pending // Therefore, `completeQueuedWithdrawals` will not complete the second withdrawal - vm.startPrank(etherfiOperatingAdmin); - etherFiRestakerInstance.completeQueuedWithdrawals(1000); + // vm.startPrank(etherfiOperatingAdmin); + // etherFiRestakerInstance.completeQueuedWithdrawals(1000); - assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots1[0])); - assertTrue(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots2[0])); + // assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots1[0])); + // assertTrue(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots2[0])); - vm.roll(block.number + 50400 / 2); + // vm.roll(block.number + 50400 / 2); - etherFiRestakerInstance.completeQueuedWithdrawals(1000); - assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots1[0])); - assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots2[0])); + // etherFiRestakerInstance.completeQueuedWithdrawals(1000); + // assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots1[0])); + // assertFalse(etherFiRestakerInstance.isPendingWithdrawal(withdrawalRoots2[0])); vm.stopPrank(); } @@ -226,4 +228,19 @@ contract EtherFiRestakerTest is TestSetup { etherFiRestakerInstance.delegateTo(avsOperator2, signature, 0x0); vm.stopPrank(); } -} \ No newline at end of file + + function test_claimer_upgrade() public { + initializeRealisticFork(MAINNET_FORK); + EtherFiRestaker restaker = EtherFiRestaker(payable(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf)); + address _claimer = vm.addr(433); + + address newRestakerImpl = address(new EtherFiRestaker(address(eigenLayerRewardsCoordinator))); + vm.startPrank(restaker.owner()); + + restaker.upgradeTo(newRestakerImpl); + restaker.setRewardsClaimer(_claimer); + + assertEq(eigenLayerRewardsCoordinator.claimerFor(address(restaker)), _claimer); + } + +} diff --git a/test/TestSetup.sol b/test/TestSetup.sol index 8af07943..fca0d370 100644 --- a/test/TestSetup.sol +++ b/test/TestSetup.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; -import "forge-std/console.sol"; + import "../src/eigenlayer-interfaces/IDelayedWithdrawalRouter.sol"; @@ -86,6 +86,7 @@ contract TestSetup is Test, ContractCodeChecker { BeaconChainOracleMock public beaconChainOracleMock; IEigenPodManager public eigenLayerEigenPodManager; IDelegationManager public eigenLayerDelegationManager; + IRewardsCoordinator public eigenLayerRewardsCoordinator; ITimelock public eigenLayerTimelock; ILidoWithdrawalQueue public lidoWithdrawalQueue; @@ -341,6 +342,7 @@ contract TestSetup is Test, ContractCodeChecker { eigenLayerStrategyManager = IEigenLayerStrategyManager(0x858646372CC42E1A627fcE94aa7A7033e7CF075A); eigenLayerEigenPodManager = IEigenPodManager(0x91E677b07F7AF907ec9a428aafA9fc14a0d3A338); eigenLayerDelegationManager = IDelegationManager(0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A); + eigenLayerRewardsCoordinator = IRewardsCoordinator(0x7750d328b314EfFa365A0402CcfD489B80B0adda); eigenLayerTimelock = ITimelock(0xA6Db1A8C5a981d1536266D2a393c5F8dDb210EAF); } else if (forkEnum == TESTNET_FORK) { @@ -363,6 +365,7 @@ contract TestSetup is Test, ContractCodeChecker { eigenLayerStrategyManager = IEigenLayerStrategyManager(0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6); eigenLayerEigenPodManager = IEigenPodManager(0x30770d7E3e71112d7A6b7259542D1f680a70e315); eigenLayerDelegationManager = IDelegationManager(0xA44151489861Fe9e3055d95adC98FbD462B948e7); + eigenLayerRewardsCoordinator == IRewardsCoordinator(0x7750d328b314EfFa365A0402CcfD489B80B0adda); eigenLayerTimelock = ITimelock(0xcF19CE0561052a7A7Ff21156730285997B350A7D); } else { @@ -421,7 +424,7 @@ contract TestSetup is Test, ContractCodeChecker { } function deployEtherFiRestaker() internal { - etherFiRestakerImplementation = new EtherFiRestaker(); + etherFiRestakerImplementation = new EtherFiRestaker(address(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf)); etherFiRestakerProxy = new UUPSProxy(address(etherFiRestakerImplementation), ""); etherFiRestakerInstance = EtherFiRestaker(payable(etherFiRestakerProxy)); @@ -573,7 +576,7 @@ contract TestSetup is Test, ContractCodeChecker { etherFiOracleProxy = new UUPSProxy(address(etherFiOracleImplementation), ""); etherFiOracleInstance = EtherFiOracle(payable(etherFiOracleProxy)); - etherFiRestakerImplementation = new EtherFiRestaker(); + etherFiRestakerImplementation = new EtherFiRestaker(address(0)); etherFiRestakerProxy = new UUPSProxy(address(etherFiRestakerImplementation), ""); etherFiRestakerInstance = EtherFiRestaker(payable(etherFiRestakerProxy)); @@ -1525,4 +1528,4 @@ contract TestSetup is Test, ContractCodeChecker { ); return address(uint160(uint256(hash))); } -} \ No newline at end of file +}