Skip to content

Commit

Permalink
eigenpods slashing updates (#745)
Browse files Browse the repository at this point in the history
* squash yet again

* change again

* update checkpoint struct
  • Loading branch information
gpsanant authored Sep 30, 2024
1 parent 0b22fc9 commit c9dfe56
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 198 deletions.
89 changes: 51 additions & 38 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ contract DelegationManager is
_;
}

modifier onlyEigenPodManager() {
require(msg.sender == address(eigenPodManager), OnlyEigenPodManager());
_;
}

Check notice

Code scanning / Slither

Incorrect modifier Low

Modifier DelegationManager.onlyEigenPodManager() does not always execute _; or revert

/**
*
* INITIALIZING FUNCTIONS
Expand Down Expand Up @@ -283,7 +288,7 @@ contract DelegationManager is

// all shares and queued withdrawn and no delegated operator
// reset staker's depositScalingFactor to default
depositScalingFactors[staker][strategies[i]] = WAD;
stakerScalingFactors[staker][strategies[i]].depositScalingFactor = WAD;
}
}

Expand Down Expand Up @@ -400,37 +405,41 @@ contract DelegationManager is
}
}

// IGNORE THIS FUNCTION
/**
* @notice Decreases a staker's delegated delegatedShares for a strategy.
* @param staker The address to increase the delegated scaled shares for their operator.
* @param strategy The strategy in which to decrease the delegated scaled shares.
* @param removedOwnedShares The number of shares to decremented for the strategy in the
* StrategyManager/EigenPodManager
* @notice Decreases a native restaker's delegated share balance in a strategy due to beacon chain slashing. This updates their beaconChainScalingFactor.
* Their operator's stakeShares are also updated (if they are delegated).
* @param staker The address to increase the delegated stakeShares for their operator.
* @param existingShares The number of shares the staker already has in the EPM. This does not change upon decreasing shares.
* @param proportionOfOldBalance The current pod owner shares proportion of the previous pod owner shares
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated delegatedShares in `strategy` by `scaledShares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated stakeShares in `strategy` by `proportionPodBalanceDecrease` proportion. Otherwise does nothing.
* @dev Callable only by the EigenPodManager.
*/
function decreaseDelegatedShares(
function decreaseBeaconChainScalingFactor(
address staker,
IStrategy strategy,
OwnedShares removedOwnedShares
) external onlyStrategyManagerOrEigenPodManager {
// if the staker is delegated to an operator
// if (isDelegated(staker)) {
// address operator = delegatedTo[staker];
Shares existingShares,
uint64 proportionOfOldBalance
) external onlyEigenPodManager {
DelegatedShares delegatedSharesBefore = existingShares.toDelegatedShares(stakerScalingFactors[staker][beaconChainETHStrategy]);

// uint64 totalMagnitude = allocationManager.getTotalMagnitude(operator, strategy);
// decrease the staker's beaconChainScalingFactor proportionally
// forgefmt: disable-next-item
stakerScalingFactors[staker][beaconChainETHStrategy].decreaseBeaconChainScalingFactor(proportionOfOldBalance);

// // subtract strategy shares from delegated scaled shares
// _decreaseOperatorScaledShares({
// operator: operator,
// staker: staker,
// strategy: strategy,
// shares: removedOwnedShares,
// totalMagnitude: totalMagnitude
// });
// }
DelegatedShares delegatedSharesAfter = existingShares.toDelegatedShares(stakerScalingFactors[staker][beaconChainETHStrategy]);

// if the staker is delegated to an operators
if (isDelegated(staker)) {
address operator = delegatedTo[staker];

// subtract strategy shares from delegated scaled shares
_decreaseDelegation({
operator: operator,
staker: staker,
strategy: beaconChainETHStrategy,
delegatedShares: delegatedSharesBefore.sub(delegatedSharesAfter)
});
}
}

/**
Expand Down Expand Up @@ -553,9 +562,11 @@ contract DelegationManager is

for (uint256 i = 0; i < withdrawal.strategies.length; i++) {
IShareManager shareManager = _getShareManager(withdrawal.strategies[i]);

// forgefmt: disable-next-line
OwnedShares ownedSharesToWithdraw = withdrawal.delegatedShares[i].toOwnedShares(totalMagnitudes[i]);
OwnedShares ownedSharesToWithdraw =
withdrawal.delegatedShares[i]
.scaleForCompleteWithdrawal(stakerScalingFactors[withdrawal.staker][withdrawal.strategies[i]])
.toOwnedShares(totalMagnitudes[i]);

if (receiveAsTokens) {
// Withdraws `shares` in `strategy` to `withdrawer`. If the shares are virtual beaconChainETH shares,
// then a call is ultimately forwarded to the `staker`s EigenPod; otherwise a call is ultimately forwarded
Expand Down Expand Up @@ -659,9 +670,9 @@ contract DelegationManager is
IShareManager shareManager = _getShareManager(strategies[i]);

// delegatedShares for staker to place into queueWithdrawal
delegatedSharesToWithdraw[i] = ownedSharesToWithdraw[i].toDelegatedShares(totalMagnitudes[i]);

Shares sharesToWithdraw = delegatedSharesToWithdraw[i].toShares(depositScalingFactors[staker][strategies[i]]);
DelegatedShares delegatedSharesToRemove = ownedSharesToWithdraw[i].toDelegatedShares(totalMagnitudes[i]);
// TODO: should this include beaconChainScalingFactor?
Shares sharesToWithdraw = delegatedSharesToRemove.toShares(stakerScalingFactors[staker][strategies[i]]);
// TODO: maybe have a getter to get shares for all strategies, like getDelegatableShares
// check sharesToWithdraw is valid
// but for inputted strategies
Expand All @@ -675,9 +686,10 @@ contract DelegationManager is
operator: operator,
staker: staker,
strategy: strategies[i],
delegatedShares: delegatedSharesToWithdraw[i]
delegatedShares: delegatedSharesToRemove
});
}
delegatedSharesToWithdraw[i] = delegatedSharesToRemove.scaleForQueueWithdrawal(stakerScalingFactors[staker][strategies[i]]);

// Remove active shares from EigenPodManager/StrategyManager
// EigenPodManager: this call will revert if it would reduce the Staker's virtual beacon chain ETH shares below zero
Expand Down Expand Up @@ -758,25 +770,26 @@ contract DelegationManager is
//
// newShares
// = newPrincipalShares.toDelegatedShares(stakerScalingFactors[staker][strategy).toOwnedShares(totalMagnitude)
// = newPrincipalShares * newDepositScalingFactor / WAD * totalMagnitude / WAD
// = (existingPrincipalShares + addedShares) * newDepositScalingFactor / WAD * totalMagnitude / WAD
// = newPrincipalShares * newDepositScalingFactor / WAD * beaonChainScalingFactor / WAD * totalMagnitude / WAD
// = (existingPrincipalShares + addedShares) * newDepositScalingFactor / WAD * beaonChainScalingFactor / WAD * totalMagnitude / WAD
//
// we can solve for
//
OwnedShares existingOwnedShares =
existingShares
.toDelegatedShares(depositScalingFactors[staker][strategy])
.toDelegatedShares(stakerScalingFactors[staker][strategy])
.toOwnedShares(totalMagnitude);
newDepositScalingFactor =
existingOwnedShares
.add(addedOwnedShares)
.unwrap()
.divWad(existingShares.unwrap() + addedOwnedShares.unwrap())
.divWad(stakerScalingFactors[staker][strategy].getBeaconChainScalingFactor())
.divWad(totalMagnitude);
}

// update the staker's depositScalingFactor
depositScalingFactors[staker][strategy] = newDepositScalingFactor;
stakerScalingFactors[staker][strategy].depositScalingFactor = newDepositScalingFactor;
}

function _getShareManager(
Expand Down Expand Up @@ -885,7 +898,7 @@ contract DelegationManager is

// forgefmt: disable-next-item
ownedShares[i] = shares
.toDelegatedShares(depositScalingFactors[staker][strategies[i]])
.toDelegatedShares(stakerScalingFactors[staker][strategies[i]])
.toOwnedShares(totalMagnitude);
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/contracts/core/DelegationManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,13 @@ abstract contract DelegationManagerStorage is IDelegationManager {
*/
mapping(IStrategy => uint256) private __deprecated_strategyWithdrawalDelayBlocks;

/// @notice Mapping: staker => strategy => scaling factor used to calculate the staker's shares in the strategy.
/// This is updated upon each deposit based on the staker's currently delegated operator's totalMagnitude.
mapping(address => mapping(IStrategy => uint256)) public depositScalingFactors;
/// @notice Mapping: staker => strategy =>
/// (
/// scaling factor used to calculate the staker's shares in the strategy,
/// beacon chain scaling factor used to calculate the staker's withdrawable shares in the strategy.
/// )
/// Note that we don't need the beaconChainScalingFactor for non beaconChainETHStrategy strategies, but it's nicer syntactically to keep it.
mapping(address => mapping(IStrategy => StakerScalingFactors)) public stakerScalingFactors;

constructor(
IStrategyManager _strategyManager,
Expand Down
26 changes: 15 additions & 11 deletions src/contracts/interfaces/IDelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import "../libraries/SlashingLib.sol";
interface IDelegationManager is ISignatureUtils {
/// @dev Thrown when msg.sender is not allowed to call a function
error UnauthorizedCaller();
/// @dev Thrown when msg.sender is not the EigenPodManager
error OnlyEigenPodManager();

/// Delegation Status

Expand Down Expand Up @@ -362,19 +364,21 @@ interface IDelegationManager is ISignatureUtils {
OwnedShares addedOwnedShares
) external;

/**
* @notice Decreases a staker's delegated share balance in a strategy. Note that before removing from operator shares,
* the delegated shares are scaled according to the operator's total magnitude as part of slashing accounting. Unlike
* `increaseDelegatedShares`, the staker's depositScalingFactor is not updated here.
* @param staker The address to increase the delegated scaled shares for their operator.
* @param strategy The strategy in which to decrease the delegated scaled shares.
* @param removedOwnedShares The number of shares to decremented for the strategy in the
* StrategyManager/EigenPodManager
/**
* @notice Decreases a native restaker's delegated share balance in a strategy due to beacon chain slashing. This updates their beaconChainScalingFactor.
* Their operator's stakeShares are also updated (if they are delegated).
* @param staker The address to increase the delegated stakeShares for their operator.
* @param existingShares The number of shares the staker already has in the EPM. This does not change upon decreasing shares.
* @param proportionOfOldBalance The current pod owner shares proportion of the previous pod owner shares
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated scaled shares in `strategy` by `scaledShares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated stakeShares in `strategy` by `proportionPodBalanceDecrease` proportion. Otherwise does nothing.
* @dev Callable only by the EigenPodManager.
*/
function decreaseDelegatedShares(address staker, IStrategy strategy, OwnedShares removedOwnedShares) external;
function decreaseBeaconChainScalingFactor(
address staker,
Shares existingShares,
uint64 proportionOfOldBalance
) external;

/**
* @notice returns the address of the operator that `staker` is delegated to.
Expand Down
17 changes: 14 additions & 3 deletions src/contracts/interfaces/IEigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
* to account balances in terms of gwei in the EigenPod contract and convert to wei when making calls to other contracts
*/
interface IEigenPod {
/// @dev Thrown when msg.sender is not allowed to call a function
error UnauthorizedCaller();
/// @dev Thrown when msg.sender is not the EPM.
error OnlyEigenPodManager();
/// @dev Thrown when msg.sender is not the pod owner.
error OnlyEigenPodOwner();
/// @dev Thrown when msg.sender is not owner or the proof submitter.
error OnlyEigenPodOwnerOrProofSubmitter();
/// @dev Thrown when attempting an action that is currently paused.
error CurrentlyPaused();

Expand Down Expand Up @@ -98,7 +102,14 @@ interface IEigenPod {
bytes32 beaconBlockRoot;
uint24 proofsRemaining;
uint64 podBalanceGwei;
int128 balanceDeltasGwei;
// this used to be an int128 before the slashing release
// now it is an int64. (2^63 - 1) gwei * 1e-9 eth/gwei = 9_223_372_036.85 eth = 9 billion eth
int64 balanceDeltasGwei;
uint64 beaconChainBalanceBeforeGwei;
}

struct ExtendedCheckpoint {
uint128 beaconChainBalanceBefore;
}

/**
Expand Down
10 changes: 9 additions & 1 deletion src/contracts/interfaces/IEigenPodManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ interface IEigenPodManager is IShareManager, IPausable {
error SharesNegative();
/// @dev Thrown when the strategy is not the beaconChainETH strategy.
error InvalidStrategy();
/// @dev Thrown when the pods shares are negative and a beacon chain balance update is attempted.
/// The podOwner should complete legacy withdrawal first.
error LegacyWithdrawalsNotCompleted();

/// @notice Emitted to notify the deployment of an EigenPod
event PodDeployed(address indexed eigenPod, address indexed podOwner);
Expand Down Expand Up @@ -72,10 +75,15 @@ interface IEigenPodManager is IShareManager, IPausable {
* to ensure that delegated shares are also tracked correctly
* @param podOwner is the pod owner whose balance is being updated.
* @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares
* @param proportionPodBalanceDecrease is the proportion (of WAD) of the podOwner's balance that has changed
* @dev Callable only by the podOwner's EigenPod contract.
* @dev Reverts if `sharesDelta` is not a whole Gwei amount
*/
function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external;
function recordBeaconChainETHBalanceUpdate(
address podOwner,
int256 sharesDelta,
uint64 proportionPodBalanceDecrease
) external;

/// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
function ownerToPod(
Expand Down
Loading

0 comments on commit c9dfe56

Please sign in to comment.