diff --git a/src/contracts/interfaces/IEigenPod.sol b/src/contracts/interfaces/IEigenPod.sol index 113e91177..638cda018 100644 --- a/src/contracts/interfaces/IEigenPod.sol +++ b/src/contracts/interfaces/IEigenPod.sol @@ -102,9 +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; - uint128 beaconChainBalanceAfter; } /** diff --git a/src/contracts/pods/EigenPod.sol b/src/contracts/pods/EigenPod.sol index 616482449..ddeef0bd1 100644 --- a/src/contracts/pods/EigenPod.sol +++ b/src/contracts/pods/EigenPod.sol @@ -189,7 +189,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC // If the proof shows the validator has a balance of 0, they are marked `WITHDRAWN`. // The assumption is that if this is the case, any withdrawn ETH was already in // the pod when `startCheckpoint` was originally called. - (int128 balanceDeltaGwei, uint64 exitedBalanceGwei) = _verifyCheckpointProof({ + (uint64 prevBalanceGwei, int64 balanceDeltaGwei, uint64 exitedBalanceGwei) = _verifyCheckpointProof({ validatorInfo: validatorInfo, checkpointTimestamp: checkpointTimestamp, balanceContainerRoot: balanceContainerProof.balanceContainerRoot, @@ -197,6 +197,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC }); checkpoint.proofsRemaining--; + checkpoint.beaconChainBalanceBeforeGwei += prevBalanceGwei; checkpoint.balanceDeltasGwei += balanceDeltaGwei; exitedBalancesGwei += exitedBalanceGwei; @@ -260,6 +261,10 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC ); } + if (currentCheckpointTimestamp != 0) { + _currentCheckpoint.beaconChainBalanceBeforeGwei += uint64(totalAmountToBeRestakedWei / GWEI_TO_WEI); + } + // Update the EigenPodManager on this pod's new balance eigenPodManager.recordBeaconChainETHBalanceUpdate(podOwner, int256(totalAmountToBeRestakedWei), 0); // no decrease } @@ -493,8 +498,6 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC uint64 lastCheckpointedAt = lastCheckpointTimestamp; if (currentCheckpointTimestamp != 0) { lastCheckpointedAt = currentCheckpointTimestamp; - _currentCheckpoint.beaconChainBalanceBefore += uint128(restakedBalanceGwei); - _currentCheckpoint.beaconChainBalanceAfter += uint128(restakedBalanceGwei); } // Proofs complete - create the validator in state @@ -515,11 +518,11 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC uint64 checkpointTimestamp, bytes32 balanceContainerRoot, BeaconChainProofs.BalanceProof calldata proof - ) internal returns (int128 balanceDeltaGwei, uint64 exitedBalanceGwei) { + ) internal returns (uint64 prevBalanceGwei, int64 balanceDeltaGwei, uint64 exitedBalanceGwei) { uint40 validatorIndex = uint40(validatorInfo.validatorIndex); // Verify validator balance against `balanceContainerRoot` - uint64 prevBalanceGwei = validatorInfo.restakedBalanceGwei; + prevBalanceGwei = validatorInfo.restakedBalanceGwei; uint64 newBalanceGwei = BeaconChainProofs.verifyValidatorBalance({ balanceContainerRoot: balanceContainerRoot, validatorIndex: validatorIndex, @@ -534,10 +537,6 @@ 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); } @@ -550,12 +549,12 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC validatorInfo.status = VALIDATOR_STATUS.WITHDRAWN; // If we reach this point, `balanceDeltaGwei` should always be negative, // so this should be a safe conversion - exitedBalanceGwei = uint64(uint128(-balanceDeltaGwei)); + exitedBalanceGwei = uint64(-balanceDeltaGwei); emit ValidatorWithdrawn(checkpointTimestamp, validatorIndex); } - return (balanceDeltaGwei, exitedBalanceGwei); + return (prevBalanceGwei, balanceDeltaGwei, exitedBalanceGwei); } /** @@ -607,8 +606,7 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC proofsRemaining: uint24(activeValidatorCount), podBalanceGwei: podBalanceGwei, balanceDeltasGwei: 0, - beaconChainBalanceBefore: 0, - beaconChainBalanceAfter: 0 + beaconChainBalanceBeforeGwei: 0 }); // Place checkpoint in storage. If `proofsRemaining` is 0, the checkpoint @@ -646,8 +644,8 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC // Calculate the slashing proportion uint64 proportionOfOldBalance = 0; if (totalShareDeltaWei < 0) { - uint256 totalBefore = withdrawableRestakedExecutionLayerGwei + checkpoint.beaconChainBalanceBefore; - proportionOfOldBalance = uint64((totalBefore + uint256(-totalShareDeltaWei)) * WAD / totalBefore); + uint256 totalRestakedBeforeWei = (withdrawableRestakedExecutionLayerGwei + checkpoint.beaconChainBalanceBeforeGwei) * GWEI_TO_WEI; + proportionOfOldBalance = uint64((totalRestakedBeforeWei + uint256(-totalShareDeltaWei)) * WAD / totalRestakedBeforeWei); } // Update pod owner's shares @@ -675,8 +673,8 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC } /// @dev Calculates the delta between two Gwei amounts and returns as an int256 - function _calcBalanceDelta(uint64 newAmountGwei, uint64 previousAmountGwei) internal pure returns (int128) { - return int128(uint128(newAmountGwei)) - int128(uint128(previousAmountGwei)); + function _calcBalanceDelta(uint64 newAmountGwei, uint64 previousAmountGwei) internal pure returns (int64) { + return int64(newAmountGwei) - int64(previousAmountGwei); } /** diff --git a/src/contracts/pods/EigenPodStorage.sol b/src/contracts/pods/EigenPodStorage.sol index 1fca0d189..aeecaf777 100644 --- a/src/contracts/pods/EigenPodStorage.sol +++ b/src/contracts/pods/EigenPodStorage.sol @@ -68,7 +68,7 @@ abstract contract EigenPodStorage is IEigenPod { mapping(uint64 => uint64) public checkpointBalanceExitedGwei; /// @notice The current checkpoint, if there is one active - Checkpoint internal _currentCheckpoint; // TODO: this storage is fucked need split structs + Checkpoint internal _currentCheckpoint; /// @notice An address with permissions to call `startCheckpoint` and `verifyWithdrawalCredentials`, set /// by the podOwner. This role exists to allow a podOwner to designate a hot wallet that can call @@ -76,10 +76,13 @@ abstract contract EigenPodStorage is IEigenPod { /// @dev If this address is NOT set, only the podOwner can call `startCheckpoint` and `verifyWithdrawalCredentials` address public proofSubmitter; + /// @notice The total balance of the pod before the current checkpoint + uint128 public beaconChainBalanceBeforeCurrentCheckpoint; + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[35] private __gap; // Reduced the gap size by 1 to accommodate the new variable + uint256[35] private __gap; }