Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: slashing integration todos #934

Closed
Closed
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
2 changes: 0 additions & 2 deletions src/contracts/interfaces/IEigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ interface IEigenPodErrors {

/// @dev Thrown when amount exceeds `restakedExecutionLayerGwei`.
error InsufficientWithdrawableBalance();
/// @dev Thrown when provided `amountGwei` is not a multiple of gwei.
error AmountMustBeMultipleOfGwei();

/// Validator Status

Expand Down
2 changes: 1 addition & 1 deletion src/contracts/pods/EigenPod.sol
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,8 @@ contract EigenPod is Initializable, ReentrancyGuardUpgradeable, EigenPodPausingC
* @dev Reverts if `amountWei` is not a whole Gwei amount
*/
function withdrawRestakedBeaconChainETH(address recipient, uint256 amountWei) external onlyEigenPodManager {
require(amountWei % GWEI_TO_WEI == 0, AmountMustBeMultipleOfGwei());
0xClandestine marked this conversation as resolved.
Show resolved Hide resolved
uint64 amountGwei = uint64(amountWei / GWEI_TO_WEI);
amountWei = amountGwei * GWEI_TO_WEI;
require(amountGwei <= restakedExecutionLayerGwei, InsufficientWithdrawableBalance());
restakedExecutionLayerGwei -= amountGwei;
emit RestakedBeaconChainETHWithdrawn(recipient, amountWei);
Expand Down
305 changes: 288 additions & 17 deletions src/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,7 @@ abstract contract IntegrationBase is IntegrationDeployer {
tokenBalance = strat.underlyingToken().balanceOf(address(user));
}

assertApproxEqAbs(expectedBalance, tokenBalance, 1, err);
// assertEq(expectedBalance, tokenBalance, err);
assertEq(expectedBalance, tokenBalance, err);
}
}

Expand Down Expand Up @@ -260,7 +259,7 @@ abstract contract IntegrationBase is IntegrationDeployer {
actualShares = strategyManager.stakerDepositShares(address(user), strat);
}

assertApproxEqAbs(expectedShares[i], actualShares, 1, err);
assertEq(expectedShares[i], actualShares, err);
}
}

Expand All @@ -271,11 +270,8 @@ abstract contract IntegrationBase is IntegrationDeployer {
string memory err
) internal view {
for (uint i = 0; i < strategies.length; i++) {
IStrategy strat = strategies[i];

uint actualShares = delegationManager.operatorShares(address(user), strat);

assertApproxEqAbs(expectedShares[i], actualShares, 1, err);
uint actualShares = delegationManager.operatorShares(address(user), strategies[i]);
assertEq(expectedShares[i], actualShares, err);
}
}

Expand Down Expand Up @@ -351,6 +347,224 @@ abstract contract IntegrationBase is IntegrationDeployer {
TIME TRAVELERS ONLY BEYOND THIS POINT
*******************************************************************************/

/*******************************************************************************
SNAPSHOT ASSERTIONS: ALLOCATIONS
*******************************************************************************/

function assert_Snap_Allocations_Modified(
User operator,
IAllocationManagerTypes.AllocateParams memory allocateParams,
bool completed,
string memory err
) internal {
IAllocationManagerTypes.Allocation[] memory curAllocs = _getAllocations(operator, allocateParams.operatorSet, allocateParams.strategies);
IAllocationManagerTypes.Allocation[] memory prevAllocs = _getPrevAllocations(operator, allocateParams.operatorSet, allocateParams.strategies);
Magnitudes[] memory curMagnitudes = _getMagnitudes(operator, allocateParams.strategies);
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(operator, allocateParams.strategies);

for (uint i = 0; i < allocateParams.strategies.length; i++) {
IAllocationManagerTypes.Allocation memory curAlloc = curAllocs[i];
IAllocationManagerTypes.Allocation memory prevAlloc = prevAllocs[i];

// Check allocations
if (completed) {
assertEq(curAlloc.currentMagnitude, allocateParams.newMagnitudes[i], string.concat(err, " (currentMagnitude)"));
assertEq(curAlloc.pendingDiff, 0, string.concat(err, " (pendingDiff)"));
assertEq(curAlloc.effectBlock, 0, string.concat(err, " (effectBlock)"));
} else {
assertEq(
curAlloc.currentMagnitude,
prevAlloc.currentMagnitude,
string.concat(err, " (currentMagnitude)")
);
assertEq(
curAlloc.pendingDiff,
prevAlloc.pendingDiff + int128(int64(allocateParams.newMagnitudes[i])),
string.concat(err, " (pendingDiff)")
);

(, uint32 delay) = allocationManager.getAllocationDelay(address(operator));

assertEq(
curAlloc.effectBlock,
block.number + delay,
string.concat(err, " (effectBlock)")
);
}

// Check magnitudes
Magnitudes memory curMagnitude = curMagnitudes[i];
Magnitudes memory prevMagnitude = prevMagnitudes[i];

// if (isAllocation) ...
if (prevAlloc.currentMagnitude < allocateParams.newMagnitudes[i]) {
uint256 delta = allocateParams.newMagnitudes[i] - prevAlloc.currentMagnitude;
0xClandestine marked this conversation as resolved.
Show resolved Hide resolved

assertEq(
curMagnitude.encumbered,
prevMagnitude.encumbered + delta,
string.concat(err, " (encumbered magnitude)")
);

assertEq(
curMagnitude.allocatable,
prevMagnitude.allocatable - delta,
string.concat(err, " (allocatable magnitude)")
);
} else {
uint256 delta = prevAlloc.currentMagnitude - allocateParams.newMagnitudes[i];

// if deallocated, and completed
if (completed) {
assertEq(
curMagnitude.encumbered - delta,
prevMagnitude.encumbered,
string.concat(err, " (encumbered magnitude)")
);

assertEq(
curMagnitude.allocatable,
prevMagnitude.allocatable + delta,
string.concat(err, " (allocatable magnitude)")
);
} else {
assertEq(
curMagnitude.encumbered,
prevMagnitude.encumbered,
string.concat(err, " (encumbered magnitude)")
);

assertEq(
curMagnitude.allocatable,
prevMagnitude.allocatable,
string.concat(err, " (allocatable magnitude)")
);
}
}

// Max magnitude should not have mutated.
assertEq(
curMagnitude.max,
prevMagnitude.max,
string.concat(err, " (max magnitude)")
);
}
}

function assert_Snap_Allocations_Slashed(
IAllocationManagerTypes.SlashingParams memory slashingParams,
OperatorSet memory operatorSet,
bool completed,
0xClandestine marked this conversation as resolved.
Show resolved Hide resolved
string memory err
) internal {
User op = User(payable(slashingParams.operator));

IAllocationManagerTypes.Allocation[] memory curAllocs = _getAllocations(op, operatorSet, slashingParams.strategies);
IAllocationManagerTypes.Allocation[] memory prevAllocs = _getPrevAllocations(op, operatorSet, slashingParams.strategies);
Magnitudes[] memory curMagnitudes = _getMagnitudes(op, slashingParams.strategies);
Magnitudes[] memory prevMagnitudes = _getPrevMagnitudes(op, slashingParams.strategies);

(, uint32 delay) = allocationManager.getAllocationDelay(slashingParams.operator);

for (uint i = 0; i < slashingParams.strategies.length; i++) {
IAllocationManagerTypes.Allocation memory curAlloc = curAllocs[i];
IAllocationManagerTypes.Allocation memory prevAlloc = prevAllocs[i];

uint64 slashedMagnitude = uint64(uint256(prevAlloc.currentMagnitude).mulWadRoundUp(slashingParams.wadsToSlash[i]));

// Check Allocations
assertEq(
curAlloc.currentMagnitude,
prevAlloc.currentMagnitude - slashedMagnitude,
string.concat(err, " (currentMagnitude)")
);

if (completed) {
0xClandestine marked this conversation as resolved.
Show resolved Hide resolved
assertEq(curAlloc.pendingDiff, 0, string.concat(err, " (pendingDiff)"));
assertEq(curAlloc.effectBlock, 0, string.concat(err, " (effectBlock)"));
} else {
assertEq(
curAlloc.currentMagnitude,
prevAlloc.currentMagnitude - slashedMagnitude,
string.concat(err, " (currentMagnitude)")
);

// If (isDeallocation) ...
if (prevAlloc.pendingDiff < 0) {
uint64 slashedPending =
uint64(uint256(uint128(-prevAlloc.pendingDiff)).mulWadRoundUp(slashingParams.wadsToSlash[i]));

assertEq(
curAlloc.pendingDiff,
prevAlloc.pendingDiff + int128(uint128(slashedPending)),
string.concat(err, " (pendingDiff)")
);

delay = DEALLOCATION_DELAY;
}

assertEq(
0xClandestine marked this conversation as resolved.
Show resolved Hide resolved
curAlloc.effectBlock,
block.number + delay,
string.concat(err, " (effectBlock)")
);
}

// Check Magnitudes
Magnitudes memory curMagnitude = curMagnitudes[i];
Magnitudes memory prevMagnitude = prevMagnitudes[i];

assertEq(
curMagnitude.encumbered,
prevMagnitude.encumbered - slashedMagnitude,
string.concat(err, " (encumbered magnitude)")
);

assertEq(
curMagnitude.allocatable,
prevMagnitude.allocatable,
string.concat(err, " (allocatable magnitude)")
);

assertEq(
curMagnitude.max,
prevMagnitude.max - slashedMagnitude,
string.concat(err, " (max magnitude)")
);
}
}

function assert_HasUnderlyingTokenBalances_AfterSlash(
Copy link
Collaborator

Choose a reason for hiding this comment

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

For non native ETH Strategies, we should also have an assertion on the token balance of the Strategy being burned/updated after the slashOperator call and it should be sent to the burn address.
Perhaps it makes sense to be in this check or a separate assert function

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will include in follow up pr

User staker,
IAllocationManagerTypes.AllocateParams memory allocateParams,
IAllocationManagerTypes.SlashingParams memory slashingParams,
uint[] memory expectedBalances,
string memory err
) internal view {
for (uint i; i < allocateParams.strategies.length; ++i) {
IStrategy strat = allocateParams.strategies[i];

uint balance = strat == BEACONCHAIN_ETH_STRAT
? address(staker).balance
: strat.underlyingToken().balanceOf(address(staker));

if (slashingParams.strategies.contains(strat)) {
uint256 wadToSlash = slashingParams.wadsToSlash[slashingParams.strategies.indexOf(strat)];

expectedBalances[i] -= expectedBalances[i]
.mulWadRoundUp(allocateParams.newMagnitudes[i].mulWadRoundUp(wadToSlash));

uint256 maxDelta = strat == BEACONCHAIN_ETH_STRAT ? 1 gwei : 1;

assertApproxEqAbs(expectedBalances[i], balance, maxDelta, err);
} else {
assertEq(expectedBalances[i], balance, err);
}
}
}

// TODO: slashable stake

/*******************************************************************************
SNAPSHOT ASSERTIONS: OPERATOR SHARES
*******************************************************************************/
Expand All @@ -369,7 +583,7 @@ abstract contract IntegrationBase is IntegrationDeployer {

// For each strategy, check (prev + added == cur)
for (uint i = 0; i < strategies.length; i++) {
assertApproxEqAbs(prevShares[i] + addedShares[i], curShares[i], 1, err);
assertEq(prevShares[i] + addedShares[i], curShares[i], err);
}
}

Expand Down Expand Up @@ -448,8 +662,8 @@ abstract contract IntegrationBase is IntegrationDeployer {
uint[] memory prevShares = _getPrevStakerDepositShares(staker, strategies);

// For each strategy, check (prev + added == cur)
for (uint i = 0; i < strategies.length; i++) {
assertApproxEqAbs(prevShares[i] + addedShares[i], curShares[i], 1, err);
for (uint i = 0; i < strategies.length; i++) {
assertEq(prevShares[i] + addedShares[i], curShares[i], err);
}
}

Expand Down Expand Up @@ -1115,16 +1329,73 @@ abstract contract IntegrationBase is IntegrationDeployer {
}

modifier timewarp() {
uint curState = timeMachine.warpToLast();
uint curState = timeMachine.travelToLast();
_;
timeMachine.warpToPresent(curState);
timeMachine.travel(curState);
}

// TODO
/// @dev Given a list of strategies, roll the block number forward to the
/// a valid blocknumber to completeWithdrawals
/// @dev Rolls forward by the minimum withdrawal delay blocks.
function _rollBlocksForCompleteWithdrawals() internal {
cheats.roll(block.number + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS());
rollForward({blocks: delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()});
}

/// @dev Rolls forward by the default allocation delay blocks.
function _rollBlocksForCompleteAllocation() internal {
(, uint32 delay) = allocationManager.getAllocationDelay(address(this));
rollForward({blocks: delay});
}

/// @dev Rolls forward by the default deallocation delay blocks.
function _rollBlocksForCompleteDeallocation() internal {
cheats.roll(block.number + allocationManager.DEALLOCATION_DELAY());
}

/// @dev Uses timewarp modifier to get the operator set strategy allocations at the last snapshot.
function _getPrevAllocations(
User operator,
OperatorSet memory operatorSet,
IStrategy[] memory strategies
) internal timewarp() returns (IAllocationManagerTypes.Allocation[] memory) {
return _getAllocations(operator, operatorSet, strategies);
}

/// @dev Looks up each strategy for an operator set and returns a list of operator allocations.
function _getAllocations(
User operator,
OperatorSet memory operatorSet,
IStrategy[] memory strategies
) internal view returns (IAllocationManagerTypes.Allocation[] memory allocations) {
allocations = new IAllocationManagerTypes.Allocation[](strategies.length);
for (uint i = 0; i < strategies.length; ++i) {
allocations[i] = allocationManager.getAllocation(address(operator), operatorSet, strategies[i]);
}
}

struct Magnitudes {
uint256 encumbered;
uint256 allocatable;
uint256 max;
}

function _getPrevMagnitudes(
User operator,
IStrategy[] memory strategies
) internal timewarp() returns (Magnitudes[] memory) {
return _getMagnitudes(operator, strategies);
}

function _getMagnitudes(
User operator,
IStrategy[] memory strategies
) internal view returns (Magnitudes[] memory magnitudes) {
magnitudes = new Magnitudes[](strategies.length);
for (uint i = 0; i < strategies.length; ++i) {
magnitudes[i] = Magnitudes({
encumbered: allocationManager.encumberedMagnitude(address(operator), strategies[i]),
allocatable: allocationManager.getAllocatableMagnitude(address(operator), strategies[i]),
max: allocationManager.getMaxMagnitude(address(operator), strategies[i])
});
}
}

/// @dev Uses timewarp modifier to get operator shares at the last snapshot
Expand Down
Loading
Loading