Skip to content

Commit

Permalink
feat: handling fully slashed scenarios
Browse files Browse the repository at this point in the history
  • Loading branch information
8sunyuan committed Oct 22, 2024
1 parent eec1ff1 commit 40b0396
Show file tree
Hide file tree
Showing 3 changed files with 469 additions and 17 deletions.
69 changes: 54 additions & 15 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -212,21 +212,36 @@ contract DelegationManager is

for (uint256 i = 0; i < strategies.length; i++) {
StakerScalingFactors storage ssf = stakerScalingFactor[staker][strategies[i]];
IStrategy[] memory singleStrategy = new IStrategy[](1);
uint256[] memory singleShares = new uint256[](1);
uint64[] memory singleMaxMagnitude = new uint64[](1);
singleStrategy[0] = strategies[i];
// TODO: this part is a bit gross, can we make it better?
singleShares[0] = depositedShares[i].toShares(ssf, maxMagnitudes[i]);
singleMaxMagnitude[0] = maxMagnitudes[i];

withdrawalRoots[i] = _removeSharesAndQueueWithdrawal({
staker: staker,
operator: operator,
strategies: singleStrategy,
sharesToWithdraw: singleShares,
maxMagnitudes: singleMaxMagnitude
});
// If the operator was not slashed 100% for the strategy and the staker has not been fully slashed
// for native restaking (if the strategy is beaconChainStrategy) then handle a normal queued withdrawal.
// Otherwise if the operator has been slashed 100% for the strategy, it implies
// the staker has no available shares to withdraw and we simply decrement their entire depositShares amount.
// Note the returned withdrawal root will be 0x0 in this scenario but is not actually a valid null root.
if (_verifyMaxMagnitudeAndScalingFactor(maxMagnitudes[i], ssf)) {
IStrategy[] memory singleStrategy = new IStrategy[](1);
uint256[] memory singleShares = new uint256[](1);
uint64[] memory singleMaxMagnitude = new uint64[](1);
singleStrategy[0] = strategies[i];
// TODO: this part is a bit gross, can we make it better?
singleShares[0] = depositedShares[i].toShares(ssf, maxMagnitudes[i]);
singleMaxMagnitude[0] = maxMagnitudes[i];

withdrawalRoots[i] = _removeSharesAndQueueWithdrawal({
staker: staker,
operator: operator,
strategies: singleStrategy,
sharesToWithdraw: singleShares,
maxMagnitudes: singleMaxMagnitude
});
} else {
IShareManager shareManager = _getShareManager(strategies[i]);

// Remove active shares from EigenPodManager/StrategyManager
// This is to ensure that all shares are removed entirely and cannot be withdrawn
// or redelegated.
shareManager.removeDepositShares(staker, strategies[i], depositedShares[i]);
}

// all shares are queued withdrawn with no delegated operator, so
// reset staker's depositScalingFactor back to WAD default.
Expand Down Expand Up @@ -550,12 +565,17 @@ contract DelegationManager is
uint256 addedShares,
uint64 maxMagnitude
) internal {
StakerScalingFactors storage ssf = stakerScalingFactor[staker][strategy];

// Ensure that the operator has not been fully slashed for a strategy
// and that the staker has not been fully slashed if its the beaconChainStrategy
require(_verifyMaxMagnitudeAndScalingFactor(maxMagnitude, ssf), FullySlashed());

// Increment operator shares
operatorShares[operator][strategy] += addedShares;
emit OperatorSharesIncreased(operator, staker, strategy, addedShares);

// update the staker's depositScalingFactor
StakerScalingFactors storage ssf = stakerScalingFactor[staker][strategy];
ssf.updateDepositScalingFactor(existingDepositShares, addedShares, maxMagnitude);
emit DepositScalingFactorUpdated(staker, strategy, ssf.depositScalingFactor);
}
Expand Down Expand Up @@ -617,6 +637,10 @@ contract DelegationManager is
IShareManager shareManager = _getShareManager(strategies[i]);
StakerScalingFactors memory ssf = stakerScalingFactor[staker][strategies[i]];

// Ensure that the operator has not been fully slashed for a strategy
// and that the staker has not been slashed fully if its the beaconChainStrategy
require(_verifyMaxMagnitudeAndScalingFactor(maxMagnitudes[i], ssf), FullySlashed());

// Calculate the deposit shares
uint256 depositSharesToRemove = sharesToWithdraw[i].toDepositShares(ssf, maxMagnitudes[i]);
uint256 depositSharesWithdrawable = shareManager.stakerDepositShares(staker, strategies[i]);
Expand Down Expand Up @@ -662,6 +686,21 @@ contract DelegationManager is
return withdrawalRoot;
}

/**
* @notice We want to avoid divide by 0 situations so if an operator's maxMagnitude is 0 (operator slashed 100% for a strategy)
* then we return false. Additionally, if the staker's beaconChainScalingFactor is 0 for the beaconChain strategy, then we return false.
* @param maxMagnitude The maxMagnitude of the operator for a given strategy
* @param ssf The staker's scaling factors for a given strategy, particularly we care about their beaconChainScalingFactor
* if this is the beaconChain strategy
* @return bool True if the maxMagnitude is not 0 and the staker's beaconChainScalingFactor is not 0
*/
function _verifyMaxMagnitudeAndScalingFactor(
uint64 maxMagnitude,
StakerScalingFactors memory ssf
) internal view returns (bool) {
return maxMagnitude != 0 && (!ssf.isBeaconChainScalingFactorSet || ssf.beaconChainScalingFactor != 0);
}

/**
*
* SHARES CONVERSION FUNCTIONS
Expand Down
6 changes: 6 additions & 0 deletions src/contracts/interfaces/IDelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ interface IDelegationManagerErrors {
/// @dev Thrown when provided delay exceeds maximum.
error WithdrawalDelayExceedsMax();

/// Slashing

/// @dev Thrown when an operator has been fully slashed(maxMagnitude is 0) for a strategy.
/// or if the staker has had been natively slashed to the point of their beaconChainScalingFactor equalling 0.
error FullySlashed();

/// Signatures

/// @dev Thrown when attempting to spend a spent eip-712 salt.
Expand Down
Loading

0 comments on commit 40b0396

Please sign in to comment.