Skip to content

Commit

Permalink
Slashing: DM Happy Path Test Cases (#815)
Browse files Browse the repository at this point in the history
* test: basic dm tests

* test: start on share increase/decrease delegated shares tests

* test: add DM unit tests passing except queue/complete

* test: undelegate tests

* test: queue withdrawals

* test: completed DM happy path test cases

* fix: compilation, rebase errors

* chore: format
  • Loading branch information
ypatil12 authored Oct 13, 2024
1 parent 5b4d415 commit 4e6b659
Show file tree
Hide file tree
Showing 18 changed files with 1,363 additions and 535 deletions.
20 changes: 9 additions & 11 deletions src/contracts/core/AVSDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -224,29 +224,27 @@ contract AVSDirectory is
}

/// @inheritdoc IAVSDirectory
function addStrategiesToOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external override {
function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external override {
OperatorSet memory operatorSet = OperatorSet(msg.sender, operatorSetId);
require(isOperatorSet[msg.sender][operatorSetId], InvalidOperatorSet());
bytes32 encodedOperatorSet = _encodeOperatorSet(operatorSet);
for (uint256 i = 0; i < strategies.length; i++) {
require(_operatorSetStrategies[encodedOperatorSet].add(address(strategies[i])), StrategyAlreadyInOperatorSet());
require(
_operatorSetStrategies[encodedOperatorSet].add(address(strategies[i])), StrategyAlreadyInOperatorSet()
);
emit StrategyAddedToOperatorSet(operatorSet, strategies[i]);
}
}

/// @inheritdoc IAVSDirectory
function removeStrategiesFromOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external override {
function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external override {
OperatorSet memory operatorSet = OperatorSet(msg.sender, operatorSetId);
require(isOperatorSet[msg.sender][operatorSetId], InvalidOperatorSet());
bytes32 encodedOperatorSet = _encodeOperatorSet(operatorSet);
for (uint256 i = 0; i < strategies.length; i++) {
require(_operatorSetStrategies[encodedOperatorSet].remove(address(strategies[i])), StrategyNotInOperatorSet());
require(
_operatorSetStrategies[encodedOperatorSet].remove(address(strategies[i])), StrategyNotInOperatorSet()
);
emit StrategyRemovedFromOperatorSet(operatorSet, strategies[i]);
}
}
Expand Down Expand Up @@ -310,7 +308,7 @@ contract AVSDirectory is
require(!operatorSaltIsSpent[operator][operatorSignature.salt], SaltSpent());

// Assert `operator` is a registered operator.
require(delegation.isOperator(operator), OperatorDoesNotExist());
require(delegation.isOperator(operator), OperatorNotRegistered());

// Assert that `operatorSignature.signature` is a valid signature for the operator AVS registration.
_checkIsValidSignatureNow({
Expand Down
2 changes: 0 additions & 2 deletions src/contracts/core/AllocationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,6 @@ contract AllocationManager is
* @param delay The allocation delay in seconds.
*/
function _setAllocationDelay(address operator, uint32 delay) internal {
require(delay != 0, InvalidDelay());

AllocationDelayInfo memory info = _allocationDelayInfo[operator];

if (info.pendingDelay != 0 && block.timestamp >= info.effectTimestamp) {
Expand Down
12 changes: 2 additions & 10 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ contract DelegationManager is
// Calculate the deposit shares
uint256 depositSharesToRemove = sharesToWithdraw[i].toDepositShares(ssf, maxMagnitudes[i]);
uint256 depositSharesWithdrawable = shareManager.stakerDepositShares(staker, strategies[i]);
require(depositSharesToRemove <= depositSharesWithdrawable, WithdrawalExeedsMax());
require(depositSharesToRemove <= depositSharesWithdrawable, WithdrawalExceedsMax());

// Remove delegated shares from the operator
if (operator != address(0)) {
Expand Down Expand Up @@ -777,15 +777,6 @@ contract DelegationManager is
return _operatorDetails[operator].delegationApprover;
}

/**
* @notice Returns the stakerOptOutWindowBlocks for an operator
*/
function stakerOptOutWindowBlocks(
address operator
) external view returns (uint256) {
return _operatorDetails[operator].stakerOptOutWindowBlocks;
}

/**
* @notice Given a staker and a set of strategies, return the shares they can queue for withdrawal.
* This value depends on which operator the staker is delegated to.
Expand All @@ -798,6 +789,7 @@ contract DelegationManager is
) public view returns (uint256[] memory withdrawableShares) {
address operator = delegatedTo[staker];
uint64[] memory totalMagnitudes = allocationManager.getMaxMagnitudes(operator, strategies);
withdrawableShares = new uint256[](strategies.length);

for (uint256 i = 0; i < strategies.length; ++i) {
IShareManager shareManager = _getShareManager(strategies[i]);
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/core/DelegationManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ abstract contract DelegationManagerStorage is IDelegationManager {
/// @notice The AllocationManager contract for EigenLayer
IAllocationManager public immutable allocationManager;

/// @notice Minimum withdrawal delay in seconds until all queued withdrawals can be completed.
/// @notice Minimum withdrawal delay in seconds until a queued withdrawal can be completed.
uint32 public immutable MIN_WITHDRAWAL_DELAY;

// Mutatables
Expand Down
14 changes: 3 additions & 11 deletions src/contracts/interfaces/IAVSDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface IAVSDirectoryErrors {
/// Operator Status

/// @dev Thrown when an operator does not exist in the DelegationManager
error OperatorDoesNotExist();
error OperatorNotRegistered();
/// @dev Thrown when `operator` is already registered to the AVS.
error OperatorAlreadyRegistered();

Expand All @@ -29,8 +29,6 @@ interface IAVSDirectoryErrors {
error StrategyAlreadyInOperatorSet();
/// @dev Thrown when a strategy is not in an operator set.
error StrategyNotInOperatorSet();
/// @dev Thrown when an invalid operator set is provided.
error OperatorNotRegistered();

/// @dev Thrown when attempting to spend a spent eip-712 salt.
error SaltSpent();
Expand Down Expand Up @@ -188,10 +186,7 @@ interface IAVSDirectory is IAVSDirectoryEvents, IAVSDirectoryErrors, ISignatureU
*
* @dev msg.sender is used as the AVS.
*/
function addStrategiesToOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external;
function addStrategiesToOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external;

/**
* @notice Called by AVSs to remove a set of strategies from an operator set.
Expand All @@ -201,10 +196,7 @@ interface IAVSDirectory is IAVSDirectoryEvents, IAVSDirectoryErrors, ISignatureU
*
* @dev msg.sender is used as the AVS.
*/
function removeStrategiesFromOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external;
function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] calldata strategies) external;

/**
* @notice Legacy function called by the AVS's service manager contract
Expand Down
3 changes: 0 additions & 3 deletions src/contracts/interfaces/IAllocationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ interface IAllocationManagerErrors {
error OperatorNotRegistered();
/// @dev Thrown when two array parameters have mismatching lengths.
error InputArrayLengthMismatch();
/// @dev Thrown when an operator attempts to set their allocation delay to 0
error InvalidDelay();
/// @dev Thrown when an operator's allocation delay has yet to be set.
error UninitializedAllocationDelay();
/// @dev Thrown when provided `expectedTotalMagnitude` for a given allocation does not match `currentTotalMagnitude`.
Expand Down Expand Up @@ -108,7 +106,6 @@ interface IAllocationManagerTypes {
int128 pendingDiff;
uint32 effectTimestamp;
}

}

interface IAllocationManagerEvents is IAllocationManagerTypes {
Expand Down
41 changes: 8 additions & 33 deletions src/contracts/interfaces/IDelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,17 @@ interface IDelegationManagerErrors {

/// Delegation Status

/// @dev Thrown when an account is currently delegated.
error AlreadyDelegated();
/// @dev Thrown when an account is not currently delegated.
error NotCurrentlyDelegated();
/// @dev Thrown when an operator attempts to undelegate.
error OperatorsCannotUndelegate();
/// @dev Thrown when an account is actively delegated.
error ActivelyDelegated();
/// @dev Thrown when an account is not actively delegated.
error NotActivelyDelegated();
/// @dev Thrown when `operator` is not a registered operator.
error OperatorDoesNotExist();
error OperatorNotRegistered();

/// Invalid Inputs

/// @dev Thrown when an account is actively delegated.
error ActivelyDelegated();
/// @dev Thrown when attempting to execute an action that was not queued.
error WithdrawalNotQueued();
/// @dev Thrown when provided delay exceeds maximum.
Expand All @@ -38,17 +36,9 @@ interface IDelegationManagerErrors {
error InputArrayLengthMismatch();
/// @dev Thrown when input arrays length is zero.
error InputArrayLengthZero();
/// @dev Thrown when `operator` is not a registered operator.
error OperatorNotRegistered();
/// @dev Thrown when caller is neither the StrategyManager or EigenPodManager contract.
error OnlyStrategyManagerOrEigenPodManager();

/// @dev Thrown when an account is not actively delegated.
error NotActivelyDelegated();
/// @dev Thrown when provided `stakerOptOutWindowBlocks` cannot decrease.
error StakerOptOutWindowBlocksCannotDecrease();
/// @dev Thrown when provided `stakerOptOutWindowBlocks` exceeds maximum.
error StakerOptOutWindowBlocksExceedsMax();
/// @dev Thrown when provided delay exceeds maximum.
error WithdrawalDelayExceedsMax();

Expand All @@ -68,7 +58,7 @@ interface IDelegationManagerErrors {
/// @dev Thrown when provided delay exceeds maximum.
error WithdrawalDelayExeedsMax();
/// @dev Thrown when a withdraw amount larger than max is attempted.
error WithdrawalExeedsMax();
error WithdrawalExceedsMax();
/// @dev Thrown when withdrawer is not the current caller.
error WithdrawerNotCaller();
/// @dev Thrown when `withdrawer` is not staker.
Expand All @@ -88,15 +78,8 @@ interface IDelegationManagerTypes {
* 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
*/
address delegationApprover;
/**
* @notice A minimum delay -- measured in blocks -- enforced between:
* 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing`
* and
* 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate`
* @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails,
* then they are only allowed to either increase this value or keep it the same.
*/
uint32 stakerOptOutWindowBlocks;
/// @notice DEPRECATED -- this field is no longer used. An analogous field is the `allocationDelay` stored in the AllocationManager
uint32 __deprecated_stakerOptOutWindowBlocks;
}

/**
Expand Down Expand Up @@ -210,7 +193,6 @@ interface IDelegationManagerEvents is IDelegationManagerTypes {

/// @notice Emitted when a queued withdrawal is completed
event SlashingWithdrawalCompleted(bytes32 withdrawalRoot);

}

/**
Expand Down Expand Up @@ -432,13 +414,6 @@ interface IDelegationManager is ISignatureUtils, IDelegationManagerErrors, IDele
address operator
) external view returns (address);

/**
* @notice Returns the stakerOptOutWindowBlocks for an operator
*/
function stakerOptOutWindowBlocks(
address operator
) external view returns (uint256);

/**
* @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/libraries/SlashingLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ library SlashingLib {
StakerScalingFactors storage ssf,
uint64 proportionOfOldBalance
) internal {
ssf.beaconChainScalingFactor = uint64(uint256(ssf.beaconChainScalingFactor).mulWad(proportionOfOldBalance));
ssf.beaconChainScalingFactor = uint64(uint256(ssf.getBeaconChainScalingFactor()).mulWad(proportionOfOldBalance));
ssf.isBeaconChainScalingFactorSet = true;
}

Expand Down
26 changes: 14 additions & 12 deletions src/test/Delegation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ contract DelegationTests is EigenLayerTestHelper {
uint8 defaultQuorumNumber = 0;
bytes32 defaultOperatorId = bytes32(uint256(0));

uint256 public constant DEFAULT_ALLOCATION_DELAY = 17.5 days;

modifier fuzzedAmounts(uint256 ethAmount, uint256 eigenAmount) {
cheats.assume(ethAmount >= 0 && ethAmount <= 1e18);
cheats.assume(eigenAmount >= 0 && eigenAmount <= 1e18);
Expand All @@ -38,7 +40,7 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: sender,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
_testRegisterAsOperator(sender, 0, operatorDetails);
}
Expand Down Expand Up @@ -87,7 +89,7 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: operator,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
if (!delegation.isOperator(operator)) {
_testRegisterAsOperator(operator, 0, operatorDetails);
Expand Down Expand Up @@ -337,10 +339,10 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: operator,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
_testRegisterAsOperator(operator, 0, operatorDetails);
cheats.expectRevert(IDelegationManagerErrors.AlreadyDelegated.selector);
cheats.expectRevert(IDelegationManagerErrors.ActivelyDelegated.selector);
_testRegisterAsOperator(operator, 0, operatorDetails);
}

Expand All @@ -351,7 +353,7 @@ contract DelegationTests is EigenLayerTestHelper {
_testDepositStrategies(getOperatorAddress(1), 1e18, 1);
_testDepositEigen(getOperatorAddress(1), 1e18);

cheats.expectRevert(IDelegationManagerErrors.OperatorDoesNotExist.selector);
cheats.expectRevert(IDelegationManagerErrors.OperatorNotRegistered.selector);
cheats.startPrank(getOperatorAddress(1));
ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry;
delegation.delegateTo(delegate, signatureWithExpiry, bytes32(0));
Expand Down Expand Up @@ -382,11 +384,11 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: msg.sender,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
string memory emptyStringForMetadataURI;
delegation.registerAsOperator(operatorDetails, 0, emptyStringForMetadataURI);
cheats.expectRevert(IDelegationManagerErrors.AlreadyDelegated.selector);
cheats.expectRevert(IDelegationManagerErrors.ActivelyDelegated.selector);
delegation.registerAsOperator(operatorDetails, 0, emptyStringForMetadataURI);
cheats.stopPrank();
}
Expand All @@ -397,10 +399,10 @@ contract DelegationTests is EigenLayerTestHelper {
address _unregisteredoperator
) public fuzzedAddress(_staker) {
vm.startPrank(_staker);
cheats.expectRevert(IDelegationManagerErrors.OperatorDoesNotExist.selector);
cheats.expectRevert(IDelegationManagerErrors.OperatorNotRegistered.selector);
ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry;
delegation.delegateTo(_unregisteredoperator, signatureWithExpiry, bytes32(0));
cheats.expectRevert(IDelegationManagerErrors.OperatorDoesNotExist.selector);
cheats.expectRevert(IDelegationManagerErrors.OperatorNotRegistered.selector);
delegation.delegateTo(_staker, signatureWithExpiry, bytes32(0));
cheats.stopPrank();
}
Expand All @@ -418,7 +420,7 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: _dt,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
string memory emptyStringForMetadataURI;
delegation.registerAsOperator(operatorDetails, 0, emptyStringForMetadataURI);
Expand Down Expand Up @@ -455,7 +457,7 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: sender,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
_testRegisterAsOperator(sender, 0, operatorDetails);
cheats.startPrank(sender);
Expand All @@ -480,7 +482,7 @@ contract DelegationTests is EigenLayerTestHelper {
IDelegationManagerTypes.OperatorDetails memory operatorDetails = IDelegationManagerTypes.OperatorDetails({
__deprecated_earningsReceiver: operator,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
__deprecated_stakerOptOutWindowBlocks: 0
});
_testRegisterAsOperator(operator, 0, operatorDetails);
}
Expand Down
6 changes: 4 additions & 2 deletions src/test/EigenLayerDeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ contract EigenLayerDeployer is Operators {
abi.encodeWithSelector(
AVSDirectory.initialize.selector,
eigenLayerReputedMultisig,
eigenLayerPauserReg
eigenLayerPauserReg,
0 /*initialPausedStatus*/
)
);
eigenLayerProxyAdmin.upgradeAndCall(
Expand Down Expand Up @@ -245,7 +246,8 @@ contract EigenLayerDeployer is Operators {
abi.encodeWithSelector(
AllocationManager.initialize.selector,
eigenLayerReputedMultisig,
eigenLayerPauserReg
eigenLayerPauserReg,
0 /*initialPausedStatus*/
)
);

Expand Down
Loading

0 comments on commit 4e6b659

Please sign in to comment.