@@ -6000,6 +6000,48 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage
60006000 delegationManager.completeQueuedWithdrawal (withdrawal, tokens, receiveAsTokens);
60016001 }
60026002
6003+ /// @notice Verifies that when we complete a withdrawal as shares after a full slash, we revert
6004+ function test_revert_fullySlashed () public {
6005+ // Register operator
6006+ _registerOperatorWithBaseDetails (defaultOperator);
6007+ _setOperatorMagnitude (defaultOperator, strategyMock, WAD);
6008+
6009+ // Set the staker deposits in the strategies
6010+ uint256 depositAmount = 100e18 ;
6011+ strategyManagerMock.addDeposit (defaultStaker, strategyMock, depositAmount);
6012+ _delegateToOperatorWhoAcceptsAllStakers (defaultStaker, defaultOperator);
6013+
6014+ // Queue withdrawal
6015+ uint256 withdrawalAmount = depositAmount;
6016+ (
6017+ QueuedWithdrawalParams[] memory queuedWithdrawalParams ,
6018+ Withdrawal memory withdrawal ,
6019+ bytes32 withdrawalRoot
6020+ ) = _setUpQueueWithdrawalsSingleStrat ({
6021+ staker: defaultStaker,
6022+ withdrawer: defaultStaker,
6023+ strategy: strategyMock,
6024+ depositSharesToWithdraw: withdrawalAmount
6025+ });
6026+ cheats.prank (defaultStaker);
6027+ delegationManager.queueWithdrawals (queuedWithdrawalParams);
6028+
6029+ // Warp to just before the MIN_WITHDRAWAL_DELAY_BLOCKS
6030+ cheats.roll (withdrawal.startBlock + delegationManager.minWithdrawalDelayBlocks ());
6031+
6032+ // Slash all of operator's shares
6033+ _setOperatorMagnitude (defaultOperator, strategyMock, 0 );
6034+ cheats.prank (address (allocationManagerMock));
6035+ delegationManager.burnOperatorShares (defaultOperator, strategyMock, WAD, 0 );
6036+
6037+ // Complete withdrawal as shares and assert that operator has no shares increased
6038+ cheats.roll (block .number + 1 );
6039+ IERC20 [] memory tokens = strategyMock.underlyingToken ().toArray ();
6040+ cheats.expectRevert (FullySlashed.selector );
6041+ cheats.prank (defaultStaker);
6042+ delegationManager.completeQueuedWithdrawal (withdrawal, tokens, false );
6043+ }
6044+
60036045 /**
60046046 * Test completing multiple queued withdrawals for a single strategy by passing in the withdrawals
60056047 */
@@ -6539,6 +6581,172 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests
65396581 assertEq (delegationManager.operatorShares (defaultOperator, strategyMock), 0 , "shares should not have changed " );
65406582 }
65416583
6584+ /// @notice Verifies that shares are burnable for a withdrawal slashed just before the MIN_WITHDRAWAL_DELAY_BLOCKS is hit
6585+ function test_sharesBurnableAtMinDelayBlocks () public {
6586+ // Register operator
6587+ _registerOperatorWithBaseDetails (defaultOperator);
6588+ _setOperatorMagnitude (defaultOperator, strategyMock, WAD);
6589+
6590+ // Set the staker deposits in the strategies
6591+ uint256 depositAmount = 100e18 ;
6592+ strategyManagerMock.addDeposit (defaultStaker, strategyMock, depositAmount);
6593+ _delegateToOperatorWhoAcceptsAllStakers (defaultStaker, defaultOperator);
6594+
6595+ // Queue withdrawal
6596+ uint256 withdrawalAmount = depositAmount;
6597+ (
6598+ QueuedWithdrawalParams[] memory queuedWithdrawalParams ,
6599+ Withdrawal memory withdrawal ,
6600+ bytes32 withdrawalRoot
6601+ ) = _setUpQueueWithdrawalsSingleStrat ({
6602+ staker: defaultStaker,
6603+ withdrawer: defaultStaker,
6604+ strategy: strategyMock,
6605+ depositSharesToWithdraw: withdrawalAmount
6606+ });
6607+ cheats.prank (defaultStaker);
6608+ delegationManager.queueWithdrawals (queuedWithdrawalParams);
6609+
6610+ // Warp to just before the MIN_WITHDRAWAL_DELAY_BLOCKS
6611+ cheats.roll (withdrawal.startBlock + delegationManager.minWithdrawalDelayBlocks ());
6612+
6613+ // Slash all of operator's shares
6614+ _setOperatorMagnitude (defaultOperator, strategyMock, 0 );
6615+ cheats.prank (address (allocationManagerMock));
6616+ delegationManager.burnOperatorShares (defaultOperator, strategyMock, WAD, 0 );
6617+
6618+ // Complete withdrawal as tokens and assert that nothing is returned
6619+ cheats.roll (block .number + 1 );
6620+ IERC20 [] memory tokens = strategyMock.underlyingToken ().toArray ();
6621+ cheats.expectCall (
6622+ address (strategyManagerMock),
6623+ abi.encodeWithSelector (
6624+ IShareManager.withdrawSharesAsTokens.selector ,
6625+ defaultStaker,
6626+ strategyMock,
6627+ strategyMock.underlyingToken (),
6628+ 0
6629+ )
6630+ );
6631+ cheats.prank (defaultStaker);
6632+ delegationManager.completeQueuedWithdrawal (withdrawal, tokens, true );
6633+ }
6634+
6635+ /// @notice Verifies that shares are NOT burnable for a withdrawal queued just before the MIN_WITHDRAWAL_DELAY_BLOCKS
6636+ function test_sharesNotBurnableWhenWithdrawalCompletable () public {
6637+ // Register operator
6638+ _registerOperatorWithBaseDetails (defaultOperator);
6639+ _setOperatorMagnitude (defaultOperator, strategyMock, WAD);
6640+
6641+ // Set the staker deposits in the strategies
6642+ uint256 depositAmount = 100e18 ;
6643+ strategyManagerMock.addDeposit (defaultStaker, strategyMock, depositAmount);
6644+ _delegateToOperatorWhoAcceptsAllStakers (defaultStaker, defaultOperator);
6645+
6646+ // Queue withdrawal
6647+ uint256 withdrawalAmount = depositAmount;
6648+ (
6649+ QueuedWithdrawalParams[] memory queuedWithdrawalParams ,
6650+ Withdrawal memory withdrawal ,
6651+ bytes32 withdrawalRoot
6652+ ) = _setUpQueueWithdrawalsSingleStrat ({
6653+ staker: defaultStaker,
6654+ withdrawer: defaultStaker,
6655+ strategy: strategyMock,
6656+ depositSharesToWithdraw: withdrawalAmount
6657+ });
6658+ cheats.prank (defaultStaker);
6659+ delegationManager.queueWithdrawals (queuedWithdrawalParams);
6660+
6661+ // Warp to completion time
6662+ cheats.roll (withdrawal.startBlock + delegationManager.minWithdrawalDelayBlocks () + 1 );
6663+ uint256 slashableShares = delegationManager.getSlashableSharesInQueue (defaultOperator, strategyMock);
6664+ assertEq (slashableShares, 0 , "shares should not be slashable " );
6665+
6666+ // Slash all of operator's shares
6667+ _setOperatorMagnitude (defaultOperator, strategyMock, 0 );
6668+ cheats.prank (address (allocationManagerMock));
6669+ delegationManager.burnOperatorShares (defaultOperator, strategyMock, WAD, 0 );
6670+
6671+ // Complete withdrawal as tokens and assert that we call back into teh SM with 100 tokens
6672+ IERC20 [] memory tokens = strategyMock.underlyingToken ().toArray ();
6673+ cheats.expectCall (
6674+ address (strategyManagerMock),
6675+ abi.encodeWithSelector (
6676+ IShareManager.withdrawSharesAsTokens.selector ,
6677+ defaultStaker,
6678+ strategyMock,
6679+ strategyMock.underlyingToken (),
6680+ 100e18
6681+ )
6682+ );
6683+ cheats.prank (defaultStaker);
6684+ delegationManager.completeQueuedWithdrawal (withdrawal, tokens, true );
6685+ }
6686+
6687+ /**
6688+ * @notice Queues 5 withdrawals at different blocks. Then, warps such that the first 2 are completable. Validates the slashable shares
6689+ */
6690+ function test_slashableSharesInQueue () public {
6691+ // Register operator
6692+ _registerOperatorWithBaseDetails (defaultOperator);
6693+ _setOperatorMagnitude (defaultOperator, strategyMock, WAD);
6694+
6695+ // Set the staker deposits in the strategies
6696+ uint256 depositAmount = 120e18 ;
6697+ strategyManagerMock.addDeposit (defaultStaker, strategyMock, depositAmount);
6698+ _delegateToOperatorWhoAcceptsAllStakers (defaultStaker, defaultOperator);
6699+
6700+ // Queue 5 withdrawals
6701+ uint256 startBlock = block .number ;
6702+ uint256 withdrawalAmount = depositAmount / 6 ;
6703+ for (uint256 i = 0 ; i < 5 ; i++ ) {
6704+ (
6705+ QueuedWithdrawalParams[] memory queuedWithdrawalParams ,
6706+ Withdrawal memory withdrawal ,
6707+ bytes32 withdrawalRoot
6708+ ) = _setUpQueueWithdrawalsSingleStrat ({
6709+ staker: defaultStaker,
6710+ withdrawer: defaultStaker,
6711+ strategy: strategyMock,
6712+ depositSharesToWithdraw: withdrawalAmount
6713+ });
6714+ cheats.prank (defaultStaker);
6715+ delegationManager.queueWithdrawals (queuedWithdrawalParams);
6716+ cheats.roll (startBlock + i + 1 );
6717+ }
6718+
6719+ // Warp to completion time for the first 2 withdrawals
6720+ // First withdrawal queued at startBlock. Second queued at startBlock + 1
6721+ cheats.roll (startBlock + 1 + delegationManager.minWithdrawalDelayBlocks () + 1 );
6722+
6723+ // Get slashable shares
6724+ uint256 slashableSharesInQueue = delegationManager.getSlashableSharesInQueue (defaultOperator, strategyMock);
6725+ assertEq (slashableSharesInQueue, depositAmount/ 6 * 3 , "slashable shares in queue should be 3/6 of the deposit amount " );
6726+
6727+ // Slash all of operator's shares
6728+ _setOperatorMagnitude (defaultOperator, strategyMock, 0 );
6729+ cheats.prank (address (allocationManagerMock));
6730+ cheats.expectEmit (true , true , true , true , address (delegationManager));
6731+ emit OperatorSharesDecreased (
6732+ defaultOperator,
6733+ address (0 ),
6734+ strategyMock,
6735+ depositAmount / 6 // 1 withdrawal not queued so decreased
6736+ );
6737+ cheats.expectEmit (true , true , true , true , address (delegationManager));
6738+ emit OperatorSharesBurned (
6739+ defaultOperator,
6740+ strategyMock,
6741+ depositAmount / 6 * 4 // 4 parts are burned
6742+ );
6743+ delegationManager.burnOperatorShares (defaultOperator, strategyMock, WAD, 0 );
6744+
6745+ // Assert slashable shares
6746+ slashableSharesInQueue = delegationManager.getSlashableSharesInQueue (defaultOperator, strategyMock);
6747+ assertEq (slashableSharesInQueue, 0 );
6748+ }
6749+
65426750 /**
65436751 * @notice Verifies that `DelegationManager.burnOperatorShares` properly decreases the delegated `shares` that the operator
65446752 * who the `defaultStaker` is delegated to has in the strategies
0 commit comments