diff --git a/src/test/integration/IntegrationChecks.t.sol b/src/test/integration/IntegrationChecks.t.sol index 4a12d0f11..ffe014d78 100644 --- a/src/test/integration/IntegrationChecks.t.sol +++ b/src/test/integration/IntegrationChecks.t.sol @@ -262,7 +262,6 @@ contract IntegrationCheckUtils is IntegrationBase { // Common checks assert_WithdrawalNotPending(delegationManager.calculateWithdrawalRoot(withdrawal), "staker withdrawal should no longer be pending"); - // FIXME: This check is currently broken for native ETH deposits for some reason. assert_Snap_Added_TokenBalances(staker, tokens, expectedTokens, "staker should have received expected tokens"); assert_Snap_Unchanged_StakerDepositShares(staker, "staker shares should not have changed"); assert_Snap_Removed_StrategyShares(strategies, shares, "strategies should have total shares decremented"); diff --git a/src/test/integration/tests/Deposit_Delegate_Queue_Complete.t.sol b/src/test/integration/tests/Deposit_Delegate_Queue_Complete.t.sol index 882143987..edb076135 100644 --- a/src/test/integration/tests/Deposit_Delegate_Queue_Complete.t.sol +++ b/src/test/integration/tests/Deposit_Delegate_Queue_Complete.t.sol @@ -16,62 +16,62 @@ contract Integration_Deposit_Delegate_Queue_Complete is IntegrationCheckUtils { /// 2. delegates to an operator /// 3. queues a withdrawal for a ALL shares /// 4. completes the queued withdrawal as tokens - // function testFuzz_deposit_delegate_queue_completeAsTokens(uint24 _random) public { - // // When new Users are created, they will choose a random configuration from these params: - // _configRand({ - // _randomSeed: _random, - // _assetTypes: HOLDS_LST | HOLDS_ETH | HOLDS_ALL, - // _userTypes: DEFAULT | ALT_METHODS - // }); - - // /// 0. Create an operator and a staker with: - // // - some nonzero underlying token balances - // // - corresponding to a random subset of valid strategies (StrategyManager and/or EigenPodManager) - // // - // // ... check that the staker has no delegatable shares and isn't currently delegated - // ( - // User staker, - // IStrategy[] memory strategies, - // uint[] memory tokenBalances - // ) = _newRandomStaker(); - // (User operator, ,) = _newRandomOperator(); - // // Upgrade contracts if forkType is not local - // _upgradeEigenLayerContracts(); - - // uint[] memory shares = _calculateExpectedShares(strategies, tokenBalances); - - // assert_HasNoDelegatableShares(staker, "staker should not have delegatable shares before depositing"); - // assertFalse(delegationManager.isDelegated(address(staker)), "staker should not be delegated"); - - // // 1. Deposit Into Strategies - // staker.depositIntoEigenlayer(strategies, tokenBalances); - // check_Deposit_State(staker, strategies, shares); - - // // 2. Delegate to an operator - // staker.delegateTo(operator); - // check_Delegation_State(staker, operator, strategies, shares); - - // // 3. Queue Withdrawals - // IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.queueWithdrawals(strategies, shares); - // bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); - // check_QueuedWithdrawal_State(staker, operator, strategies, shares, withdrawals, withdrawalRoots); - - // // 4. Complete withdrawal - // // Fast forward to when we can complete the withdrawal - // _rollBlocksForCompleteWithdrawals(); - - // for (uint256 i = 0; i < withdrawals.length; i++) { - // uint256[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].scaledShares); - // IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]); - // check_Withdrawal_AsTokens_State(staker, operator, withdrawals[i], strategies, shares, tokens, expectedTokens); - // } - - // // Check final state: - // assertEq(address(operator), delegationManager.delegatedTo(address(staker)), "staker should still be delegated to operator"); - // assert_HasNoDelegatableShares(staker, "staker should have withdrawn all shares"); - // assert_HasUnderlyingTokenBalances(staker, strategies, tokenBalances, "staker should once again have original token balances"); - // assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending"); - // } + function testFuzz_deposit_delegate_queue_completeAsTokens(uint24 _random) public { + // When new Users are created, they will choose a random configuration from these params: + _configRand({ + _randomSeed: _random, + _assetTypes: HOLDS_LST | HOLDS_ETH | HOLDS_ALL, + _userTypes: DEFAULT | ALT_METHODS + }); + + /// 0. Create an operator and a staker with: + // - some nonzero underlying token balances + // - corresponding to a random subset of valid strategies (StrategyManager and/or EigenPodManager) + // + // ... check that the staker has no delegatable shares and isn't currently delegated + ( + User staker, + IStrategy[] memory strategies, + uint[] memory tokenBalances + ) = _newRandomStaker(); + (User operator, ,) = _newRandomOperator(); + // Upgrade contracts if forkType is not local + _upgradeEigenLayerContracts(); + + uint[] memory shares = _calculateExpectedShares(strategies, tokenBalances); + + assert_HasNoDelegatableShares(staker, "staker should not have delegatable shares before depositing"); + assertFalse(delegationManager.isDelegated(address(staker)), "staker should not be delegated"); + + // 1. Deposit Into Strategies + staker.depositIntoEigenlayer(strategies, tokenBalances); + check_Deposit_State(staker, strategies, shares); + + // 2. Delegate to an operator + staker.delegateTo(operator); + check_Delegation_State(staker, operator, strategies, shares); + + // 3. Queue Withdrawals + IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.queueWithdrawals(strategies, shares); + bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); + check_QueuedWithdrawal_State(staker, operator, strategies, shares, withdrawals, withdrawalRoots); + + // 4. Complete withdrawal + // Fast forward to when we can complete the withdrawal + _rollBlocksForCompleteWithdrawals(); + + for (uint256 i = 0; i < withdrawals.length; i++) { + uint256[] memory expectedTokens = _calculateExpectedTokens(withdrawals[i].strategies, withdrawals[i].scaledShares); + IERC20[] memory tokens = staker.completeWithdrawalAsTokens(withdrawals[i]); + check_Withdrawal_AsTokens_State(staker, operator, withdrawals[i], strategies, shares, tokens, expectedTokens); + } + + // Check final state: + assertEq(address(operator), delegationManager.delegatedTo(address(staker)), "staker should still be delegated to operator"); + assert_HasNoDelegatableShares(staker, "staker should have withdrawn all shares"); + assert_HasUnderlyingTokenBalances(staker, strategies, tokenBalances, "staker should once again have original token balances"); + assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending"); + } /// Generates a random staker and operator. The staker: /// 1. deposits all assets into strategies diff --git a/src/test/unit/EigenPodManagerUnit.t.sol b/src/test/unit/EigenPodManagerUnit.t.sol index 1efbd0151..4f9bd7b18 100644 --- a/src/test/unit/EigenPodManagerUnit.t.sol +++ b/src/test/unit/EigenPodManagerUnit.t.sol @@ -353,6 +353,12 @@ contract EigenPodManagerUnitTests_WithdrawSharesAsTokensTests is EigenPodManager emit PodSharesUpdated(defaultStaker, 100e18); cheats.expectEmit(true, true, true, true); emit NewTotalShares(defaultStaker, 0); + // Expect call to EigenPod for the withdrawal + cheats.expectCall( + address(defaultPod), + abi.encodeWithSelector(IEigenPod.withdrawRestakedBeaconChainETH.selector, defaultStaker, 1e18), + 1 + ); eigenPodManager.withdrawSharesAsTokens(defaultStaker, beaconChainETHStrategy, IERC20(address(0)), sharesToWithdraw); // Check storage update @@ -370,6 +376,17 @@ contract EigenPodManagerUnitTests_WithdrawSharesAsTokensTests is EigenPodManager // Withdraw shares cheats.prank(address(delegationManagerMock)); + cheats.expectEmit(true, true, true, true); + emit PodSharesUpdated(defaultStaker, 50e18); + cheats.expectEmit(true, true, true, true); + emit NewTotalShares(defaultStaker, -50e18); + // Assert that no call is made by passing in zero for the count + bytes memory emptyBytes; + cheats.expectCall( + address(defaultPod), + emptyBytes, // Cheatcode checks a partial match starting at the first byte of the calldata + 0 + ); eigenPodManager.withdrawSharesAsTokens(defaultStaker, beaconChainETHStrategy, IERC20(address(0)), sharesToWithdraw); // Check storage update @@ -387,6 +404,12 @@ contract EigenPodManagerUnitTests_WithdrawSharesAsTokensTests is EigenPodManager // Withdraw shares cheats.prank(address(delegationManagerMock)); + // Expect call to EigenPod for the withdrawal + cheats.expectCall( + address(defaultPod), + abi.encodeWithSelector(IEigenPod.withdrawRestakedBeaconChainETH.selector, defaultStaker, sharesToWithdraw), + 1 + ); eigenPodManager.withdrawSharesAsTokens(defaultStaker, beaconChainETHStrategy, IERC20(address(0)), sharesToWithdraw); // Check storage remains the same