Skip to content

feat(redistribution): update slashers #492

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

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
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
4 changes: 2 additions & 2 deletions docs/slashing/VetoableSlasher.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Creates and queues a new slashing request that will be executable after the veto
#### `cancelSlashingRequest`
```solidity
function cancelSlashingRequest(
uint256 requestId
uint256 slashId
)
external
virtual
Expand All @@ -68,7 +68,7 @@ Allows the veto committee to cancel a pending slashing request within the veto w
#### `fulfillSlashingRequest`
```solidity
function fulfillSlashingRequest(
uint256 requestId
uint256 slashId
)
external
virtual
Expand Down
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
5 changes: 1 addition & 4 deletions src/interfaces/ISlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface ISlasherTypes {
interface ISlasherEvents is ISlasherTypes {
/// @notice Emitted when an operator is successfully slashed
event OperatorSlashed(
uint256 indexed slashingRequestId,
uint256 indexed slashId,
address indexed operator,
uint32 indexed operatorSetId,
uint256[] wadsToSlash,
Expand All @@ -33,7 +33,4 @@ interface ISlasherEvents is ISlasherTypes {
interface ISlasher is ISlasherErrors, ISlasherEvents {
/// @notice Returns the address authorized to create and fulfill slashing requests
function slasher() external view returns (address);

/// @notice Returns the next slashing request ID
function nextRequestId() external view returns (uint256);
}
12 changes: 6 additions & 6 deletions src/interfaces/IVetoableSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ interface IVetoableSlasherTypes {
interface IVetoableSlasherEvents {
/// @notice Emitted when a new slashing request is created
event SlashingRequested(
uint256 indexed requestId,
uint256 indexed slashId,
address indexed operator,
uint32 operatorSetId,
uint256[] wadsToSlash,
string description
);

/// @notice Emitted when a slashing request is cancelled by the veto committee
event SlashingRequestCancelled(uint256 indexed requestId);
event SlashingRequestCancelled(uint256 indexed slashId);
}

/// @title IVetoableSlasher
Expand All @@ -71,16 +71,16 @@ interface IVetoableSlasher is
) external;

/// @notice Cancels a pending slashing request
/// @param requestId The ID of the slashing request to cancel
/// @param slashId The ID of the slashing request to cancel
/// @dev Can only be called by the veto committee during the veto period
function cancelSlashingRequest(
uint256 requestId
uint256 slashId
) external;

/// @notice Executes a slashing request after the veto period has passed
/// @param requestId The ID of the slashing request to fulfill
/// @param slashId The ID of the slashing request to fulfill
/// @dev Can only be called by the authorized slasher after the veto period
function fulfillSlashingRequest(
uint256 requestId
uint256 slashId
) external;
}
12 changes: 5 additions & 7 deletions src/slashers/InstantSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.27;
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {SlasherBase} from "./base/SlasherBase.sol";
import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol";
import {IInstantSlasher} from "../interfaces/IInstantSlasher.sol";
Expand All @@ -14,19 +15,16 @@ import {IInstantSlasher} from "../interfaces/IInstantSlasher.sol";
contract InstantSlasher is IInstantSlasher, SlasherBase {
constructor(
IAllocationManager _allocationManager,
IStrategyManager _strategyManager,
ISlashingRegistryCoordinator _slashingRegistryCoordinator,
address _slasher
) SlasherBase(_allocationManager, _slashingRegistryCoordinator, _slasher) {}
) SlasherBase(_allocationManager, _strategyManager, _slashingRegistryCoordinator, _slasher) {}

/// @inheritdoc IInstantSlasher
function fulfillSlashingRequest(
IAllocationManager.SlashingParams calldata _slashingParams
) external virtual override(IInstantSlasher) onlySlasher {
uint256 requestId = nextRequestId++;
_fulfillSlashingRequest(requestId, _slashingParams);

address[] memory operators = new address[](1);
operators[0] = _slashingParams.operator;
slashingRegistryCoordinator.updateOperators(operators);
_fulfillSlashingRequest(_slashingParams);
_updateOperatorStakeWeights(_slashingParams.operator);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we add a redistributable slasher which fulfills slash then redistributes?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not opposed, added _fulfillSlashingRequestAndBurnOrRedistribute already.

}
}
55 changes: 31 additions & 24 deletions src/slashers/VetoableSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
pragma solidity ^0.8.27;

import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {
IAllocationManager,
OperatorSet
} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {SlasherBase} from "./base/SlasherBase.sol";
import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol";
import {IVetoableSlasher, IVetoableSlasherTypes} from "../interfaces/IVetoableSlasher.sol";
Expand All @@ -29,11 +32,12 @@ contract VetoableSlasher is IVetoableSlasher, SlasherBase {

constructor(
IAllocationManager _allocationManager,
IStrategyManager _strategyManager,
ISlashingRegistryCoordinator _slashingRegistryCoordinator,
address _slasher,
address _vetoCommittee,
uint32 _vetoWindowBlocks
) SlasherBase(_allocationManager, _slashingRegistryCoordinator, _slasher) {
) SlasherBase(_allocationManager, _strategyManager, _slashingRegistryCoordinator, _slasher) {
vetoWindowBlocks = _vetoWindowBlocks;
vetoCommittee = _vetoCommittee;
}
Expand All @@ -47,59 +51,65 @@ contract VetoableSlasher is IVetoableSlasher, SlasherBase {

/// @inheritdoc IVetoableSlasher
function cancelSlashingRequest(
uint256 requestId
uint256 slashId
) external virtual override onlyVetoCommittee {
_cancelSlashingRequest(requestId);
_cancelSlashingRequest(slashId);
}

/// @inheritdoc IVetoableSlasher
function fulfillSlashingRequest(
uint256 requestId
uint256 slashId
) external virtual override onlySlasher {
_fulfillSlashingRequestAndMarkAsCompleted(requestId);
_fulfillSlashingRequestAndMarkAsCompleted(slashId);
}

/// @notice Internal function to create and store a new slashing request
/// @param params Parameters defining the slashing request
function _queueSlashingRequest(
IAllocationManager.SlashingParams memory params
) internal virtual {
uint256 requestId = nextRequestId++;
slashingRequests[requestId] = IVetoableSlasherTypes.VetoableSlashingRequest({
uint256 nextSlashId = allocationManager.getSlashCount(
OperatorSet({avs: slashingRegistryCoordinator.avs(), id: params.operatorSetId})
);
slashingRequests[nextSlashId] = IVetoableSlasherTypes.VetoableSlashingRequest({
params: params,
requestBlock: block.number,
status: IVetoableSlasherTypes.SlashingStatus.Requested
});

emit SlashingRequested(
requestId, params.operator, params.operatorSetId, params.wadsToSlash, params.description
nextSlashId,
params.operator,
params.operatorSetId,
params.wadsToSlash,
params.description
);
}

/// @notice Internal function to mark a slashing request as cancelled
/// @param requestId The ID of the slashing request to cancel
/// @param slashId The ID of the slashing request to cancel
function _cancelSlashingRequest(
uint256 requestId
uint256 slashId
) internal virtual {
require(
block.number < slashingRequests[requestId].requestBlock + vetoWindowBlocks,
block.number < slashingRequests[slashId].requestBlock + vetoWindowBlocks,
VetoPeriodPassed()
);
require(
slashingRequests[requestId].status == IVetoableSlasherTypes.SlashingStatus.Requested,
slashingRequests[slashId].status == IVetoableSlasherTypes.SlashingStatus.Requested,
SlashingRequestNotRequested()
);

slashingRequests[requestId].status = IVetoableSlasherTypes.SlashingStatus.Cancelled;
emit SlashingRequestCancelled(requestId);
slashingRequests[slashId].status = IVetoableSlasherTypes.SlashingStatus.Cancelled;
emit SlashingRequestCancelled(slashId);
}

/// @notice Internal function to fulfill a slashing request and mark it as completed
/// @param requestId The ID of the slashing request to fulfill
/// @param slashId The ID of the slashing request to fulfill
function _fulfillSlashingRequestAndMarkAsCompleted(
uint256 requestId
uint256 slashId
) internal virtual {
IVetoableSlasherTypes.VetoableSlashingRequest storage request = slashingRequests[requestId];
IVetoableSlasherTypes.VetoableSlashingRequest storage request = slashingRequests[slashId];
require(block.number >= request.requestBlock + vetoWindowBlocks, VetoPeriodNotPassed());
require(
request.status == IVetoableSlasherTypes.SlashingStatus.Requested,
Expand All @@ -108,11 +118,8 @@ contract VetoableSlasher is IVetoableSlasher, SlasherBase {

request.status = IVetoableSlasherTypes.SlashingStatus.Completed;

_fulfillSlashingRequest(requestId, request.params);

address[] memory operators = new address[](1);
operators[0] = request.params.operator;
slashingRegistryCoordinator.updateOperators(operators);
_fulfillSlashingRequest(request.params);
_updateOperatorStakeWeights(request.params.operator);
}

/// @notice Internal function to verify if an account is the veto committee
Expand Down
42 changes: 36 additions & 6 deletions src/slashers/base/SlasherBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ pragma solidity ^0.8.27;

import {SlasherStorage, ISlashingRegistryCoordinator} from "./SlasherStorage.sol";
import {
OperatorSet,
IAllocationManagerTypes,
IAllocationManager
} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";

/// @title SlasherBase
Expand All @@ -24,28 +26,46 @@ abstract contract SlasherBase is SlasherStorage {
/// @param _slasher The address of the slasher
constructor(
IAllocationManager _allocationManager,
IStrategyManager _strategyManager,
ISlashingRegistryCoordinator _registryCoordinator,
address _slasher
) SlasherStorage(_allocationManager, _registryCoordinator, _slasher) {}
) SlasherStorage(_allocationManager, _strategyManager, _registryCoordinator, _slasher) {}

/// @notice Internal function to execute a slashing request
/// @param _requestId The ID of the slashing request to fulfill
/// @param _params Parameters defining the slashing request including operator, strategies, and amounts
/// @dev Calls AllocationManager.slashOperator to perform the actual slashing
function _fulfillSlashingRequest(
uint256 _requestId,
IAllocationManager.SlashingParams memory _params
) internal virtual {
allocationManager.slashOperator({avs: slashingRegistryCoordinator.avs(), params: _params});
) internal virtual returns (uint256 slashId) {
(slashId,) = allocationManager.slashOperator({
avs: slashingRegistryCoordinator.avs(),
params: _params
});
emit OperatorSlashed(
_requestId,
slashId,
_params.operator,
_params.operatorSetId,
_params.wadsToSlash,
_params.description
);
}

/// @notice Internal function to optionally fulfill burn or redistribution instead of waiting for cron job
function _fulfillBurnOrRedistribution(uint32 operatorSetId, uint256 slashId) internal virtual {
strategyManager.clearBurnOrRedistributableShares({
operatorSet: OperatorSet({avs: slashingRegistryCoordinator.avs(), id: operatorSetId}),
slashId: slashId
});
}

/// @notice Internal function to fulfill a slashing request and burn or redistribute shares
function _fulfillSlashingRequestAndBurnOrRedistribute(
IAllocationManager.SlashingParams memory _params
) internal virtual returns (uint256 slashId) {
slashId = _fulfillSlashingRequest(_params);
_fulfillBurnOrRedistribution(_params.operatorSetId, slashId);
}

/// @notice Internal function to verify if an account is the authorized slasher
/// @param account The address to check
/// @dev Reverts if the account is not the authorized slasher
Expand All @@ -54,4 +74,14 @@ abstract contract SlasherBase is SlasherStorage {
) internal view virtual {
require(account == slasher, OnlySlasher());
}

/// @notice Internal function to update stake weights for the given operator
/// @param operator The operator to update
function _updateOperatorStakeWeights(
address operator
) internal virtual {
address[] memory operators = new address[](1);
operators[0] = operator;
slashingRegistryCoordinator.updateOperators(operators);
}
}
13 changes: 10 additions & 3 deletions src/slashers/base/SlasherStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.27;

import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {ISlashingRegistryCoordinator} from "../../interfaces/ISlashingRegistryCoordinator.sol";
import {ISlasher} from "../../interfaces/ISlasher.sol";

Expand All @@ -16,21 +17,27 @@ abstract contract SlasherStorage is ISlasher {
*
*/

/// @notice the AllocationManager that tracks OperatorSets and Slashing in EigenLayer
/// @notice The `AllocationManager` tracks operator sets, operator set allocations, and slashing in EigenLayer.
IAllocationManager public immutable allocationManager;
/// @notice the SlashingRegistryCoordinator for this AVS
/// @notice The `StrategyManager` handles strategy inflows/outflows.
IStrategyManager public immutable strategyManager;
/// @notice The `SlashingRegistryCoordinator` for this AVS.
ISlashingRegistryCoordinator public immutable slashingRegistryCoordinator;

/// @notice the address of the slasher
address public immutable slasher;

uint256 public nextRequestId;
/// @dev DEPRECATED -- `AllocationManager` now tracks monotonically increasing `slashId`.
uint256 private __deprecated_nextRequestId;

constructor(
IAllocationManager _allocationManager,
IStrategyManager _strategyManager,
ISlashingRegistryCoordinator _slashingRegistryCoordinator,
address _slasher
) {
allocationManager = _allocationManager;
strategyManager = _strategyManager;
slashingRegistryCoordinator = _slashingRegistryCoordinator;
slasher = _slasher;
}
Expand Down
7 changes: 6 additions & 1 deletion test/integration/IntegrationDeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import "eigenlayer-contracts/src/contracts/core/AllocationManager.sol";
import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol";
import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol";
import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol";
import "eigenlayer-contracts/src/contracts/strategies/EigenStrategy.sol";
import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol";
import "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol";
import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol";
Expand Down Expand Up @@ -206,7 +207,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
"v0.0.1"
);
StrategyManager strategyManagerImplementation =
new StrategyManager(delegationManager, pauserRegistry, "v0.0.1");
new StrategyManager(allocationManager, delegationManager, pauserRegistry, "v0.0.1");
EigenPodManager eigenPodManagerImplementation = new EigenPodManager(
ethPOSDeposit, eigenPodBeacon, delegationManager, pauserRegistry, "v0.0.1"
);
Expand All @@ -229,8 +230,12 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
})
);

IStrategy eigenStrategy =
IStrategy(new EigenStrategy(strategyManager, pauserRegistry, "v0.0.1"));

AllocationManager allocationManagerImplementation = new AllocationManager(
delegationManager,
eigenStrategy,
pauserRegistry,
permissionController,
uint32(7 days), // DEALLOCATION_DELAY
Expand Down
Loading
Loading