Skip to content

Commit

Permalink
reimpl
Browse files Browse the repository at this point in the history
  • Loading branch information
gpsanant committed Sep 20, 2024
1 parent 85a3840 commit 48d36d9
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 134 deletions.
120 changes: 80 additions & 40 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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 @@ -398,37 +403,43 @@ contract DelegationManager is
}
}

// IGNORE THIS FUNCTION
/**
* @notice Decreases a staker's delegated stakeShares 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 removedShares 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 existingDepositShares The number of deposit shares the staker already has in the EPM. This does not change upon decreasing shares.
* @param proportionPodBalanceDecrease The proportion of the staker's shares to decrease. This is a fraction of the staker's shares in the strategy.
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated stakeShares 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,
uint256 removedShares
) external onlyStrategyManagerOrEigenPodManager {
// if the staker is delegated to an operator
// if (isDelegated(staker)) {
// address operator = delegatedTo[staker];

// uint64 totalMagnitude = allocationManager.getTotalMagnitude(operator, strategy);

// // subtract strategy shares from delegated scaled shares
// _decreaseOperatorScaledShares({
// operator: operator,
// staker: staker,
// strategy: strategy,
// shares: removedShares,
// totalMagnitude: totalMagnitude
// });
// }
function decreaseBeaconChainScalingFactor(
address staker,
uint256 existingDepositShares,
uint256 proportionPodBalanceDecrease
) external onlyEigenPodManager {
uint256 stakeSharesBefore = _convertDepositSharesToStakeShares(staker, beaconChainETHStrategy, existingDepositShares);

// decrease the staker's beaconChainScalingFactor proportionally
beaconChainScalingFactors[staker] =
beaconChainScalingFactors[staker]
* (PRECISION_FACTOR - proportionPodBalanceDecrease)
/ PRECISION_FACTOR;

uint256 stakeSharesAfter = _convertDepositSharesToStakeShares(staker, beaconChainETHStrategy, existingDepositShares);

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

// subtract strategy shares from delegated scaled shares
_decreaseOperatorStakeShares({
operator: operator,
staker: staker,
strategy: beaconChainETHStrategy,
stakeShares: stakeSharesBefore - stakeSharesAfter
});
}
}

/**
Expand Down Expand Up @@ -612,18 +623,15 @@ contract DelegationManager is
* @param operator The operator to decrease the delegated delegated shares for
* @param staker The staker to decrease the delegated delegated shares for
* @param strategy The strategy to decrease the delegated delegated shares for
* @param shares The shares removed from the staker in the StrategyManager/EigenPodManager
* @param totalMagnitude The current total magnitude of the operator for the strategy
* @param stakeShares The stakeShares to be removed from the operator's total delegated
*/
function _decreaseOperatorStakeShares(
address operator,
address staker,
IStrategy strategy,
uint256 shares,
uint64 totalMagnitude
uint256 stakeShares
) internal {
// based on total magnitude, decrement operator's stakeShares
uint256 stakeShares = _convertSharesToStakeShares(shares, totalMagnitude);
operatorStakeShares[operator][strategy] -= stakeShares;
// TODO: What to do about event wrt scaling?
emit OperatorSharesDecreased(operator, staker, strategy, stakeShares);
Expand Down Expand Up @@ -670,8 +678,7 @@ contract DelegationManager is
operator: operator,
staker: staker,
strategy: strategies[i],
shares: sharesToWithdraw[i],
totalMagnitude: totalMagnitudes[i]
stakeShares: stakeSharesToWithdraw[i]
});
}

Expand Down Expand Up @@ -748,9 +755,11 @@ contract DelegationManager is
* 3. depositShares
* - These can be converted into stakeShares given a staker and a strategy
* - by multiplying by the staker's depositScalingFactor for the strategy
* - additionally, if the strategy is beaconChainETHStrategy, then multiplying by the staker's beaconChainScalingFactor
* - These values automatically update their conversion into tokens
* - when the staker's depositScalingFactor for the strategy is increased upon new deposits
* - or when the staker's operator's total magnitude for the strategy is decreased upon slashing
* - or when the staker's beaconChainScalingFactor is decreased upon beacon chain slashing
* - These represent the total amount of shares the staker would have of a strategy if they were never slashed
* - These live in the storage of the StrategyManager/EigenPodManager
* - `stakerStrategyShares` in the SM is the staker's depositShares that have not been queued for withdrawal in a strategy
Expand All @@ -766,11 +775,35 @@ contract DelegationManager is
}

function _convertSharesToDepositShares(address staker, IStrategy strategy, uint256 shares, uint64 totalMagnitude) internal view returns (uint256) {
return shares * PRECISION_FACTOR / _getDepositScalingFactor(staker, strategy) * PRECISION_FACTOR / totalMagnitude;
// scale by depositScalingFactor first
uint256 depositShares = shares * PRECISION_FACTOR / _getDepositScalingFactor(staker, strategy);
// scale by beaconChainScalingFactor if beaconChainETHStrategy
if(strategy == beaconChainETHStrategy) {
depositShares = depositShares * _getBeaconChainScalingFactor(staker) / PRECISION_FACTOR;
}
// finally scale by totalMagnitude
return depositShares * PRECISION_FACTOR / totalMagnitude;
}

function _convertDepositSharesToShares(address staker, IStrategy strategy, uint256 depositShares, uint64 totalMagnitude) internal view returns (uint256) {
return depositShares * _getDepositScalingFactor(staker, strategy) / PRECISION_FACTOR * totalMagnitude / PRECISION_FACTOR;
// scale by depositScalingFactor first
uint256 shares = depositShares * _getDepositScalingFactor(staker, strategy) / PRECISION_FACTOR;
// scale by beaconChainScalingFactor if beaconChainETHStrategy
if(strategy == beaconChainETHStrategy) {
shares = shares * PRECISION_FACTOR / _getBeaconChainScalingFactor(staker);
}
// finally scale by totalMagnitude
return shares * totalMagnitude / PRECISION_FACTOR;
}

function _convertDepositSharesToStakeShares(address staker, IStrategy strategy, uint256 depositShares) internal view returns (uint256) {
// scale by depositScalingFactor first
uint256 stakeShares = depositShares * PRECISION_FACTOR / _getDepositScalingFactor(staker, strategy);
// scale by beaconChainScalingFactor if beaconChainETHStrategy
if(strategy == beaconChainETHStrategy) {
stakeShares = stakeShares * _getBeaconChainScalingFactor(staker) / PRECISION_FACTOR;
}
return stakeShares;
}

/**
Expand Down Expand Up @@ -823,9 +856,7 @@ contract DelegationManager is
depositScalingFactors[staker][strategy] = newDepositScalingFactor;
}

/**
* @notice depositScalingFactor should be initialized and lower bounded to 1e18
*/
/// @notice returns the depositScalingFactor for a staker or PRECISION_FACTOR if it is not set
function _getDepositScalingFactor(address staker, IStrategy strategy) internal view returns (uint256) {
uint256 currDepositScalingFactor = depositScalingFactors[staker][strategy];
if (currDepositScalingFactor == 0) {
Expand All @@ -834,6 +865,15 @@ contract DelegationManager is
return currDepositScalingFactor;
}

/// @notice returns the beaconChainScalingFactor factor for a staker or PRECISION_FACTOR if it is not set
function _getBeaconChainScalingFactor(address staker) internal view returns (uint256) {
uint256 currBeaconChainScalingFactor = beaconChainScalingFactors[staker];
if (currBeaconChainScalingFactor == 0) {
currBeaconChainScalingFactor = PRECISION_FACTOR;
}
return currBeaconChainScalingFactor;
}

function _getShareManager(IStrategy strategy) internal view returns (IShareManager) {
return strategy == beaconChainETHStrategy ? IShareManager(address(eigenPodManager)) : IShareManager(address(strategyManager));
}
Expand Down
8 changes: 6 additions & 2 deletions src/contracts/core/DelegationManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,14 @@ 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.
/// @notice Mapping: staker => strategy => scaling factor used to calculate the staker's shares in the strategy
/// This is updated upon each deposit of a delegated staker.
mapping(address => mapping(IStrategy => uint256)) public depositScalingFactors;

/// @notice Mapping: staker => their beacon chain scaling factor used to calculate the staker's withdrawable shares in the strategy.
/// This is updated upon each ending checkpoints that include beacon chain slashing.
mapping(address => uint256) public beaconChainScalingFactors;

constructor(
IStrategyManager _strategyManager,
ISlasher _slasher,
Expand Down
16 changes: 8 additions & 8 deletions src/contracts/interfaces/IDelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ interface IDelegationManager is ISignatureUtils {
error OperatorNotRegistered();
/// @dev Thrown when caller is neither the StrategyManager or EigenPodManager contract.
error OnlyStrategyManagerOrEigenPodManager();
/// @dev Thrown when caller is not the EigenPodManager contract.
error OnlyEigenPodManager();
/// @dev Thrown when an operator attempts to undelegate.
error OperatorsCannotUndelegate();
/// @dev Thrown when an account is not actively delegated.
Expand Down Expand Up @@ -337,18 +339,16 @@ interface IDelegationManager is ISignatureUtils {
) 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.
* @notice Decreases a native restaker's delegated share balance in a strategy due to beacon chain slashing. This updates their beaconChainScalingFactor.
* Their operator's scaled shares are also updated (if they are delegated).
* @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 removedShares The number of shares to decremented for the strategy in the
* StrategyManager/EigenPodManager
* @param shares The number of shares of beaconChainETHStrategy the staker has, not including scaling factors or
* @param proportionPodBalanceDecrease The proportion of the staker's shares to decrease. This is a fraction of the staker's shares in the strategy.
*
* @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 Callable only by the EigenPodManager.
*/
function decreaseDelegatedShares(address staker, IStrategy strategy, uint256 removedShares) external;
function decreaseBeaconChainScalingFactor(address staker, uint256 shares, uint256 proportionPodBalanceDecrease) external;

/**
* @notice returns the address of the operator that `staker` is delegated to.
Expand Down
2 changes: 2 additions & 0 deletions src/contracts/interfaces/IEigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ interface IEigenPod {
uint24 proofsRemaining;
uint64 podBalanceGwei;
int128 balanceDeltasGwei;
uint128 beaconChainBalanceBefore;
uint128 beaconChainBalanceAfter;
}

/**
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 PRECISION_FACTOR) 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,
uint256 proportionPodBalanceDecrease
) external;

/// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
function ownerToPod(
Expand Down
31 changes: 26 additions & 5 deletions src/contracts/pods/EigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
}

// Update the EigenPodManager on this pod's new balance
eigenPodManager.recordBeaconChainETHBalanceUpdate(podOwner, int256(totalAmountToBeRestakedWei));
eigenPodManager.recordBeaconChainETHBalanceUpdate(podOwner, int256(totalAmountToBeRestakedWei), 0); // no decrease
}

/**
Expand Down Expand Up @@ -492,8 +492,12 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
// purpose of `lastCheckpointedAt` is to enforce that newly-verified validators are not
// eligible to progress already-existing checkpoints - however in this case, no checkpoints exist.
activeValidatorCount++;
uint64 lastCheckpointedAt =
currentCheckpointTimestamp == 0 ? lastCheckpointTimestamp : currentCheckpointTimestamp;
uint64 lastCheckpointedAt = lastCheckpointTimestamp;
if (currentCheckpointTimestamp != 0) {
lastCheckpointedAt = currentCheckpointTimestamp;
_currentCheckpoint.beaconChainBalanceBefore += uint128(restakedBalanceGwei);
_currentCheckpoint.beaconChainBalanceAfter += uint128(restakedBalanceGwei);
}

// Proofs complete - create the validator in state
_validatorPubkeyHashToInfo[pubkeyHash] = ValidatorInfo({
Expand Down Expand Up @@ -532,6 +536,10 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
previousAmountGwei: prevBalanceGwei
});

// Update the checkpoint values
_currentCheckpoint.beaconChainBalanceBefore += uint128(prevBalanceGwei);
_currentCheckpoint.beaconChainBalanceAfter += uint128(newBalanceGwei);

emit ValidatorBalanceUpdated(validatorIndex, checkpointTimestamp, newBalanceGwei);
}

Expand Down Expand Up @@ -600,7 +608,9 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
beaconBlockRoot: getParentBlockRoot(uint64(block.timestamp)),
proofsRemaining: uint24(activeValidatorCount),
podBalanceGwei: podBalanceGwei,
balanceDeltasGwei: 0
balanceDeltasGwei: 0,
beaconChainBalanceBefore: 0,
beaconChainBalanceAfter: 0
});

// Place checkpoint in storage. If `proofsRemaining` is 0, the checkpoint
Expand Down Expand Up @@ -635,8 +645,19 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
delete currentCheckpointTimestamp;
delete _currentCheckpoint;

// Calculate the slashing proportion
uint256 proportionPodBalanceDecrease = 0;
if (checkpoint.balanceDeltasGwei < 0) {
uint256 totalBefore = withdrawableRestakedExecutionLayerGwei + checkpoint.beaconChainBalanceBefore;
proportionPodBalanceDecrease = uint256(uint128(-checkpoint.balanceDeltasGwei)) * 1e18 / totalBefore;
}

// Update pod owner's shares
eigenPodManager.recordBeaconChainETHBalanceUpdate(podOwner, totalShareDeltaWei);
eigenPodManager.recordBeaconChainETHBalanceUpdate(
podOwner,
totalShareDeltaWei,
proportionPodBalanceDecrease
);
emit CheckpointFinalized(lastCheckpointTimestamp, totalShareDeltaWei);
} else {
_currentCheckpoint = checkpoint;
Expand Down
Loading

0 comments on commit 48d36d9

Please sign in to comment.