Skip to content
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
410 changes: 360 additions & 50 deletions src/test/integration/IntegrationBase.t.sol

Large diffs are not rendered by default.

151 changes: 51 additions & 100 deletions src/test/integration/IntegrationChecks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ contract IntegrationCheckUtils is IntegrationBase {
User staker,
User operator,
IStrategy[] memory strategies,
uint[] memory shares,
uint[] memory depositShares,
uint[] memory withdrawableShares,
Withdrawal[] memory withdrawals,
bytes32[] memory withdrawalRoots
) internal {
Expand All @@ -215,11 +216,11 @@ contract IntegrationCheckUtils is IntegrationBase {
"check_QueuedWithdrawal_State: calculated withdrawals should match returned roots");
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals,
"check_QueuedWithdrawal_State: staker should have increased nonce by withdrawals.length");
assert_Snap_Removed_OperatorShares(operator, strategies, shares,
assert_Snap_Removed_OperatorShares(operator, strategies, withdrawableShares,
"check_QueuedWithdrawal_State: failed to remove operator shares");
assert_Snap_Removed_Staker_DepositShares(staker, strategies, shares,
assert_Snap_Removed_Staker_DepositShares(staker, strategies, depositShares,
"check_QueuedWithdrawal_State: failed to remove staker shares");
assert_Snap_Removed_Staker_WithdrawableShares(staker, strategies, shares,
assert_Snap_Removed_Staker_WithdrawableShares(staker, strategies, withdrawableShares,
"check_QueuedWithdrawal_State: failed to remove staker withdrawable shares");
}

Expand All @@ -237,12 +238,15 @@ contract IntegrationCheckUtils is IntegrationBase {
// ... check that the staker is undelegated, all strategies from which the staker is deposited are unqueued,
// that the returned root matches the hashes for each strategy and share amounts, and that the staker
// and operator have reduced shares
assert_HasNoDelegatableShares(staker, "staker should have withdrawn all shares");
assertFalse(delegationManager.isDelegated(address(staker)),
"check_Undelegate_State: staker should not be delegated");
assert_ValidWithdrawalHashes(withdrawals, withdrawalRoots,
"check_Undelegate_State: calculated withdrawal should match returned root");
assert_AllWithdrawalsPending(withdrawalRoots,
"check_Undelegate_State: stakers withdrawal should now be pending");
assert_DSF_Reset(staker, strategies,
"check_Undelegate_State: staker dsfs should be reset to wad");
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals,
"check_Undelegate_State: staker should have increased nonce by withdrawals.length");
assert_Snap_Removed_OperatorShares(operator, strategies, stakerDelegatedShares,
Expand All @@ -255,7 +259,8 @@ contract IntegrationCheckUtils is IntegrationBase {

function check_Redelegate_State(
User staker,
User operator,
User prevOperator,
User newOperator,
IDelegationManagerTypes.Withdrawal[] memory withdrawals,
bytes32[] memory withdrawalRoots,
IStrategy[] memory strategies,
Expand All @@ -269,13 +274,16 @@ contract IntegrationCheckUtils is IntegrationBase {
// and operator have reduced shares
assertTrue(delegationManager.isDelegated(address(staker)),
"check_Redelegate_State: staker should not be delegated");
assertEq(address(newOperator), delegationManager.delegatedTo(address(staker)), "staker should be delegated to operator");
assert_HasExpectedShares(staker, strategies, new uint[](strategies.length), "staker should not have deposit shares after redelegation");
assert_Snap_Unchanged_OperatorShares(newOperator, "new operator should not have received any shares");
assert_ValidWithdrawalHashes(withdrawals, withdrawalRoots,
"check_Redelegate_State: calculated withdrawl should match returned root");
assert_AllWithdrawalsPending(withdrawalRoots,
"check_Redelegate_State: stakers withdrawal should now be pending");
assert_Snap_Added_QueuedWithdrawals(staker, withdrawals,
"check_Redelegate_State: staker should have increased nonce by withdrawals.length");
assert_Snap_Removed_OperatorShares(operator, strategies, stakerDelegatedShares,
assert_Snap_Removed_OperatorShares(prevOperator, strategies, stakerDelegatedShares,
"check_Redelegate_State: failed to remove operator shares");
assert_Snap_Removed_Staker_DepositShares(staker, strategies, stakerDepositShares,
"check_Redelegate_State: failed to remove staker shares");
Expand Down Expand Up @@ -336,7 +344,7 @@ contract IntegrationCheckUtils is IntegrationBase {
if (operator != staker) {
assert_Snap_Unchanged_TokenBalances(operator, "operator should not have any change in underlying token balances");
}
assert_Snap_Added_OperatorShares(operator, withdrawal.strategies, withdrawal.scaledShares, "operator should have received shares");
assert_Snap_Added_OperatorShares(operator, strategies, shares, "operator should have received shares");
}
}

Expand All @@ -357,7 +365,9 @@ contract IntegrationCheckUtils is IntegrationBase {
assert_WithdrawalNotPending(delegationManager.calculateWithdrawalRoot(withdrawal), "staker withdrawal should no longer be pending");
assert_Snap_Unchanged_TokenBalances(staker, "staker should not have any change in underlying token balances");
assert_Snap_Unchanged_TokenBalances(operator, "operator should not have any change in underlying token balances");
assert_Snap_Added_Staker_DepositShares(staker, strategies, shares, "staker should have received expected shares");
assert_Snap_Added_Staker_DepositShares(staker, strategies, shares, "staker should have received expected deposit shares");
uint[] memory expectedWithdrawableShares = _getExpectedWithdrawableSharesUndelegate(staker, strategies, shares);
assert_Snap_Added_Staker_WithdrawableShares(staker, strategies, expectedWithdrawableShares, "staker should have received expected withdrawable shares");
assert_Snap_Unchanged_OperatorShares(operator, "operator should have shares unchanged");
assert_Snap_Unchanged_StrategyShares(strategies, "strategies should have total shares unchanged");
}
Expand Down Expand Up @@ -487,18 +497,6 @@ contract IntegrationCheckUtils is IntegrationBase {
assert_Snap_Unchanged_AllocatableMagnitude(operator, allStrats, "should not have updated allocatable magnitude");
}

// /// @dev Checks invariants for registration for a variety of allocation states
// ///
// function check_Registration_State(
// User operator,
// OperatorSet memory operatorSet,
// IStrategy[] memory unallocated,
// AllocateParams memory pending,
// AllocateParams memory active
// ) internal {
// check_Base_Registration_State(operator, operatorSet);
// }

/// @dev Check invariants for registerForOperatorSets given a set of strategies
/// for which NO allocation exists (currentMag/pendingDiff are 0)
/// @param unallocated For the given operatorSet, a list of strategies for which NO allocation exists
Expand Down Expand Up @@ -875,93 +873,46 @@ contract IntegrationCheckUtils is IntegrationBase {
check_ActiveModification_State(operator, deallocateParams);
}

// TODO: improvement needed
/*******************************************************************************
ALM - SLASHING
*******************************************************************************/

function check_Withdrawal_AsTokens_State_AfterSlash(
User staker,
function check_Base_Slashing_State(
User operator,
Withdrawal memory withdrawal,
AllocateParams memory allocateParams,
SlashingParams memory slashingParams,
uint[] memory expectedTokens
SlashingParams memory slashParams
) internal {
IERC20[] memory tokens = new IERC20[](withdrawal.strategies.length);

for (uint i; i < withdrawal.strategies.length; i++) {
IStrategy strat = withdrawal.strategies[i];

bool isBeaconChainETHStrategy = strat == beaconChainETHStrategy;

tokens[i] = isBeaconChainETHStrategy ? NATIVE_ETH : withdrawal.strategies[i].underlyingToken();

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

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

uint256 max = allocationManager.getMaxMagnitude(address(operator), strat);

withdrawal.scaledShares[i] -= withdrawal.scaledShares[i].calcSlashedAmount(WAD, max);

// Round down to the nearest gwei for beaconchain ETH strategy.
if (isBeaconChainETHStrategy) {
expectedTokens[i] -= expectedTokens[i] % 1 gwei;
}
}
}

// Common checks
assert_WithdrawalNotPending(delegationManager.calculateWithdrawalRoot(withdrawal), "staker withdrawal should no longer be pending");

// TODO FIXME
// assert_Snap_Added_TokenBalances(staker, tokens, expectedTokens, "staker should have received expected tokens");
assert_Snap_Unchanged_Staker_DepositShares(staker, "staker shares should not have changed");
assert_Snap_Removed_StrategyShares(withdrawal.strategies, withdrawal.scaledShares, "strategies should have total shares decremented");

// Checks specific to an operator that the Staker has delegated to
if (operator != User(payable(0))) {
if (operator != staker) {
assert_Snap_Unchanged_TokenBalances(operator, "operator token balances should not have changed");
}
assert_Snap_Unchanged_OperatorShares(operator, "operator shares should not have changed");
}
}
OperatorSet memory operatorSet = allocateParams.operatorSet;

function check_Withdrawal_AsShares_State_AfterSlash(
User staker,
check_MaxMag_Invariants(operator);
check_IsSlashable_State(operator, operatorSet, allocateParams.strategies);

// Slashing SHOULD change max magnitude and current allocation
assert_Snap_Slashed_MaxMagnitude(operator, operatorSet, slashParams, "slash should lower max magnitude");
assert_Snap_Slashed_EncumberedMagnitude(operator, slashParams, "slash should lower encumbered magnitude");
assert_Snap_Slashed_AllocatedStake(operator, operatorSet, slashParams, "slash should lower allocated stake");
assert_Snap_Slashed_SlashableStake(operator, operatorSet, slashParams, "slash should lower slashable stake");
assert_Snap_Slashed_OperatorShares(operator, slashParams, "slash should remove operator shares");
assert_Snap_Slashed_Allocation(operator, operatorSet, slashParams, "slash should reduce current magnitude");
assert_Snap_Increased_BurnableShares(operator, slashParams, "slash should increase burnable shares");

// Slashing SHOULD NOT change allocatable magnitude, registration, and slashability status
assert_Snap_Unchanged_AllocatableMagnitude(operator, allStrats, "slashing should not change allocatable magnitude");
assert_Snap_Unchanged_Registration(operator, operatorSet, "slash should not change registration status");
assert_Snap_Unchanged_Slashability(operator, operatorSet, "slash should not change slashability status");
// assert_Snap_Unchanged_AllocatedSets(operator, "should not have updated allocated sets");
// assert_Snap_Unchanged_AllocatedStrats(operator, operatorSet, "should not have updated allocated strategies");
}

/// Slashing invariants when the operator has been fully slashed for every strategy in the operator set
function check_FullySlashed_State(
User operator,
Withdrawal memory withdrawal,
AllocateParams memory allocateParams, // TODO - was this needed?
SlashingParams memory slashingParams
AllocateParams memory allocateParams,
SlashingParams memory slashParams
) internal {
IERC20[] memory tokens = new IERC20[](withdrawal.strategies.length);

for (uint i; i < withdrawal.strategies.length; i++) {
IStrategy strat = withdrawal.strategies[i];

bool isBeaconChainETHStrategy = strat == beaconChainETHStrategy;

tokens[i] = isBeaconChainETHStrategy ? NATIVE_ETH : withdrawal.strategies[i].underlyingToken();

if (slashingParams.strategies.contains(strat)) {
uint256 max = allocationManager.getMaxMagnitude(address(operator), strat);
check_Base_Slashing_State(operator, allocateParams, slashParams);

withdrawal.scaledShares[i] -= withdrawal.scaledShares[i].calcSlashedAmount(WAD, max);
}
}

// Common checks applicable to both user and non-user operator types
assert_WithdrawalNotPending(delegationManager.calculateWithdrawalRoot(withdrawal), "staker withdrawal should no longer be pending");
assert_Snap_Unchanged_TokenBalances(staker, "staker should not have any change in underlying token balances");
assert_Snap_Added_Staker_DepositShares(staker, withdrawal.strategies, withdrawal.scaledShares, "staker should have received expected shares");
assert_Snap_Unchanged_StrategyShares(withdrawal.strategies, "strategies should have total shares unchanged");

// Additional checks or handling for the non-user operator scenario
if (operator != User(User(payable(0)))) {
if (operator != staker) {
assert_Snap_Unchanged_TokenBalances(operator, "operator should not have any change in underlying token balances");
}
}
assert_Snap_Removed_AllocatedSet(operator, allocateParams.operatorSet, "should not have updated allocated sets");
assert_Snap_Removed_AllocatedStrats(operator, allocateParams.operatorSet, slashParams.strategies, "should not have updated allocated strategies");
}
}
Loading