Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 (_hasNonZeroScalingFactors(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(_hasNonZeroScalingFactors(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(_hasNonZeroScalingFactors(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 _hasNonZeroScalingFactors(
uint64 maxMagnitude,
StakerScalingFactors memory ssf
) internal view returns (bool) {
return maxMagnitude != 0 && (!ssf.isBeaconChainScalingFactorSet || ssf.beaconChainScalingFactor != 0);
}

/**
*
* SHARES CONVERSION FUNCTIONS
Expand Down
11 changes: 11 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add to documentation somewhere that:

  1. A staker can always redelegate for any strategy if the maxMagnitude is 0
  2. A staker can never delegate for beacon chain strategy if they are fully slashed on the beacon chain.

error FullySlashed();

/// Signatures

/// @dev Thrown when attempting to spend a spent eip-712 salt.
Expand Down Expand Up @@ -262,6 +268,8 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele
* or their delegationApprover is the `msg.sender`, then approval is assumed.
* @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
* @dev If the staker delegating has shares in a strategy that the operator was slashed 100% for (the operator's maxMagnitude = 0),
* then delegation is blocked and will revert.
*/
function delegateTo(
address operator,
Expand All @@ -285,6 +293,8 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele
* @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
* @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
* @dev If the staker delegating has shares in a strategy that the operator was slashed 100% for (the operator's maxMagnitude = 0),
* then delegation is blocked and will revert.
*/
function delegateToBySignature(
address staker,
Expand Down Expand Up @@ -367,6 +377,7 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele
*
* @dev *If the staker is actively delegated*, then increases the `staker`'s delegated delegatedShares in `strategy`.
* Otherwise does nothing.
* @dev If the operator was slashed 100% for the strategy (the operator's maxMagnitude = 0), then increasing delegated shares is blocked and will revert.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function increaseDelegatedShares(
Expand Down
Loading
Loading