diff --git a/src/test/integration/IntegrationBase.t.sol b/src/test/integration/IntegrationBase.t.sol index 7b2f3e3108..990aa86090 100644 --- a/src/test/integration/IntegrationBase.t.sol +++ b/src/test/integration/IntegrationBase.t.sol @@ -1995,11 +1995,12 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter { /// @dev On a delegation, the DSF should be increased if the operator magnitude is non-WAD function assert_Snap_DSF_State_Delegation( User staker, + User operator, IStrategy[] memory strategies, uint[] memory delegatableShares, string memory err ) internal { - uint64[] memory maxMags = _getMaxMagnitudes(staker, strategies); + uint64[] memory maxMags = _getMaxMagnitudes(operator, strategies); for (uint i = 0; i < strategies.length; i++) { IStrategy[] memory stratArray = strategies[i].toArray(); diff --git a/src/test/integration/IntegrationChecks.t.sol b/src/test/integration/IntegrationChecks.t.sol index 113d98ac73..51bc3c5820 100644 --- a/src/test/integration/IntegrationChecks.t.sol +++ b/src/test/integration/IntegrationChecks.t.sol @@ -251,7 +251,7 @@ contract IntegrationCheckUtils is IntegrationBase { uint256[] memory delegatableShares = _getPrevStakerWithdrawableShares(staker, strategies); assert_Snap_Added_OperatorShares(operator, strategies, delegatableShares, "operator should have received shares"); check_Added_SlashableStake(operator, strategies, delegatableShares); - assert_Snap_DSF_State_Delegation(staker, strategies, delegatableShares, "staker's DSF not updated correctly"); + assert_Snap_DSF_State_Delegation(staker, operator, strategies, delegatableShares, "staker's DSF not updated correctly"); } function check_Added_SlashableStake( diff --git a/src/test/integration/tests/Deposit_Delegate_Allocate_Slash_Queue_Redeposit.t.sol b/src/test/integration/tests/Deposit_Delegate_Allocate_Slash_Queue_Redeposit.t.sol index 428dbadeb1..ec94b0a045 100644 --- a/src/test/integration/tests/Deposit_Delegate_Allocate_Slash_Queue_Redeposit.t.sol +++ b/src/test/integration/tests/Deposit_Delegate_Allocate_Slash_Queue_Redeposit.t.sol @@ -21,9 +21,6 @@ contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is Integrat uint[] numTokensRemaining; function _init() internal override { - // TODO: Partial deposits don't work when beacon chain eth balance is initialized to < 64 ETH, need to write _newRandomStaker variant that ensures beacon chain ETH balance - // greater than or equal to 64 - _configAssetTypes(HOLDS_LST); _configUserTypes(DEFAULT); (staker, strategies, initTokenBalances) = _newRandomStaker(); @@ -33,9 +30,14 @@ contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is Integrat uint[] memory tokensToDeposit = new uint[](initTokenBalances.length); numTokensRemaining = new uint[](initTokenBalances.length); + uint256 eth_to_deal; for (uint i = 0; i < initTokenBalances.length; i++) { if (strategies[i] == BEACONCHAIN_ETH_STRAT) { tokensToDeposit[i] = initTokenBalances[i]; + //user.depositIntoEigenlayer uses all ETH balance for a pod, so we deal back staker's + //starting ETH to replicate partial deposit state + eth_to_deal = initTokenBalances[i]; + numTokensRemaining[i] = initTokenBalances[i]; continue; } @@ -46,6 +48,8 @@ contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is Integrat // 1. Deposit Into Strategies initDepositShares = _calculateExpectedShares(strategies, tokensToDeposit); staker.depositIntoEigenlayer(strategies, tokensToDeposit); + //dealing back ETH + cheats.deal(address(staker), eth_to_deal); check_Deposit_State_PartialDeposit(staker, strategies, initDepositShares, numTokensRemaining); // 2. Delegate to operator @@ -302,4 +306,79 @@ contract Integration_Deposit_Delegate_Allocate_Slash_Queue_Redeposit is Integrat operator.modifyAllocations(deallocateParams); check_DecrAlloc_State_Slashable(operator, deallocateParams); } + + function testFuzz_fullSlash_undelegate_redeposit_complete( + uint24 _random + ) public rand(_random) { + + initDepositShares = _getStakerDepositShares(staker, strategies); + + // 4. Fully slash operator + SlashingParams memory slashParams = _genSlashing_Full(operator, operatorSet); + avs.slashOperator(slashParams); + check_FullySlashed_State(operator, allocateParams, slashParams); + + // 5. Undelegate from an operator + uint[] memory withdrawableShares = _getStakerWithdrawableShares(staker, strategies); + Withdrawal[] memory withdrawals = staker.undelegate(); + bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); + check_Undelegate_State(staker, operator, withdrawals, withdrawalRoots, strategies, withdrawableShares); + + // 6. Redeposit + uint[] memory shares = _calculateExpectedShares(strategies, numTokensRemaining); + staker.depositIntoEigenlayer(strategies, numTokensRemaining); + check_Deposit_State(staker, strategies, shares); + + // 7. Complete withdrawal. Staker should receive 0 shares/tokens after a full slash + _rollBlocksForCompleteWithdrawals(withdrawals); + + for (uint256 i = 0; i < withdrawals.length; ++i) { + uint[] memory expectedShares = _calculateExpectedShares(withdrawals[i]); + staker.completeWithdrawalAsShares(withdrawals[i]); + check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[i], withdrawals[i].strategies, expectedShares); + } + + + // Final state checks + assert_HasExpectedShares(staker, strategies, shares, "staker should have expected shares after redeposit"); + assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending"); + } + + function testFuzz_fullSlash_redelegate_redeposit_complete( + uint24 _random + ) public rand(_random) { + + (User operator2, ,) = _newRandomOperator(); + initDepositShares = _getStakerDepositShares(staker, strategies); + + // 4. Fully slash operator + SlashingParams memory slashParams = _genSlashing_Full(operator, operatorSet); + avs.slashOperator(slashParams); + check_FullySlashed_State(operator, allocateParams, slashParams); + + // 5. Undelegate from an operator + uint[] memory withdrawableShares = _getStakerWithdrawableShares(staker, strategies); + Withdrawal[] memory withdrawals = staker.redelegate(operator2); + bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); + check_Redelegate_State(staker, operator, operator2, withdrawals, withdrawalRoots, strategies, withdrawableShares); + + // 6. Redeposit + uint[] memory shares = _calculateExpectedShares(strategies, numTokensRemaining); + staker.depositIntoEigenlayer(strategies, numTokensRemaining); + check_Deposit_State(staker, strategies, shares); + + // 7. Complete withdrawal. Staker should receive 0 shares/tokens after a full slash + _rollBlocksForCompleteWithdrawals(withdrawals); + + for (uint256 i = 0; i < withdrawals.length; ++i) { + uint[] memory expectedShares = _calculateExpectedShares(withdrawals[i]); + staker.completeWithdrawalAsShares(withdrawals[i]); + check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[i], withdrawals[i].strategies, expectedShares); + } + + + // Final state checks + assert_HasExpectedShares(staker, strategies, shares, "staker should have expected shares after redeposit"); + assert_NoWithdrawalsPending(withdrawalRoots, "all withdrawals should be removed from pending"); + } } diff --git a/src/test/integration/tests/Slashed_Eigenpod_BC.t.sol b/src/test/integration/tests/Slashed_Eigenpod_BC.t.sol index c75af4e8bb..2759f41f56 100644 --- a/src/test/integration/tests/Slashed_Eigenpod_BC.t.sol +++ b/src/test/integration/tests/Slashed_Eigenpod_BC.t.sol @@ -130,72 +130,71 @@ contract Integration_SlashedEigenpod_BC is IntegrationCheckUtils { assertApproxEqAbs(withdrawableSharesAfter[0], depositSharesAfter[0], 100, "Withdrawable shares should equal deposit shares after withdrawal"); } - // TODO: Fix this test - // function testFuzz_delegateSlashedStaker_slashedOperator(uint24 _random) public rand(_random) { - - - // (User staker2,,) = _newRandomStaker(); - // (uint40[] memory validators2,) = staker2.startValidators(); - // beaconChain.advanceEpoch_NoWithdrawNoRewards(); - // staker2.verifyWithdrawalCredentials(validators2); - // staker2.startCheckpoint(); - // staker2.completeCheckpoint(); - // staker2.delegateTo(operator); - - // //randomize additional deposit to eigenpod - // if(_randBool()){ - // uint amount = 32 ether * _randUint({min: 1, max: 5}); - // cheats.deal(address(staker), amount); - // (uint40[] memory validators,) = staker.startValidators(); - // beaconChain.advanceEpoch_NoWithdrawNoRewards(); - // staker.verifyWithdrawalCredentials(validators); + function testFuzz_delegateSlashedStaker_slashedOperator(uint24 _random) public rand(_random) { + + + (User staker2,,) = _newRandomStaker(); + (uint40[] memory validators2,) = staker2.startValidators(); + beaconChain.advanceEpoch_NoWithdrawNoRewards(); + staker2.verifyWithdrawalCredentials(validators2); + staker2.startCheckpoint(); + staker2.completeCheckpoint(); + staker2.delegateTo(operator); + + //randomize additional deposit to eigenpod + if(_randBool()){ + uint amount = 32 ether * _randUint({min: 1, max: 5}); + cheats.deal(address(staker), amount); + (uint40[] memory validators,) = staker.startValidators(); + beaconChain.advanceEpoch_NoWithdrawNoRewards(); + staker.verifyWithdrawalCredentials(validators); - // staker.startCheckpoint(); - // staker.completeCheckpoint(); - // } - - // // Create an operator set and register an operator. - // operatorSet = avs.createOperatorSet(strategies); - // operator.registerForOperatorSet(operatorSet); - // check_Registration_State_NoAllocation(operator, operatorSet, strategies); - - // // Allocate to operator set - // allocateParams = _genAllocation_AllAvailable(operator, operatorSet, strategies); - // operator.modifyAllocations(allocateParams); - // check_IncrAlloc_State_Slashable(operator, allocateParams); - // _rollBlocksForCompleteAllocation(operator, operatorSet, strategies); - - // //Slash operator before delegation - // IAllocationManagerTypes.SlashingParams memory slashingParams; - // uint wadToSlash = _randWadToSlash(); - // slashingParams = avs.slashOperator(operator, operatorSet.id, strategies, wadToSlash.toArrayU256()); - // assert_Snap_Allocations_Slashed(slashingParams, operatorSet, true, "operator allocations should be slashed"); - - // uint256[] memory initDelegatableShares = _getWithdrawableShares(staker, strategies); - // uint256[] memory initDepositShares = _getStakerDepositShares(staker, strategies); - - // // Delegate to an operator - // staker.delegateTo(operator); - // (uint256[] memory delegatedShares, ) = delegationManager.getWithdrawableShares(address(staker), strategies); - // check_Delegation_State(staker, operator, strategies, initDepositShares); + staker.startCheckpoint(); + staker.completeCheckpoint(); + } + + // Create an operator set and register an operator. + operatorSet = avs.createOperatorSet(strategies); + operator.registerForOperatorSet(operatorSet); + check_Registration_State_NoAllocation(operator, operatorSet, strategies); + + // Allocate to operator set + allocateParams = _genAllocation_AllAvailable(operator, operatorSet, strategies); + operator.modifyAllocations(allocateParams); + check_IncrAlloc_State_Slashable(operator, allocateParams); + _rollBlocksForCompleteAllocation(operator, operatorSet, strategies); + + //Slash operator before delegation + IAllocationManagerTypes.SlashingParams memory slashingParams; + uint wadToSlash = _randWadToSlash(); + slashingParams = avs.slashOperator(operator, operatorSet.id, strategies, wadToSlash.toArrayU256()); + assert_Snap_Allocations_Slashed(slashingParams, operatorSet, true, "operator allocations should be slashed"); + + uint256[] memory initDelegatableShares = _getWithdrawableShares(staker, strategies); + uint256[] memory initDepositShares = _getStakerDepositShares(staker, strategies); + + // Delegate to an operator + staker.delegateTo(operator); + (uint256[] memory delegatedShares, ) = delegationManager.getWithdrawableShares(address(staker), strategies); + check_Delegation_State(staker, operator, strategies, initDepositShares); - // // Undelegate from an operator - // IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.undelegate(); - // bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); - // check_Undelegate_State(staker, operator, withdrawals, withdrawalRoots, strategies, initDepositShares, delegatedShares); - - // // Complete withdrawal as shares - // // Fast forward to when we can complete the withdrawal - // _rollBlocksForCompleteWithdrawals(withdrawals); - // for (uint256 i = 0; i < withdrawals.length; ++i) { - // staker.completeWithdrawalAsShares(withdrawals[i]); - // check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[i], withdrawals[i].strategies, delegatedShares); - // } - - // (uint256[] memory withdrawableSharesAfter, uint256[] memory depositSharesAfter) = delegationManager.getWithdrawableShares(address(staker), strategies); - // assertEq(depositSharesAfter[0], delegatedShares[0], "Deposit shares should reset to reflect slash(es)"); - // assertApproxEqAbs(withdrawableSharesAfter[0], depositSharesAfter[0], 100, "Withdrawable shares should equal deposit shares after withdrawal"); - // } + // Undelegate from an operator + IDelegationManagerTypes.Withdrawal[] memory withdrawals = staker.undelegate(); + bytes32[] memory withdrawalRoots = _getWithdrawalHashes(withdrawals); + check_Undelegate_State(staker, operator, withdrawals, withdrawalRoots, strategies, delegatedShares); + + // Complete withdrawal as shares + // Fast forward to when we can complete the withdrawal + _rollBlocksForCompleteWithdrawals(withdrawals); + for (uint256 i = 0; i < withdrawals.length; ++i) { + staker.completeWithdrawalAsShares(withdrawals[i]); + check_Withdrawal_AsShares_Undelegated_State(staker, operator, withdrawals[i], withdrawals[i].strategies, delegatedShares); + } + + (uint256[] memory withdrawableSharesAfter, uint256[] memory depositSharesAfter) = delegationManager.getWithdrawableShares(address(staker), strategies); + assertEq(depositSharesAfter[0], delegatedShares[0], "Deposit shares should reset to reflect slash(es)"); + assertApproxEqAbs(withdrawableSharesAfter[0], depositSharesAfter[0], 100, "Withdrawable shares should equal deposit shares after withdrawal"); + } function testFuzz_delegateSlashedStaker_redelegate_complete(uint24 _random) public rand(_random){ diff --git a/src/test/integration/tests/SlashingWithdrawals.t.sol b/src/test/integration/tests/SlashingWithdrawals.t.sol index ee82e546cd..458c4ed07c 100644 --- a/src/test/integration/tests/SlashingWithdrawals.t.sol +++ b/src/test/integration/tests/SlashingWithdrawals.t.sol @@ -27,7 +27,6 @@ contract Integration_ALMSlashBase is IntegrationCheckUtils { /// 5. Operator registers for operator set /// NOTE: Steps 4 and 5 are done in random order, as these should not have an outcome on the test function _init() internal virtual override { - _configAssetTypes(HOLDS_LST); (staker, strategies, initTokenBalances) = _newRandomStaker(); operator = _newRandomOperator_NoAssets(); (avs,) = _newRandomAVS();