From ff8068ec305d8f4faed29a6b99026abf1e41b5b1 Mon Sep 17 00:00:00 2001 From: "clandestine.eth" <96172957+0xClandestine@users.noreply.github.com> Date: Tue, 28 Jan 2025 11:18:11 -0500 Subject: [PATCH 01/13] fix: remappings --- foundry.toml | 8 +++++--- remappings.txt | 5 ----- 2 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 remappings.txt diff --git a/foundry.toml b/foundry.toml index ce96ecd1..bef35acd 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,10 +16,12 @@ # Defines paths for Solidity imports. remappings = [ - "@openzeppelin/=lib/openzeppelin-contracts-v4.9.0/", - "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable-v4.9.0/", + "forge-std/=lib/forge-std/src/", "ds-test/=lib/ds-test/src/", - "forge-std/=lib/forge-std/src/" + "@openzeppelin/=lib/openzeppelin-contracts/", + "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/", + "eigenlayer-contracts/=lib/eigenlayer-contracts/", + ] # Specifies the exact version of Solidity to use, overriding auto-detection. solc_version = '0.8.27' diff --git a/remappings.txt b/remappings.txt deleted file mode 100644 index 8c001fd0..00000000 --- a/remappings.txt +++ /dev/null @@ -1,5 +0,0 @@ -@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/ -@openzeppelin/=lib/openzeppelin-contracts/ -ds-test/=lib/ds-test/src/ -eigenlayer-contracts/=lib/eigenlayer-contracts/ -forge-std/=lib/forge-std/src/ From eef70d98429ed2b5e2c9e5cc4f26669d3fd1f7f4 Mon Sep 17 00:00:00 2001 From: Noel <103109678+neutiyoo@users.noreply.github.com> Date: Fri, 31 Jan 2025 12:02:01 +0900 Subject: [PATCH 02/13] fix: remove unused OnlySlasher error (#376) --- src/interfaces/IServiceManager.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index a93f03f0..29f50501 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -19,8 +19,6 @@ interface IServiceManagerErrors { error OnlyRewardsInitiator(); /// @notice Thrown when a function is called by an address that is not the StakeRegistry. error OnlyStakeRegistry(); - /// @notice Thrown when a function is called by an address that is not the Slasher. - error OnlySlasher(); /// @notice Thrown when a slashing proposal delay has not been met yet. error DelayPeriodNotPassed(); } From 25a57ebf5ba7e267796e8713495dc8e71887d09a Mon Sep 17 00:00:00 2001 From: Noel <103109678+neutiyoo@users.noreply.github.com> Date: Fri, 31 Jan 2025 22:03:51 +0900 Subject: [PATCH 03/13] refactor: VetoableSlasher (#377) --- src/slashers/VetoableSlasher.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 949c46c2..c7206278 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -20,8 +20,7 @@ contract VetoableSlasher is SlasherBase { constructor( IAllocationManager _allocationManager, - ISlashingRegistryCoordinator _slashingRegistryCoordinator, - address _slasher + ISlashingRegistryCoordinator _slashingRegistryCoordinator ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} function initialize(address _vetoCommittee, address _slasher) external virtual initializer { @@ -30,7 +29,7 @@ contract VetoableSlasher is SlasherBase { } function queueSlashingRequest( - IAllocationManager.SlashingParams memory params + IAllocationManager.SlashingParams calldata params ) external virtual onlySlasher { _queueSlashingRequest(params); } @@ -63,7 +62,7 @@ contract VetoableSlasher is SlasherBase { } function _queueSlashingRequest( - IAllocationManager.SlashingParams memory params + IAllocationManager.SlashingParams calldata params ) internal virtual { uint256 requestId = nextRequestId++; slashingRequests[requestId] = SlashingRequest({ From 293e91916fd8c3d1b1e0e02c7148e653cab1b471 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:49:52 -0500 Subject: [PATCH 04/13] chore: use inheritdoc for SlashingRegCoord (#378) --- src/SlashingRegistryCoordinator.sol | 76 +++---------------- .../ISlashingRegistryCoordinator.sol | 23 ++++-- 2 files changed, 30 insertions(+), 69 deletions(-) diff --git a/src/SlashingRegistryCoordinator.sol b/src/SlashingRegistryCoordinator.sol index 101fad15..482be871 100644 --- a/src/SlashingRegistryCoordinator.sol +++ b/src/SlashingRegistryCoordinator.sol @@ -116,17 +116,7 @@ contract SlashingRegistryCoordinator is m2QuorumsDisabled = true; } - /** - * @notice Creates a quorum and initializes it in each registry contract - * @param operatorSetParams configures the quorum's max operator count and churn parameters - * @param minimumStake sets the minimum stake required for an operator to register or remain - * registered - * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to - * calculate an operator's stake weight for the quorum - * @dev For m2 AVS this function has the same behavior as createQuorum before - * For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set - * - */ + /// @inheritdoc ISlashingRegistryCoordinator function createTotalDelegatedStakeQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, @@ -141,6 +131,7 @@ contract SlashingRegistryCoordinator is ); } + /// @inheritdoc ISlashingRegistryCoordinator function createSlashableStakeQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, @@ -157,6 +148,7 @@ contract SlashingRegistryCoordinator is ); } + /// @inheritdoc ISlashingRegistryCoordinator function registerOperator( address operator, uint32[] memory operatorSetIds, @@ -238,6 +230,7 @@ contract SlashingRegistryCoordinator is } } + /// @inheritdoc ISlashingRegistryCoordinator function deregisterOperator( address operator, uint32[] memory operatorSetIds @@ -247,12 +240,7 @@ contract SlashingRegistryCoordinator is _deregisterOperator(operator, quorumNumbers); } - /** - * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator - * is found to be below the minimum stake for the quorum, they are deregistered. - * @dev stakes are queried from the Eigenlayer core DelegationManager contract - * @param operators a list of operator addresses to update - */ + /// @inheritdoc ISlashingRegistryCoordinator function updateOperators( address[] memory operators ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { @@ -268,20 +256,7 @@ contract SlashingRegistryCoordinator is } } - /** - * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. - * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered - * operators were updated. - * @dev stakes are queried from the Eigenlayer core DelegationManager contract - * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. - * @dev Each list of operator addresses MUST be sorted in ascending order - * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum - * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated - * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` - * for the corresponding quorum. - * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to - * this method is broadcast (but before it is executed), the method will fail - */ + /// @inheritdoc ISlashingRegistryCoordinator function updateOperatorsForQuorum( address[][] memory operatorsPerQuorum, bytes calldata quorumNumbers @@ -336,10 +311,7 @@ contract SlashingRegistryCoordinator is } } - /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param socket is the new socket of the operator - */ + /// @inheritdoc ISlashingRegistryCoordinator function updateSocket( string memory socket ) external { @@ -353,12 +325,7 @@ contract SlashingRegistryCoordinator is * */ - /** - * @notice Forcibly deregisters an operator from one or more quorums - * @param operator the operator to eject - * @param quorumNumbers the quorum numbers to eject the operator from - * @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset - */ + /// @inheritdoc ISlashingRegistryCoordinator function ejectOperator(address operator, bytes memory quorumNumbers) external onlyEjector { lastEjectionTimestamp[operator] = block.timestamp; @@ -385,13 +352,7 @@ contract SlashingRegistryCoordinator is * */ - /** - * @notice Updates an existing quorum's configuration with a new max operator count - * and operator churn parameters - * @param quorumNumber the quorum number to update - * @param operatorSetParams the new config - * @dev only callable by the owner - */ + /// @inheritdoc ISlashingRegistryCoordinator function setOperatorSetParams( uint8 quorumNumber, OperatorSetParam memory operatorSetParams @@ -411,34 +372,21 @@ contract SlashingRegistryCoordinator is _setChurnApprover(_churnApprover); } - /** - * @notice Sets the ejector, which can force-deregister operators from quorums - * @param _ejector the new ejector - * @dev only callable by the owner - */ + /// @inheritdoc ISlashingRegistryCoordinator function setEjector( address _ejector ) external onlyOwner { _setEjector(_ejector); } - /** - * @notice Sets the account identifier for this AVS (used for UAM integration in EigenLayer) - * @param _accountIdentifier the new account identifier - * @dev only callable by the owner - */ + /// @inheritdoc ISlashingRegistryCoordinator function setAccountIdentifier( address _accountIdentifier ) external onlyOwner { _setAccountIdentifier(_accountIdentifier); } - /** - * @notice Sets the ejection cooldown, which is the time an operator must wait in - * seconds afer ejection before registering for any quorum - * @param _ejectionCooldown the new ejection cooldown in seconds - * @dev only callable by the owner - */ + /// @inheritdoc ISlashingRegistryCoordinator function setEjectionCooldown( uint256 _ejectionCooldown ) external onlyOwner { diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol index 70b74af8..4984535a 100644 --- a/src/interfaces/ISlashingRegistryCoordinator.sol +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -352,12 +352,15 @@ interface ISlashingRegistryCoordinator is * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered * operators were updated. + * @dev stakes are queried from the Eigenlayer core DelegationManager contract * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. - * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated. - * @dev Each list of operator addresses MUST be sorted in ascending order. - * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum. - * @dev Stakes are queried from the Eigenlayer core DelegationManager contract. - * @dev Will revert if an operator registers/deregisters for any quorum in `quorumNumbers` after transaction broadcast but before execution. + * @dev Each list of operator addresses MUST be sorted in ascending order + * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum + * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated + * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` + * for the corresponding quorum. + * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to + * this method is broadcast (but before it is executed), the method will fail */ function updateOperatorsForQuorum( address[][] memory operatorsPerQuorum, @@ -453,6 +456,16 @@ interface ISlashingRegistryCoordinator is uint256 _ejectionCooldown ) external; + /** + * @notice Updates the account identifier for this AVS (used for UAM integration in EigenLayer) + * @param _accountIdentifier The new account identifier address + * @dev Can only be called by the contract owner + * @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. This value should only be set once. + */ + function setAccountIdentifier( + address _accountIdentifier + ) external; + /// VIEW /** From 80535455bf2c8a65f1373204450c6933e606f90d Mon Sep 17 00:00:00 2001 From: steven Date: Sat, 1 Feb 2025 20:39:17 -0500 Subject: [PATCH 05/13] test: slasher contracts and add some natspec --- src/slashers/InstantSlasher.sol | 10 +- src/slashers/VetoableSlasher.sol | 30 +++++ src/slashers/base/SlasherBase.sol | 16 +++ test/unit/InstantSlasher.t.sol | 96 ++++++++++++++++ test/unit/VetoableSlasher.t.sol | 182 ++++++++++++++++++++++++++++++ 5 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 test/unit/InstantSlasher.t.sol create mode 100644 test/unit/VetoableSlasher.t.sol diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol index 10276bff..5827dd07 100644 --- a/src/slashers/InstantSlasher.sol +++ b/src/slashers/InstantSlasher.sol @@ -4,9 +4,12 @@ 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 {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; +/// @title InstantSlasher +/// @notice A slashing contract that immediately executes slashing requests without any delay or veto period +/// @dev Extends SlasherBase to provide access controlled slashing functionality contract InstantSlasher is SlasherBase { constructor( IAllocationManager _allocationManager, @@ -14,12 +17,17 @@ contract InstantSlasher is SlasherBase { address _slasher ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} + /// @notice Initializes the contract with a slasher address + /// @param _slasher Address authorized to create and fulfill slashing requests function initialize( address _slasher ) external initializer { __SlasherBase_init(_slasher); } + /// @notice Immediately executes a slashing request + /// @param _slashingParams Parameters defining the slashing request including operator and amount + /// @dev Can only be called by the authorized slasher function fulfillSlashingRequest( IAllocationManager.SlashingParams memory _slashingParams ) external virtual onlySlasher { diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index c7206278..6e154ba9 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -7,12 +7,21 @@ import {IAllocationManager} from import {SlasherBase} from "./base/SlasherBase.sol"; import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; +/// @title VetoableSlasher +/// @notice A slashing contract that implements a veto mechanism allowing a designated committee to cancel slashing requests +/// @dev Extends SlasherBase and adds a veto period during which slashing requests can be cancelled contract VetoableSlasher is SlasherBase { + /// @notice Duration of the veto period during which the veto committee can cancel slashing requests + /// @dev Set to 3 days (259,200 seconds) uint256 public constant VETO_PERIOD = 3 days; + + /// @notice Address of the committee that has veto power over slashing requests address public vetoCommittee; + /// @notice Mapping of request IDs to their corresponding slashing request details mapping(uint256 => SlashingRequest) public slashingRequests; + /// @notice Modifier to restrict function access to only the veto committee modifier onlyVetoCommittee() { _checkVetoCommittee(msg.sender); _; @@ -23,17 +32,26 @@ contract VetoableSlasher is SlasherBase { ISlashingRegistryCoordinator _slashingRegistryCoordinator ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} + /// @notice Initializes the contract with a veto committee and slasher address + /// @param _vetoCommittee Address of the committee that can veto slashing requests + /// @param _slasher Address authorized to create and fulfill slashing requests function initialize(address _vetoCommittee, address _slasher) external virtual initializer { __SlasherBase_init(_slasher); vetoCommittee = _vetoCommittee; } + /// @notice Queues a new slashing request + /// @param params Parameters defining the slashing request including operator and amount + /// @dev Can only be called by the authorized slasher function queueSlashingRequest( IAllocationManager.SlashingParams calldata params ) external virtual onlySlasher { _queueSlashingRequest(params); } + /// @notice Cancels a pending slashing request + /// @param requestId 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 ) external virtual onlyVetoCommittee { @@ -49,6 +67,9 @@ contract VetoableSlasher is SlasherBase { _cancelSlashingRequest(requestId); } + /// @notice Executes a slashing request after the veto period has passed + /// @param requestId 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 ) external virtual onlySlasher { @@ -61,6 +82,9 @@ contract VetoableSlasher is SlasherBase { _fulfillSlashingRequest(requestId, request.params); } + /// @notice Internal function to create and store a new slashing request + /// @param params Parameters defining the slashing request + /// @dev Emits a SlashingRequested event function _queueSlashingRequest( IAllocationManager.SlashingParams calldata params ) internal virtual { @@ -76,6 +100,9 @@ contract VetoableSlasher is SlasherBase { ); } + /// @notice Internal function to mark a slashing request as cancelled + /// @param requestId The ID of the slashing request to cancel + /// @dev Emits a SlashingRequestCancelled event function _cancelSlashingRequest( uint256 requestId ) internal virtual { @@ -83,6 +110,9 @@ contract VetoableSlasher is SlasherBase { emit SlashingRequestCancelled(requestId); } + /// @notice Internal function to verify if an account is the veto committee + /// @param account The address to check + /// @dev Reverts if the account is not the veto committee function _checkVetoCommittee( address account ) internal view virtual { diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index a6917d39..b8eb615e 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -9,12 +9,19 @@ import { } from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +/// @title SlasherBase +/// @notice Base contract for implementing slashing functionality in EigenLayer middleware +/// @dev Provides core slashing functionality and interfaces with EigenLayer's AllocationManager abstract contract SlasherBase is Initializable, SlasherStorage { + /// @notice Ensures only the authorized slasher can call certain functions modifier onlySlasher() { _checkSlasher(msg.sender); _; } + /// @notice Constructs the base slasher contract + /// @param _allocationManager The EigenLayer allocation manager contract + /// @param _registryCoordinator The registry coordinator for this middleware constructor( IAllocationManager _allocationManager, ISlashingRegistryCoordinator _registryCoordinator @@ -22,12 +29,18 @@ abstract contract SlasherBase is Initializable, SlasherStorage { _disableInitializers(); } + /// @notice Initializes the slasher contract with authorized slasher address + /// @param _slasher Address authorized to create and fulfill slashing requests function __SlasherBase_init( address _slasher ) internal onlyInitializing { slasher = _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 @@ -45,6 +58,9 @@ abstract contract SlasherBase is Initializable, SlasherStorage { ); } + /// @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 function _checkSlasher( address account ) internal view virtual { diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol new file mode 100644 index 00000000..eff081fc --- /dev/null +++ b/test/unit/InstantSlasher.t.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {Test} from "forge-std/Test.sol"; +import {InstantSlasher} from "../../src/slashers/InstantSlasher.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {ISlasher, ISlasherTypes, ISlasherErrors} from "../../src/interfaces/ISlasher.sol"; +import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; + +contract InstantSlasherTest is Test { + InstantSlasher public instantSlasher; + InstantSlasher public instantSlasherImplementation; + ProxyAdmin public proxyAdmin; + EmptyContract public emptyContract; + + address public allocationManager; + address public slashingRegistryCoordinator; + address public slasher; + address public operator; + IStrategy public mockStrategy; + address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); + + function setUp() public { + allocationManager = address(0x1); + slasher = address(0x3); + operator = address(0x4); + mockStrategy = IStrategy(address(0x5)); + slashingRegistryCoordinator = address(0x6); + + // Deploy proxy admin + vm.startPrank(proxyAdminOwner); + proxyAdmin = new ProxyAdmin(); + emptyContract = new EmptyContract(); + + // Deploy instant slasher behind proxy + instantSlasher = InstantSlasher( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + // Deploy implementation and upgrade + instantSlasherImplementation = new InstantSlasher( + IAllocationManager(allocationManager), + ISlashingRegistryCoordinator(slashingRegistryCoordinator), + slasher + ); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(instantSlasher))), + address(instantSlasherImplementation) + ); + vm.stopPrank(); + + // Initialize + instantSlasher.initialize(slasher); + } + + function test_initialization() public { + assertEq(instantSlasher.slasher(), slasher); + } + + function _createMockSlashingParams() internal view returns (IAllocationManagerTypes.SlashingParams memory) { + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = mockStrategy; + + uint256[] memory wadsToSlash = new uint256[](1); + wadsToSlash[0] = 0.5e18; // 50% slash + + return IAllocationManagerTypes.SlashingParams({ + operator: operator, + operatorSetId: 1, + strategies: strategies, + wadsToSlash: wadsToSlash, + description: "Test slashing" + }); + } + + function test_fulfillSlashingRequest_revert_notSlasher() public { + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + vm.expectRevert(ISlasherErrors.OnlySlasher.selector); + instantSlasher.fulfillSlashingRequest(params); + } + + function test_fulfillSlashingRequest() public { + vm.skip(true); // TODO: + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + instantSlasher.fulfillSlashingRequest(params); + } +} diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol new file mode 100644 index 00000000..81c4111f --- /dev/null +++ b/test/unit/VetoableSlasher.t.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {Test} from "forge-std/Test.sol"; +import {VetoableSlasher} from "../../src/slashers/VetoableSlasher.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {ISlasher, ISlasherTypes, ISlasherErrors} from "../../src/interfaces/ISlasher.sol"; +import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; + +contract VetoableSlasherTest is Test { + VetoableSlasher public vetoableSlasher; + VetoableSlasher public vetoableSlasherImplementation; + ProxyAdmin public proxyAdmin; + EmptyContract public emptyContract; + + address public allocationManager; + address public slashingRegistryCoordinator; + address public vetoCommittee; + address public slasher; + address public operator; + IStrategy public mockStrategy; + address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); + + uint256 constant VETO_PERIOD = 3 days; + + function setUp() public { + allocationManager = address(0x1); + vetoCommittee = address(0x2); + slasher = address(0x3); + operator = address(0x4); + mockStrategy = IStrategy(address(0x5)); + slashingRegistryCoordinator = address(0x6); + + // Deploy proxy admin + vm.startPrank(proxyAdminOwner); + proxyAdmin = new ProxyAdmin(); + emptyContract = new EmptyContract(); + + // Deploy vetoable slasher behind proxy + vetoableSlasher = VetoableSlasher( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + // Deploy implementation and upgrade + vetoableSlasherImplementation = new VetoableSlasher( + IAllocationManager(allocationManager), + ISlashingRegistryCoordinator(slashingRegistryCoordinator) + ); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(vetoableSlasher))), + address(vetoableSlasherImplementation) + ); + vm.stopPrank(); + + // Initialize + vetoableSlasher.initialize(vetoCommittee, slasher); + } + + function test_initialization() public { + assertEq(vetoableSlasher.vetoCommittee(), vetoCommittee); + assertEq(vetoableSlasher.VETO_PERIOD(), VETO_PERIOD); + } + + function _createMockSlashingParams() internal view returns (IAllocationManagerTypes.SlashingParams memory) { + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = mockStrategy; + + uint256[] memory wadsToSlash = new uint256[](1); + wadsToSlash[0] = 0.5e18; // 50% slash + + return IAllocationManagerTypes.SlashingParams({ + operator: operator, + operatorSetId: 1, + strategies: strategies, + wadsToSlash: wadsToSlash, + description: "Test slashing" + }); + } + + function test_queueSlashingRequest_revert_notSlasher() public { + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + vm.expectRevert(ISlasherErrors.OnlySlasher.selector); + vetoableSlasher.queueSlashingRequest(params); + } + + function test_queueSlashingRequest() public { + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + vetoableSlasher.queueSlashingRequest(params); + + // Get the request from storage and verify + (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); + ISlasherTypes.SlashingRequest memory request = ISlasherTypes.SlashingRequest(params, requestTimestamp, status); + assertEq(resultParams.operator, operator); + assertEq(resultParams.operatorSetId, 1); + assertEq(resultParams.wadsToSlash[0], 0.5e18); + assertEq(resultParams.description, "Test slashing"); + assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Requested)); + assertEq(requestTimestamp, block.timestamp); + } + + function test_cancelSlashingRequest_revert_notVetoCommittee() public { + // First queue a request + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + vetoableSlasher.queueSlashingRequest(params); + + // Try to cancel from non-veto committee + vm.expectRevert(ISlasherErrors.OnlyVetoCommittee.selector); + vetoableSlasher.cancelSlashingRequest(0); + } + + function test_cancelSlashingRequest_revert_afterVetoPeriod() public { + // First queue a request + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + vetoableSlasher.queueSlashingRequest(params); + + // Move time past veto period + vm.warp(block.timestamp + VETO_PERIOD + 1); + + // Try to cancel + vm.prank(vetoCommittee); + vm.expectRevert(ISlasherErrors.VetoPeriodPassed.selector); + vetoableSlasher.cancelSlashingRequest(0); + } + + function test_cancelSlashingRequest() public { + // First queue a request + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + vetoableSlasher.queueSlashingRequest(params); + + // Cancel within veto period + vm.prank(vetoCommittee); + vetoableSlasher.cancelSlashingRequest(0); + + // Verify request is cancelled + (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); + assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Cancelled)); + } + + function test_fulfillSlashingRequest_revert_beforeVetoPeriod() public { + // First queue a request + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + vetoableSlasher.queueSlashingRequest(params); + + // Try to fulfill before veto period + vm.prank(slasher); + vm.expectRevert(ISlasherErrors.VetoPeriodNotPassed.selector); + vetoableSlasher.fulfillSlashingRequest(0); + } + + function test_fulfillSlashingRequest() public { + vm.skip(true); + IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + + vm.prank(slasher); + vetoableSlasher.queueSlashingRequest(params); + + vm.warp(block.timestamp + VETO_PERIOD + 1); + + vm.prank(slasher); + vetoableSlasher.fulfillSlashingRequest(0); + + (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); + assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Completed)); + } +} \ No newline at end of file From 048d3973e07fcb30620f1a7185fc242b664af77c Mon Sep 17 00:00:00 2001 From: steven Date: Sat, 1 Feb 2025 20:40:36 -0500 Subject: [PATCH 06/13] chore: unneeded comment --- src/slashers/VetoableSlasher.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 6e154ba9..4ac05974 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -84,7 +84,6 @@ contract VetoableSlasher is SlasherBase { /// @notice Internal function to create and store a new slashing request /// @param params Parameters defining the slashing request - /// @dev Emits a SlashingRequested event function _queueSlashingRequest( IAllocationManager.SlashingParams calldata params ) internal virtual { @@ -102,7 +101,6 @@ contract VetoableSlasher is SlasherBase { /// @notice Internal function to mark a slashing request as cancelled /// @param requestId The ID of the slashing request to cancel - /// @dev Emits a SlashingRequestCancelled event function _cancelSlashingRequest( uint256 requestId ) internal virtual { From 252ce513879424f6189cfaa069252216a7306aba Mon Sep 17 00:00:00 2001 From: steven Date: Sat, 1 Feb 2025 20:42:14 -0500 Subject: [PATCH 07/13] chore: redundant comments --- test/unit/InstantSlasher.t.sol | 4 ---- test/unit/VetoableSlasher.t.sol | 15 --------------- 2 files changed, 19 deletions(-) diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index eff081fc..9218c1a9 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -31,19 +31,16 @@ contract InstantSlasherTest is Test { mockStrategy = IStrategy(address(0x5)); slashingRegistryCoordinator = address(0x6); - // Deploy proxy admin vm.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); emptyContract = new EmptyContract(); - // Deploy instant slasher behind proxy instantSlasher = InstantSlasher( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - // Deploy implementation and upgrade instantSlasherImplementation = new InstantSlasher( IAllocationManager(allocationManager), ISlashingRegistryCoordinator(slashingRegistryCoordinator), @@ -56,7 +53,6 @@ contract InstantSlasherTest is Test { ); vm.stopPrank(); - // Initialize instantSlasher.initialize(slasher); } diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index 81c4111f..7fc66436 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -35,19 +35,16 @@ contract VetoableSlasherTest is Test { mockStrategy = IStrategy(address(0x5)); slashingRegistryCoordinator = address(0x6); - // Deploy proxy admin vm.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); emptyContract = new EmptyContract(); - // Deploy vetoable slasher behind proxy vetoableSlasher = VetoableSlasher( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - // Deploy implementation and upgrade vetoableSlasherImplementation = new VetoableSlasher( IAllocationManager(allocationManager), ISlashingRegistryCoordinator(slashingRegistryCoordinator) @@ -59,7 +56,6 @@ contract VetoableSlasherTest is Test { ); vm.stopPrank(); - // Initialize vetoableSlasher.initialize(vetoCommittee, slasher); } @@ -96,7 +92,6 @@ contract VetoableSlasherTest is Test { vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - // Get the request from storage and verify (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); ISlasherTypes.SlashingRequest memory request = ISlasherTypes.SlashingRequest(params, requestTimestamp, status); assertEq(resultParams.operator, operator); @@ -108,57 +103,47 @@ contract VetoableSlasherTest is Test { } function test_cancelSlashingRequest_revert_notVetoCommittee() public { - // First queue a request IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - // Try to cancel from non-veto committee vm.expectRevert(ISlasherErrors.OnlyVetoCommittee.selector); vetoableSlasher.cancelSlashingRequest(0); } function test_cancelSlashingRequest_revert_afterVetoPeriod() public { - // First queue a request IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - // Move time past veto period vm.warp(block.timestamp + VETO_PERIOD + 1); - // Try to cancel vm.prank(vetoCommittee); vm.expectRevert(ISlasherErrors.VetoPeriodPassed.selector); vetoableSlasher.cancelSlashingRequest(0); } function test_cancelSlashingRequest() public { - // First queue a request IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - // Cancel within veto period vm.prank(vetoCommittee); vetoableSlasher.cancelSlashingRequest(0); - // Verify request is cancelled (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Cancelled)); } function test_fulfillSlashingRequest_revert_beforeVetoPeriod() public { - // First queue a request IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - // Try to fulfill before veto period vm.prank(slasher); vm.expectRevert(ISlasherErrors.VetoPeriodNotPassed.selector); vetoableSlasher.fulfillSlashingRequest(0); From ccc98809883e37a6c9125bfc192438fecb2a908c Mon Sep 17 00:00:00 2001 From: steven Date: Sat, 1 Feb 2025 21:06:57 -0500 Subject: [PATCH 08/13] test: more extensive setup for slashOperator --- test/unit/InstantSlasher.t.sol | 110 +++++++++++++++++++++++++++++--- test/unit/VetoableSlasher.t.sol | 102 +++++++++++++++++++++++++++-- 2 files changed, 196 insertions(+), 16 deletions(-) diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index 9218c1a9..ffee526b 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -10,50 +10,143 @@ import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegist import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; +import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {DelegationMock} from "../mocks/DelegationMock.sol"; +import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; contract InstantSlasherTest is Test { InstantSlasher public instantSlasher; InstantSlasher public instantSlasherImplementation; ProxyAdmin public proxyAdmin; EmptyContract public emptyContract; + AllocationManager public allocationManager; + AllocationManager public allocationManagerImplementation; + PermissionController public permissionController; + PauserRegistry public pauserRegistry; + DelegationMock public delegationMock; + SlashingRegistryCoordinator public slashingRegistryCoordinator; + SlashingRegistryCoordinator public slashingRegistryCoordinatorImplementation; - address public allocationManager; - address public slashingRegistryCoordinator; address public slasher; + address public serviceManager; address public operator; IStrategy public mockStrategy; address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); + address public pauser = address(uint160(uint256(keccak256("pauser")))); + address public unpauser = address(uint160(uint256(keccak256("unpauser")))); + address public churnApprover = address(uint160(uint256(keccak256("churnApprover")))); + address public ejector = address(uint160(uint256(keccak256("ejector")))); + + uint32 constant DEALLOCATION_DELAY = 7 days; + uint32 constant ALLOCATION_CONFIGURATION_DELAY = 1 days; function setUp() public { - allocationManager = address(0x1); + serviceManager = address(0x2); slasher = address(0x3); operator = address(0x4); mockStrategy = IStrategy(address(0x5)); - slashingRegistryCoordinator = address(0x6); vm.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); emptyContract = new EmptyContract(); - instantSlasher = InstantSlasher( + address[] memory pausers = new address[](1); + pausers[0] = pauser; + pauserRegistry = new PauserRegistry(pausers, unpauser); + + delegationMock = new DelegationMock(); + + permissionController = new PermissionController(); + + allocationManagerImplementation = new AllocationManager( + delegationMock, + pauserRegistry, + permissionController, + DEALLOCATION_DELAY, + ALLOCATION_CONFIGURATION_DELAY + ); + + allocationManager = AllocationManager( + address( + new TransparentUpgradeableProxy( + address(allocationManagerImplementation), + address(proxyAdmin), + "" + ) + ) + ); + + allocationManager.initialize(proxyAdminOwner, 0); + + // Deploy and set up SlashingRegistryCoordinator + slashingRegistryCoordinatorImplementation = new SlashingRegistryCoordinator( + IStakeRegistry(address(0)), // Mock stake registry + IBLSApkRegistry(address(0)), // Mock BLS APK registry + IIndexRegistry(address(0)), // Mock index registry + ISocketRegistry(address(0)), // Mock socket registry + allocationManager, + pauserRegistry + ); + + slashingRegistryCoordinator = SlashingRegistryCoordinator( address( - new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + new TransparentUpgradeableProxy( + address(slashingRegistryCoordinatorImplementation), + address(proxyAdmin), + "" + ) ) ); + slashingRegistryCoordinator.initialize( + proxyAdminOwner, + churnApprover, + ejector, + 0, // Initial paused status + serviceManager + ); + + vm.stopPrank(); + instantSlasherImplementation = new InstantSlasher( - IAllocationManager(allocationManager), + allocationManager, ISlashingRegistryCoordinator(slashingRegistryCoordinator), slasher ); + instantSlasher = InstantSlasher( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + + vm.startPrank(proxyAdminOwner); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(instantSlasher))), address(instantSlasherImplementation) ); vm.stopPrank(); + instantSlasher.initialize(slasher); + + vm.prank(serviceManager); + permissionController.setAppointee( + address(serviceManager), + address(instantSlasher), + address(allocationManager), + AllocationManager.slashOperator.selector + ); } function test_initialization() public { @@ -83,9 +176,8 @@ contract InstantSlasherTest is Test { } function test_fulfillSlashingRequest() public { - vm.skip(true); // TODO: + vm.skip(true); /// TODO: IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); - vm.prank(slasher); instantSlasher.fulfillSlashingRequest(params); } diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index 7fc66436..95eaa22c 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -10,46 +10,126 @@ import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegist import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; +import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {DelegationMock} from "../mocks/DelegationMock.sol"; +import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; contract VetoableSlasherTest is Test { VetoableSlasher public vetoableSlasher; VetoableSlasher public vetoableSlasherImplementation; ProxyAdmin public proxyAdmin; EmptyContract public emptyContract; + AllocationManager public allocationManager; + AllocationManager public allocationManagerImplementation; + PermissionController public permissionController; + PauserRegistry public pauserRegistry; + DelegationMock public delegationMock; + SlashingRegistryCoordinator public slashingRegistryCoordinator; + SlashingRegistryCoordinator public slashingRegistryCoordinatorImplementation; - address public allocationManager; - address public slashingRegistryCoordinator; address public vetoCommittee; address public slasher; + address public serviceManager; address public operator; IStrategy public mockStrategy; address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); + address public pauser = address(uint160(uint256(keccak256("pauser")))); + address public unpauser = address(uint160(uint256(keccak256("unpauser")))); + address public churnApprover = address(uint160(uint256(keccak256("churnApprover")))); + address public ejector = address(uint160(uint256(keccak256("ejector")))); uint256 constant VETO_PERIOD = 3 days; + uint32 constant DEALLOCATION_DELAY = 7 days; + uint32 constant ALLOCATION_CONFIGURATION_DELAY = 1 days; function setUp() public { - allocationManager = address(0x1); + serviceManager = address(0x1); vetoCommittee = address(0x2); slasher = address(0x3); operator = address(0x4); mockStrategy = IStrategy(address(0x5)); - slashingRegistryCoordinator = address(0x6); vm.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); emptyContract = new EmptyContract(); - vetoableSlasher = VetoableSlasher( + address[] memory pausers = new address[](1); + pausers[0] = pauser; + pauserRegistry = new PauserRegistry(pausers, unpauser); + + delegationMock = new DelegationMock(); + + permissionController = new PermissionController(); + + allocationManagerImplementation = new AllocationManager( + delegationMock, + pauserRegistry, + permissionController, + DEALLOCATION_DELAY, + ALLOCATION_CONFIGURATION_DELAY + ); + + allocationManager = AllocationManager( + address( + new TransparentUpgradeableProxy( + address(allocationManagerImplementation), + address(proxyAdmin), + "" + ) + ) + ); + + allocationManager.initialize(proxyAdminOwner, 0); + + // Deploy and set up SlashingRegistryCoordinator + slashingRegistryCoordinatorImplementation = new SlashingRegistryCoordinator( + IStakeRegistry(address(0)), // Mock stake registry + IBLSApkRegistry(address(0)), // Mock BLS APK registry + IIndexRegistry(address(0)), // Mock index registry + ISocketRegistry(address(0)), // Mock socket registry + allocationManager, + pauserRegistry + ); + + slashingRegistryCoordinator = SlashingRegistryCoordinator( address( - new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + new TransparentUpgradeableProxy( + address(slashingRegistryCoordinatorImplementation), + address(proxyAdmin), + "" + ) ) ); + slashingRegistryCoordinator.initialize( + proxyAdminOwner, + churnApprover, + ejector, + 0, // Initial paused status + serviceManager + ); + vetoableSlasherImplementation = new VetoableSlasher( IAllocationManager(allocationManager), ISlashingRegistryCoordinator(slashingRegistryCoordinator) ); + vetoableSlasher = VetoableSlasher( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(vetoableSlasher))), address(vetoableSlasherImplementation) @@ -57,6 +137,14 @@ contract VetoableSlasherTest is Test { vm.stopPrank(); vetoableSlasher.initialize(vetoCommittee, slasher); + + vm.prank(serviceManager); + permissionController.setAppointee( + address(serviceManager), + address(vetoableSlasher), + address(allocationManager), + AllocationManager.slashOperator.selector + ); } function test_initialization() public { @@ -150,7 +238,7 @@ contract VetoableSlasherTest is Test { } function test_fulfillSlashingRequest() public { - vm.skip(true); + vm.skip(true); /// TODO: IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); vm.prank(slasher); From c7e8a657103498dde656d3b9a5d1c2ef4a403b44 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 5 Feb 2025 09:46:57 -0500 Subject: [PATCH 09/13] test: integrate core deployment lib --- test/unit/InstantSlasher.t.sol | 75 ++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index ffee526b..63e0dc44 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -13,25 +13,24 @@ import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.s import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; +import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; contract InstantSlasherTest is Test { InstantSlasher public instantSlasher; InstantSlasher public instantSlasherImplementation; ProxyAdmin public proxyAdmin; EmptyContract public emptyContract; - AllocationManager public allocationManager; - AllocationManager public allocationManagerImplementation; - PermissionController public permissionController; - PauserRegistry public pauserRegistry; - DelegationMock public delegationMock; SlashingRegistryCoordinator public slashingRegistryCoordinator; SlashingRegistryCoordinator public slashingRegistryCoordinatorImplementation; + CoreDeploymentLib.DeploymentData public coreDeployment; + PauserRegistry public pauserRegistry; address public slasher; address public serviceManager; @@ -56,33 +55,50 @@ contract InstantSlasherTest is Test { proxyAdmin = new ProxyAdmin(); emptyContract = new EmptyContract(); + // Setup PauserRegistry address[] memory pausers = new address[](1); pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationMock(); + // Setup core deployment config + CoreDeploymentLib.DeploymentConfigData memory configData; + configData.strategyManager.initialOwner = proxyAdminOwner; + configData.strategyManager.initialStrategyWhitelister = proxyAdminOwner; + configData.strategyManager.initPausedStatus = 0; - permissionController = new PermissionController(); + configData.delegationManager.initialOwner = proxyAdminOwner; + configData.delegationManager.minWithdrawalDelayBlocks = 50400; + configData.delegationManager.initPausedStatus = 0; - allocationManagerImplementation = new AllocationManager( - delegationMock, - pauserRegistry, - permissionController, - DEALLOCATION_DELAY, - ALLOCATION_CONFIGURATION_DELAY - ); + configData.eigenPodManager.initialOwner = proxyAdminOwner; + configData.eigenPodManager.initPausedStatus = 0; - allocationManager = AllocationManager( - address( - new TransparentUpgradeableProxy( - address(allocationManagerImplementation), - address(proxyAdmin), - "" - ) - ) - ); + configData.allocationManager.initialOwner = proxyAdminOwner; + configData.allocationManager.deallocationDelay = DEALLOCATION_DELAY; + configData.allocationManager.allocationConfigurationDelay = ALLOCATION_CONFIGURATION_DELAY; + configData.allocationManager.initPausedStatus = 0; + + configData.strategyFactory.initialOwner = proxyAdminOwner; + configData.strategyFactory.initPausedStatus = 0; + + configData.avsDirectory.initialOwner = proxyAdminOwner; + configData.avsDirectory.initPausedStatus = 0; - allocationManager.initialize(proxyAdminOwner, 0); + configData.rewardsCoordinator.initialOwner = proxyAdminOwner; + configData.rewardsCoordinator.rewardsUpdater = address(0x14dC79964da2C08b23698B3D3cc7Ca32193d9955); + configData.rewardsCoordinator.initPausedStatus = 0; + configData.rewardsCoordinator.activationDelay = 0; + configData.rewardsCoordinator.defaultSplitBips = 1000; + configData.rewardsCoordinator.calculationIntervalSeconds = 86400; + configData.rewardsCoordinator.maxRewardsDuration = 864000; + configData.rewardsCoordinator.maxRetroactiveLength = 86400; + configData.rewardsCoordinator.maxFutureLength = 86400; + configData.rewardsCoordinator.genesisRewardsTimestamp = 1672531200; + + configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); // Mock ETH POS deposit contract address to avoid revert + + // Deploy core contracts + coreDeployment = CoreDeploymentLib.deployContracts(address(proxyAdmin), configData); // Deploy and set up SlashingRegistryCoordinator slashingRegistryCoordinatorImplementation = new SlashingRegistryCoordinator( @@ -90,8 +106,8 @@ contract InstantSlasherTest is Test { IBLSApkRegistry(address(0)), // Mock BLS APK registry IIndexRegistry(address(0)), // Mock index registry ISocketRegistry(address(0)), // Mock socket registry - allocationManager, - pauserRegistry + IAllocationManager(coreDeployment.allocationManager), + IPauserRegistry(address(pauserRegistry)) ); slashingRegistryCoordinator = SlashingRegistryCoordinator( @@ -115,7 +131,7 @@ contract InstantSlasherTest is Test { vm.stopPrank(); instantSlasherImplementation = new InstantSlasher( - allocationManager, + IAllocationManager(coreDeployment.allocationManager), ISlashingRegistryCoordinator(slashingRegistryCoordinator), slasher ); @@ -137,14 +153,13 @@ contract InstantSlasherTest is Test { ); vm.stopPrank(); - instantSlasher.initialize(slasher); vm.prank(serviceManager); - permissionController.setAppointee( + PermissionController(coreDeployment.permissionController).setAppointee( address(serviceManager), address(instantSlasher), - address(allocationManager), + coreDeployment.allocationManager, AllocationManager.slashOperator.selector ); } From d5dc5b05b2b98040bbba9b797311699e46df8759 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 5 Feb 2025 11:58:23 -0500 Subject: [PATCH 10/13] test: unit test slashing --- src/RegistryCoordinator.sol | 10 - src/SlashingRegistryCoordinator.sol | 10 + .../ISlashingRegistryCoordinator.sol | 6 + test/unit/InstantSlasher.t.sol | 288 ++++++++++++++++-- 4 files changed, 282 insertions(+), 32 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index c1970c08..325722ad 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -208,16 +208,6 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato return (1 << quorumCount) - 1; } - /** - * @notice Returns the message hash that an operator must sign to register their BLS public key. - * @param operator is the address of the operator registering their BLS public key - */ - function calculatePubkeyRegistrationMessageHash( - address operator - ) public view returns (bytes32) { - return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))); - } - /// @dev need to override function here since its defined in both these contracts function owner() public diff --git a/src/SlashingRegistryCoordinator.sol b/src/SlashingRegistryCoordinator.sol index 482be871..78540f2f 100644 --- a/src/SlashingRegistryCoordinator.sol +++ b/src/SlashingRegistryCoordinator.sol @@ -1077,6 +1077,16 @@ contract SlashingRegistryCoordinator is ); } + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function calculatePubkeyRegistrationMessageHash( + address operator + ) public view returns (bytes32) { + return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))); + } + /// @dev need to override function here since its defined in both these contracts function owner() public diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol index 4984535a..48989463 100644 --- a/src/interfaces/ISlashingRegistryCoordinator.sol +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -468,6 +468,12 @@ interface ISlashingRegistryCoordinator is /// VIEW + /** + * @notice Returns the hash of the message that operators must sign with their BLS key to register + * @param operator The operator's Ethereum address + */ + function calculatePubkeyRegistrationMessageHash(address operator) external view returns (bytes32); + /** * @notice Returns the operator set parameters for a given quorum. * @param quorumNumber The identifier of the quorum to query. diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index 63e0dc44..3d491c55 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -2,11 +2,16 @@ pragma solidity ^0.8.27; import {Test} from "forge-std/Test.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {InstantSlasher} from "../../src/slashers/InstantSlasher.sol"; import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISlasher, ISlasherTypes, ISlasherErrors} from "../../src/interfaces/ISlasher.sol"; import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "../../src/interfaces/IStakeRegistry.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -14,13 +19,24 @@ import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/Allocat import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; -import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {SlashingRegistryCoordinator }from "../../src/SlashingRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; +import {OperatorWalletLib, Operator, Wallet, BLSWallet, SigningKeyOperationsLib} from "../utils/OperatorWalletLib.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol"; +import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {SocketRegistry} from "../../src/SocketRegistry.sol"; contract InstantSlasherTest is Test { InstantSlasher public instantSlasher; @@ -31,10 +47,16 @@ contract InstantSlasherTest is Test { SlashingRegistryCoordinator public slashingRegistryCoordinatorImplementation; CoreDeploymentLib.DeploymentData public coreDeployment; PauserRegistry public pauserRegistry; + ERC20Mock public mockToken; + StrategyFactory public strategyFactory; + StakeRegistry public stakeRegistry; + BLSApkRegistry public blsApkRegistry; + IndexRegistry public indexRegistry; + SocketRegistry public socketRegistry; address public slasher; address public serviceManager; - address public operator; + Operator public operatorWallet; IStrategy public mockStrategy; address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); address public pauser = address(uint160(uint256(keccak256("pauser")))); @@ -48,19 +70,18 @@ contract InstantSlasherTest is Test { function setUp() public { serviceManager = address(0x2); slasher = address(0x3); - operator = address(0x4); - mockStrategy = IStrategy(address(0x5)); + operatorWallet = OperatorWalletLib.createOperator("operator"); + + mockToken = new ERC20Mock("Mock Token", "MOCK", address(this), 0); vm.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); emptyContract = new EmptyContract(); - // Setup PauserRegistry address[] memory pausers = new address[](1); pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - // Setup core deployment config CoreDeploymentLib.DeploymentConfigData memory configData; configData.strategyManager.initialOwner = proxyAdminOwner; configData.strategyManager.initialStrategyWhitelister = proxyAdminOwner; @@ -95,17 +116,66 @@ contract InstantSlasherTest is Test { configData.rewardsCoordinator.maxFutureLength = 86400; configData.rewardsCoordinator.genesisRewardsTimestamp = 1672531200; - configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); // Mock ETH POS deposit contract address to avoid revert + configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); - // Deploy core contracts coreDeployment = CoreDeploymentLib.deployContracts(address(proxyAdmin), configData); - // Deploy and set up SlashingRegistryCoordinator + address strategyManagerOwner = Ownable(coreDeployment.strategyManager).owner(); + vm.stopPrank(); + + vm.startPrank(strategyManagerOwner); + IStrategyManager(coreDeployment.strategyManager).setStrategyWhitelister(coreDeployment.strategyFactory); + vm.stopPrank(); + + vm.startPrank(proxyAdminOwner); + mockStrategy = IStrategy(StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy(IERC20(address(mockToken)))); + + // Deploy empty proxies for all registries + stakeRegistry = StakeRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + + blsApkRegistry = BLSApkRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + + indexRegistry = IndexRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + + socketRegistry = SocketRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + slashingRegistryCoordinatorImplementation = new SlashingRegistryCoordinator( - IStakeRegistry(address(0)), // Mock stake registry - IBLSApkRegistry(address(0)), // Mock BLS APK registry - IIndexRegistry(address(0)), // Mock index registry - ISocketRegistry(address(0)), // Mock socket registry + IStakeRegistry(address(stakeRegistry)), + IBLSApkRegistry(address(blsApkRegistry)), + IIndexRegistry(address(indexRegistry)), + ISocketRegistry(address(socketRegistry)), IAllocationManager(coreDeployment.allocationManager), IPauserRegistry(address(pauserRegistry)) ); @@ -120,11 +190,41 @@ contract InstantSlasherTest is Test { ) ); + // Deploy registry implementations pointing to the coordinator + StakeRegistry stakeRegistryImplementation = new StakeRegistry( + ISlashingRegistryCoordinator(address(slashingRegistryCoordinator)), + IDelegationManager(coreDeployment.delegationManager), + IAVSDirectory(coreDeployment.avsDirectory), + IAllocationManager(coreDeployment.allocationManager) + ); + BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + IndexRegistry indexRegistryImplementation = new IndexRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + SocketRegistry socketRegistryImplementation = new SocketRegistry(IRegistryCoordinator(address(slashingRegistryCoordinator))); + + // Upgrade all registry proxies + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(stakeRegistry))), + address(stakeRegistryImplementation) + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(blsApkRegistry))), + address(blsApkRegistryImplementation) + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(indexRegistry))), + address(indexRegistryImplementation) + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(socketRegistry))), + address(socketRegistryImplementation) + ); + + // Initialize the SlashingRegistryCoordinator first slashingRegistryCoordinator.initialize( proxyAdminOwner, churnApprover, ejector, - 0, // Initial paused status + 0, serviceManager ); @@ -136,6 +236,7 @@ contract InstantSlasherTest is Test { slasher ); + vm.startPrank(proxyAdminOwner); instantSlasher = InstantSlasher( address( new TransparentUpgradeableProxy( @@ -146,7 +247,6 @@ contract InstantSlasherTest is Test { ) ); - vm.startPrank(proxyAdminOwner); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(instantSlasher))), address(instantSlasherImplementation) @@ -155,13 +255,57 @@ contract InstantSlasherTest is Test { instantSlasher.initialize(slasher); - vm.prank(serviceManager); + vm.startPrank(serviceManager); PermissionController(coreDeployment.permissionController).setAppointee( address(serviceManager), address(instantSlasher), coreDeployment.allocationManager, AllocationManager.slashOperator.selector ); + + PermissionController(coreDeployment.permissionController).setAppointee( + address(serviceManager), + address(slashingRegistryCoordinator), + coreDeployment.allocationManager, + AllocationManager.createOperatorSets.selector + ); + + vm.stopPrank(); + + uint8 quorumNumber = 0; + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = mockStrategy; + + uint96[] memory minimumStakes = new uint96[](1); + minimumStakes[0] = 1 ether; + + IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: mockStrategy, + multiplier: 1 ether + }); + + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + vm.startPrank(proxyAdminOwner); + slashingRegistryCoordinator.createSlashableStakeQuorum( + operatorSetParams, + 1 ether, + strategyParams, + 0 + ); + vm.stopPrank(); + + vm.label(address(instantSlasher), "InstantSlasher Proxy"); + vm.label(address(instantSlasherImplementation), "InstantSlasher Implementation"); + vm.label(address(slashingRegistryCoordinator), "SlashingRegistryCoordinator Proxy"); + vm.label(address(slashingRegistryCoordinatorImplementation), "SlashingRegistryCoordinator Implementation"); + vm.label(address(proxyAdmin), "ProxyAdmin"); + vm.label(coreDeployment.allocationManager, "AllocationManager Proxy"); } function test_initialization() public { @@ -176,7 +320,7 @@ contract InstantSlasherTest is Test { wadsToSlash[0] = 0.5e18; // 50% slash return IAllocationManagerTypes.SlashingParams({ - operator: operator, + operator: operatorWallet.key.addr, operatorSetId: 1, strategies: strategies, wadsToSlash: wadsToSlash, @@ -191,9 +335,109 @@ contract InstantSlasherTest is Test { } function test_fulfillSlashingRequest() public { - vm.skip(true); /// TODO: - IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + vm.skip(false); + vm.startPrank(operatorWallet.key.addr); + IDelegationManager(coreDeployment.delegationManager).registerAsOperator(address(0), 1, "metadata"); + + // Mint tokens and deposit into strategy + uint256 depositAmount = 1 ether; + mockToken.mint(operatorWallet.key.addr, depositAmount); + mockToken.approve(address(coreDeployment.strategyManager), depositAmount); + IStrategyManager(coreDeployment.strategyManager).depositIntoStrategy(mockStrategy, mockToken, depositAmount); + + // Set allocation delay + uint32 minDelay = 1; + IAllocationManager(coreDeployment.allocationManager).setAllocationDelay(operatorWallet.key.addr, minDelay); + vm.stopPrank(); + + // Assert operator has allocation delay set + (bool isSet, ) = IAllocationManager(coreDeployment.allocationManager).getAllocationDelay(operatorWallet.key.addr); + assertFalse(isSet, "Operator allocation delay not set"); + + vm.roll(block.number + ALLOCATION_CONFIGURATION_DELAY + 1); + + // Set up allocation parameters + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = mockStrategy; + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); // Allocate full magnitude + + OperatorSet memory operatorSet = OperatorSet({ + avs: address(serviceManager), + id: 0 + }); + + vm.startPrank(serviceManager); + IAllocationManagerTypes.CreateSetParams[] memory createParams = new IAllocationManagerTypes.CreateSetParams[](1); + createParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: 0, + strategies: allocStrategies + }); + IAllocationManager(coreDeployment.allocationManager).setAVSRegistrar( + address(serviceManager), + IAVSRegistrar(address(slashingRegistryCoordinator)) + ); + vm.stopPrank(); + + vm.startPrank(operatorWallet.key.addr); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + IAllocationManager(coreDeployment.allocationManager).modifyAllocations(operatorWallet.key.addr, allocParams); + vm.roll(block.number + 100); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + // Create BLS signing key params + bytes32 messageHash = slashingRegistryCoordinator.calculatePubkeyRegistrationMessageHash(operatorWallet.key.addr); + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkeyParams = + IBLSApkRegistryTypes.PubkeyRegistrationParams({ + pubkeyRegistrationSignature: SigningKeyOperationsLib.sign( + operatorWallet.signingKey, messageHash + ), + pubkeyG1: operatorWallet.signingKey.publicKeyG1, + pubkeyG2: operatorWallet.signingKey.publicKeyG2 + }); + + // Encode registration data with socket and pubkey params + bytes memory registrationData = abi.encode( + ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, + "socket", + pubkeyParams + ); + + IAllocationManagerTypes.RegisterParams memory registerParams = IAllocationManagerTypes.RegisterParams({ + avs: address(serviceManager), + operatorSetIds: operatorSetIds, + data: registrationData + }); + IAllocationManager(coreDeployment.allocationManager).registerForOperatorSets(operatorWallet.key.addr, registerParams); + vm.stopPrank(); + + vm.roll(block.number + 100); + + // Create slashing params + IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes.SlashingParams({ + operator: operatorWallet.key.addr, + operatorSetId: 0, + strategies: allocStrategies, + wadsToSlash: new uint256[](allocStrategies.length), + description: "Test slashing" + }); + + // Set each wad to slash to 1e18 (100% slash) + for(uint256 i = 0; i < params.wadsToSlash.length; i++) { + params.wadsToSlash[i] = 1e18; + } + + // Execute slashing vm.prank(slasher); instantSlasher.fulfillSlashingRequest(params); } -} +} \ No newline at end of file From 95228e42465366cd9f5bad895e32f6c75d7dc6ca Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 5 Feb 2025 16:29:06 -0500 Subject: [PATCH 11/13] test: vetoable slasher --- test/unit/VetoableSlasher.t.sol | 331 ++++++++++++++++++++++++++++---- 1 file changed, 292 insertions(+), 39 deletions(-) diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index 95eaa22c..415a09f9 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -4,39 +4,60 @@ pragma solidity ^0.8.27; import {Test} from "forge-std/Test.sol"; import {VetoableSlasher} from "../../src/slashers/VetoableSlasher.sol"; import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISlasher, ISlasherTypes, ISlasherErrors} from "../../src/interfaces/ISlasher.sol"; import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "../../src/interfaces/IStakeRegistry.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; +import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; +import {OperatorWalletLib, Operator, Wallet, BLSWallet, SigningKeyOperationsLib} from "../utils/OperatorWalletLib.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol"; +import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {SocketRegistry} from "../../src/SocketRegistry.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; contract VetoableSlasherTest is Test { VetoableSlasher public vetoableSlasher; VetoableSlasher public vetoableSlasherImplementation; ProxyAdmin public proxyAdmin; EmptyContract public emptyContract; - AllocationManager public allocationManager; - AllocationManager public allocationManagerImplementation; - PermissionController public permissionController; + CoreDeploymentLib.DeploymentData public coreDeployment; PauserRegistry public pauserRegistry; - DelegationMock public delegationMock; + ERC20Mock public mockToken; + StrategyFactory public strategyFactory; + StakeRegistry public stakeRegistry; + BLSApkRegistry public blsApkRegistry; + IndexRegistry public indexRegistry; + SocketRegistry public socketRegistry; SlashingRegistryCoordinator public slashingRegistryCoordinator; SlashingRegistryCoordinator public slashingRegistryCoordinatorImplementation; address public vetoCommittee; address public slasher; address public serviceManager; - address public operator; + Operator public operatorWallet; IStrategy public mockStrategy; address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); address public pauser = address(uint160(uint256(keccak256("pauser")))); @@ -49,11 +70,12 @@ contract VetoableSlasherTest is Test { uint32 constant ALLOCATION_CONFIGURATION_DELAY = 1 days; function setUp() public { - serviceManager = address(0x1); - vetoCommittee = address(0x2); - slasher = address(0x3); - operator = address(0x4); - mockStrategy = IStrategy(address(0x5)); + serviceManager = address(0x2); + vetoCommittee = address(0x3); + slasher = address(0x4); + operatorWallet = OperatorWalletLib.createOperator("operator"); + + mockToken = new ERC20Mock("Mock Token", "MOCK", address(this), 0); vm.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); @@ -63,38 +85,102 @@ contract VetoableSlasherTest is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationMock(); + CoreDeploymentLib.DeploymentConfigData memory configData; + configData.strategyManager.initialOwner = proxyAdminOwner; + configData.strategyManager.initialStrategyWhitelister = proxyAdminOwner; + configData.strategyManager.initPausedStatus = 0; + + configData.delegationManager.initialOwner = proxyAdminOwner; + configData.delegationManager.minWithdrawalDelayBlocks = 50400; + configData.delegationManager.initPausedStatus = 0; + + configData.eigenPodManager.initialOwner = proxyAdminOwner; + configData.eigenPodManager.initPausedStatus = 0; + + configData.allocationManager.initialOwner = proxyAdminOwner; + configData.allocationManager.deallocationDelay = DEALLOCATION_DELAY; + configData.allocationManager.allocationConfigurationDelay = ALLOCATION_CONFIGURATION_DELAY; + configData.allocationManager.initPausedStatus = 0; + + configData.strategyFactory.initialOwner = proxyAdminOwner; + configData.strategyFactory.initPausedStatus = 0; + + configData.avsDirectory.initialOwner = proxyAdminOwner; + configData.avsDirectory.initPausedStatus = 0; + + configData.rewardsCoordinator.initialOwner = proxyAdminOwner; + configData.rewardsCoordinator.rewardsUpdater = address(0x14dC79964da2C08b23698B3D3cc7Ca32193d9955); + configData.rewardsCoordinator.initPausedStatus = 0; + configData.rewardsCoordinator.activationDelay = 0; + configData.rewardsCoordinator.defaultSplitBips = 1000; + configData.rewardsCoordinator.calculationIntervalSeconds = 86400; + configData.rewardsCoordinator.maxRewardsDuration = 864000; + configData.rewardsCoordinator.maxRetroactiveLength = 86400; + configData.rewardsCoordinator.maxFutureLength = 86400; + configData.rewardsCoordinator.genesisRewardsTimestamp = 1672531200; - permissionController = new PermissionController(); + configData.ethPOSDeposit.ethPOSDepositAddress = address(0x123); - allocationManagerImplementation = new AllocationManager( - delegationMock, - pauserRegistry, - permissionController, - DEALLOCATION_DELAY, - ALLOCATION_CONFIGURATION_DELAY + coreDeployment = CoreDeploymentLib.deployContracts(address(proxyAdmin), configData); + + address strategyManagerOwner = Ownable(coreDeployment.strategyManager).owner(); + vm.stopPrank(); + + vm.startPrank(strategyManagerOwner); + IStrategyManager(coreDeployment.strategyManager).setStrategyWhitelister(coreDeployment.strategyFactory); + vm.stopPrank(); + + vm.startPrank(proxyAdminOwner); + mockStrategy = IStrategy(StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy(IERC20(address(mockToken)))); + + // Deploy empty proxies for all registries + stakeRegistry = StakeRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + + blsApkRegistry = BLSApkRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) ); - allocationManager = AllocationManager( + indexRegistry = IndexRegistry( address( new TransparentUpgradeableProxy( - address(allocationManagerImplementation), + address(emptyContract), address(proxyAdmin), "" ) ) ); - allocationManager.initialize(proxyAdminOwner, 0); + socketRegistry = SocketRegistry( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); - // Deploy and set up SlashingRegistryCoordinator slashingRegistryCoordinatorImplementation = new SlashingRegistryCoordinator( - IStakeRegistry(address(0)), // Mock stake registry - IBLSApkRegistry(address(0)), // Mock BLS APK registry - IIndexRegistry(address(0)), // Mock index registry - ISocketRegistry(address(0)), // Mock socket registry - allocationManager, - pauserRegistry + IStakeRegistry(address(stakeRegistry)), + IBLSApkRegistry(address(blsApkRegistry)), + IIndexRegistry(address(indexRegistry)), + ISocketRegistry(address(socketRegistry)), + IAllocationManager(coreDeployment.allocationManager), + IPauserRegistry(address(pauserRegistry)) ); slashingRegistryCoordinator = SlashingRegistryCoordinator( @@ -107,19 +193,52 @@ contract VetoableSlasherTest is Test { ) ); + // Deploy registry implementations pointing to the coordinator + StakeRegistry stakeRegistryImplementation = new StakeRegistry( + ISlashingRegistryCoordinator(address(slashingRegistryCoordinator)), + IDelegationManager(coreDeployment.delegationManager), + IAVSDirectory(coreDeployment.avsDirectory), + IAllocationManager(coreDeployment.allocationManager) + ); + BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + IndexRegistry indexRegistryImplementation = new IndexRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + SocketRegistry socketRegistryImplementation = new SocketRegistry(IRegistryCoordinator(address(slashingRegistryCoordinator))); + + // Upgrade all registry proxies + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(stakeRegistry))), + address(stakeRegistryImplementation) + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(blsApkRegistry))), + address(blsApkRegistryImplementation) + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(indexRegistry))), + address(indexRegistryImplementation) + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(socketRegistry))), + address(socketRegistryImplementation) + ); + + // Initialize the SlashingRegistryCoordinator first slashingRegistryCoordinator.initialize( proxyAdminOwner, churnApprover, ejector, - 0, // Initial paused status + 0, serviceManager ); + vm.stopPrank(); + vetoableSlasherImplementation = new VetoableSlasher( - IAllocationManager(allocationManager), + IAllocationManager(coreDeployment.allocationManager), ISlashingRegistryCoordinator(slashingRegistryCoordinator) ); + vm.startPrank(proxyAdminOwner); vetoableSlasher = VetoableSlasher( address( new TransparentUpgradeableProxy( @@ -138,13 +257,57 @@ contract VetoableSlasherTest is Test { vetoableSlasher.initialize(vetoCommittee, slasher); - vm.prank(serviceManager); - permissionController.setAppointee( + vm.startPrank(serviceManager); + PermissionController(coreDeployment.permissionController).setAppointee( address(serviceManager), address(vetoableSlasher), - address(allocationManager), + coreDeployment.allocationManager, AllocationManager.slashOperator.selector ); + + PermissionController(coreDeployment.permissionController).setAppointee( + address(serviceManager), + address(slashingRegistryCoordinator), + coreDeployment.allocationManager, + AllocationManager.createOperatorSets.selector + ); + + vm.stopPrank(); + + uint8 quorumNumber = 0; + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = mockStrategy; + + uint96[] memory minimumStakes = new uint96[](1); + minimumStakes[0] = 1 ether; + + IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: mockStrategy, + multiplier: 1 ether + }); + + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + vm.startPrank(proxyAdminOwner); + slashingRegistryCoordinator.createSlashableStakeQuorum( + operatorSetParams, + 1 ether, + strategyParams, + 0 + ); + vm.stopPrank(); + + vm.label(address(vetoableSlasher), "VetoableSlasher Proxy"); + vm.label(address(vetoableSlasherImplementation), "VetoableSlasher Implementation"); + vm.label(address(slashingRegistryCoordinator), "SlashingRegistryCoordinator Proxy"); + vm.label(address(slashingRegistryCoordinatorImplementation), "SlashingRegistryCoordinator Implementation"); + vm.label(address(proxyAdmin), "ProxyAdmin"); + vm.label(coreDeployment.allocationManager, "AllocationManager Proxy"); } function test_initialization() public { @@ -160,7 +323,7 @@ contract VetoableSlasherTest is Test { wadsToSlash[0] = 0.5e18; // 50% slash return IAllocationManagerTypes.SlashingParams({ - operator: operator, + operator: operatorWallet.key.addr, operatorSetId: 1, strategies: strategies, wadsToSlash: wadsToSlash, @@ -182,7 +345,7 @@ contract VetoableSlasherTest is Test { (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); ISlasherTypes.SlashingRequest memory request = ISlasherTypes.SlashingRequest(params, requestTimestamp, status); - assertEq(resultParams.operator, operator); + assertEq(resultParams.operator, operatorWallet.key.addr); assertEq(resultParams.operatorSetId, 1); assertEq(resultParams.wadsToSlash[0], 0.5e18); assertEq(resultParams.description, "Test slashing"); @@ -238,12 +401,102 @@ contract VetoableSlasherTest is Test { } function test_fulfillSlashingRequest() public { - vm.skip(true); /// TODO: - IAllocationManagerTypes.SlashingParams memory params = _createMockSlashingParams(); + vm.skip(false); + vm.startPrank(operatorWallet.key.addr); + IDelegationManager(coreDeployment.delegationManager).registerAsOperator(address(0), 1, "metadata"); + + uint256 depositAmount = 1 ether; + mockToken.mint(operatorWallet.key.addr, depositAmount); + mockToken.approve(address(coreDeployment.strategyManager), depositAmount); + IStrategyManager(coreDeployment.strategyManager).depositIntoStrategy(mockStrategy, mockToken, depositAmount); + + uint32 minDelay = 1; + IAllocationManager(coreDeployment.allocationManager).setAllocationDelay(operatorWallet.key.addr, minDelay); + vm.stopPrank(); + + vm.roll(block.number + ALLOCATION_CONFIGURATION_DELAY + 1); + + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = mockStrategy; + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); // Allocate full magnitude + + OperatorSet memory operatorSet = OperatorSet({ + avs: address(serviceManager), + id: 0 + }); + + vm.startPrank(serviceManager); + IAllocationManagerTypes.CreateSetParams[] memory createParams = new IAllocationManagerTypes.CreateSetParams[](1); + createParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: 0, + strategies: allocStrategies + }); + IAllocationManager(coreDeployment.allocationManager).setAVSRegistrar( + address(serviceManager), + IAVSRegistrar(address(slashingRegistryCoordinator)) + ); + vm.stopPrank(); + + vm.startPrank(operatorWallet.key.addr); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + IAllocationManager(coreDeployment.allocationManager).modifyAllocations(operatorWallet.key.addr, allocParams); + vm.roll(block.number + 100); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + bytes32 messageHash = slashingRegistryCoordinator.calculatePubkeyRegistrationMessageHash(operatorWallet.key.addr); + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkeyParams = + IBLSApkRegistryTypes.PubkeyRegistrationParams({ + pubkeyRegistrationSignature: SigningKeyOperationsLib.sign( + operatorWallet.signingKey, messageHash + ), + pubkeyG1: operatorWallet.signingKey.publicKeyG1, + pubkeyG2: operatorWallet.signingKey.publicKeyG2 + }); + + bytes memory registrationData = abi.encode( + ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, + "socket", + pubkeyParams + ); + + IAllocationManagerTypes.RegisterParams memory registerParams = IAllocationManagerTypes.RegisterParams({ + avs: address(serviceManager), + operatorSetIds: operatorSetIds, + data: registrationData + }); + IAllocationManager(coreDeployment.allocationManager).registerForOperatorSets(operatorWallet.key.addr, registerParams); + vm.stopPrank(); + + vm.roll(block.number + 100); + + // Create slashing params + IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes.SlashingParams({ + operator: operatorWallet.key.addr, + operatorSetId: 0, + strategies: allocStrategies, + wadsToSlash: new uint256[](allocStrategies.length), + description: "Test slashing" + }); + + // Set each wad to slash to 1e18 (100% slash) + for(uint256 i = 0; i < params.wadsToSlash.length; i++) { + params.wadsToSlash[i] = 1e18; + } vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); + // Wait for veto period to pass vm.warp(block.timestamp + VETO_PERIOD + 1); vm.prank(slasher); From 7f3bbd10ff8da3f4846e11c6522fd2b0b0d4fd57 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 5 Feb 2025 16:30:29 -0500 Subject: [PATCH 12/13] chore: forge fmt --- .../ISlashingRegistryCoordinator.sol | 4 +- test/unit/InstantSlasher.t.sol | 183 ++++++++-------- test/unit/VetoableSlasher.t.sol | 198 ++++++++++-------- 3 files changed, 210 insertions(+), 175 deletions(-) diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol index 48989463..f54a2356 100644 --- a/src/interfaces/ISlashingRegistryCoordinator.sol +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -472,7 +472,9 @@ interface ISlashingRegistryCoordinator is * @notice Returns the hash of the message that operators must sign with their BLS key to register * @param operator The operator's Ethereum address */ - function calculatePubkeyRegistrationMessageHash(address operator) external view returns (bytes32); + function calculatePubkeyRegistrationMessageHash( + address operator + ) external view returns (bytes32); /** * @notice Returns the operator set parameters for a given quorum. diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index 3d491c55..72e15e8d 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -4,7 +4,10 @@ pragma solidity ^0.8.27; import {Test} from "forge-std/Test.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {InstantSlasher} from "../../src/slashers/InstantSlasher.sol"; -import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import { + IAllocationManager, + IAllocationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; @@ -13,22 +16,32 @@ import {ISlasher, ISlasherTypes, ISlasherErrors} from "../../src/interfaces/ISla import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry, IStakeRegistryTypes} from "../../src/interfaces/IStakeRegistry.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; -import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; -import {SlashingRegistryCoordinator }from "../../src/SlashingRegistryCoordinator.sol"; -import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinatorTypes} from + "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; -import {OperatorWalletLib, Operator, Wallet, BLSWallet, SigningKeyOperationsLib} from "../utils/OperatorWalletLib.sol"; +import { + OperatorWalletLib, + Operator, + Wallet, + BLSWallet, + SigningKeyOperationsLib +} from "../utils/OperatorWalletLib.sol"; import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol"; @@ -106,7 +119,8 @@ contract InstantSlasherTest is Test { configData.avsDirectory.initPausedStatus = 0; configData.rewardsCoordinator.initialOwner = proxyAdminOwner; - configData.rewardsCoordinator.rewardsUpdater = address(0x14dC79964da2C08b23698B3D3cc7Ca32193d9955); + configData.rewardsCoordinator.rewardsUpdater = + address(0x14dC79964da2C08b23698B3D3cc7Ca32193d9955); configData.rewardsCoordinator.initPausedStatus = 0; configData.rewardsCoordinator.activationDelay = 0; configData.rewardsCoordinator.defaultSplitBips = 1000; @@ -124,50 +138,40 @@ contract InstantSlasherTest is Test { vm.stopPrank(); vm.startPrank(strategyManagerOwner); - IStrategyManager(coreDeployment.strategyManager).setStrategyWhitelister(coreDeployment.strategyFactory); + IStrategyManager(coreDeployment.strategyManager).setStrategyWhitelister( + coreDeployment.strategyFactory + ); vm.stopPrank(); vm.startPrank(proxyAdminOwner); - mockStrategy = IStrategy(StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy(IERC20(address(mockToken)))); + mockStrategy = IStrategy( + StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy( + IERC20(address(mockToken)) + ) + ); // Deploy empty proxies for all registries stakeRegistry = StakeRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); blsApkRegistry = BLSApkRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); indexRegistry = IndexRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); socketRegistry = SocketRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); @@ -183,9 +187,7 @@ contract InstantSlasherTest is Test { slashingRegistryCoordinator = SlashingRegistryCoordinator( address( new TransparentUpgradeableProxy( - address(slashingRegistryCoordinatorImplementation), - address(proxyAdmin), - "" + address(slashingRegistryCoordinatorImplementation), address(proxyAdmin), "" ) ) ); @@ -197,9 +199,12 @@ contract InstantSlasherTest is Test { IAVSDirectory(coreDeployment.avsDirectory), IAllocationManager(coreDeployment.allocationManager) ); - BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); - IndexRegistry indexRegistryImplementation = new IndexRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); - SocketRegistry socketRegistryImplementation = new SocketRegistry(IRegistryCoordinator(address(slashingRegistryCoordinator))); + BLSApkRegistry blsApkRegistryImplementation = + new BLSApkRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + IndexRegistry indexRegistryImplementation = + new IndexRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + SocketRegistry socketRegistryImplementation = + new SocketRegistry(IRegistryCoordinator(address(slashingRegistryCoordinator))); // Upgrade all registry proxies proxyAdmin.upgrade( @@ -221,11 +226,7 @@ contract InstantSlasherTest is Test { // Initialize the SlashingRegistryCoordinator first slashingRegistryCoordinator.initialize( - proxyAdminOwner, - churnApprover, - ejector, - 0, - serviceManager + proxyAdminOwner, churnApprover, ejector, 0, serviceManager ); vm.stopPrank(); @@ -239,11 +240,7 @@ contract InstantSlasherTest is Test { vm.startPrank(proxyAdminOwner); instantSlasher = InstantSlasher( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); @@ -279,13 +276,13 @@ contract InstantSlasherTest is Test { uint96[] memory minimumStakes = new uint96[](1); minimumStakes[0] = 1 ether; - IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); - strategyParams[0] = IStakeRegistryTypes.StrategyParams({ - strategy: mockStrategy, - multiplier: 1 ether - }); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: mockStrategy, multiplier: 1 ether}); - ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 0, kickBIPsOfTotalStake: 0 @@ -293,17 +290,17 @@ contract InstantSlasherTest is Test { vm.startPrank(proxyAdminOwner); slashingRegistryCoordinator.createSlashableStakeQuorum( - operatorSetParams, - 1 ether, - strategyParams, - 0 + operatorSetParams, 1 ether, strategyParams, 0 ); vm.stopPrank(); vm.label(address(instantSlasher), "InstantSlasher Proxy"); vm.label(address(instantSlasherImplementation), "InstantSlasher Implementation"); vm.label(address(slashingRegistryCoordinator), "SlashingRegistryCoordinator Proxy"); - vm.label(address(slashingRegistryCoordinatorImplementation), "SlashingRegistryCoordinator Implementation"); + vm.label( + address(slashingRegistryCoordinatorImplementation), + "SlashingRegistryCoordinator Implementation" + ); vm.label(address(proxyAdmin), "ProxyAdmin"); vm.label(coreDeployment.allocationManager, "AllocationManager Proxy"); } @@ -312,7 +309,11 @@ contract InstantSlasherTest is Test { assertEq(instantSlasher.slasher(), slasher); } - function _createMockSlashingParams() internal view returns (IAllocationManagerTypes.SlashingParams memory) { + function _createMockSlashingParams() + internal + view + returns (IAllocationManagerTypes.SlashingParams memory) + { IStrategy[] memory strategies = new IStrategy[](1); strategies[0] = mockStrategy; @@ -337,21 +338,29 @@ contract InstantSlasherTest is Test { function test_fulfillSlashingRequest() public { vm.skip(false); vm.startPrank(operatorWallet.key.addr); - IDelegationManager(coreDeployment.delegationManager).registerAsOperator(address(0), 1, "metadata"); + IDelegationManager(coreDeployment.delegationManager).registerAsOperator( + address(0), 1, "metadata" + ); // Mint tokens and deposit into strategy uint256 depositAmount = 1 ether; mockToken.mint(operatorWallet.key.addr, depositAmount); mockToken.approve(address(coreDeployment.strategyManager), depositAmount); - IStrategyManager(coreDeployment.strategyManager).depositIntoStrategy(mockStrategy, mockToken, depositAmount); + IStrategyManager(coreDeployment.strategyManager).depositIntoStrategy( + mockStrategy, mockToken, depositAmount + ); // Set allocation delay uint32 minDelay = 1; - IAllocationManager(coreDeployment.allocationManager).setAllocationDelay(operatorWallet.key.addr, minDelay); + IAllocationManager(coreDeployment.allocationManager).setAllocationDelay( + operatorWallet.key.addr, minDelay + ); vm.stopPrank(); // Assert operator has allocation delay set - (bool isSet, ) = IAllocationManager(coreDeployment.allocationManager).getAllocationDelay(operatorWallet.key.addr); + (bool isSet,) = IAllocationManager(coreDeployment.allocationManager).getAllocationDelay( + operatorWallet.key.addr + ); assertFalse(isSet, "Operator allocation delay not set"); vm.roll(block.number + ALLOCATION_CONFIGURATION_DELAY + 1); @@ -363,41 +372,41 @@ contract InstantSlasherTest is Test { uint64[] memory magnitudes = new uint64[](1); magnitudes[0] = uint64(1 ether); // Allocate full magnitude - OperatorSet memory operatorSet = OperatorSet({ - avs: address(serviceManager), - id: 0 - }); + OperatorSet memory operatorSet = OperatorSet({avs: address(serviceManager), id: 0}); vm.startPrank(serviceManager); - IAllocationManagerTypes.CreateSetParams[] memory createParams = new IAllocationManagerTypes.CreateSetParams[](1); - createParams[0] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: 0, - strategies: allocStrategies - }); + IAllocationManagerTypes.CreateSetParams[] memory createParams = + new IAllocationManagerTypes.CreateSetParams[](1); + createParams[0] = + IAllocationManagerTypes.CreateSetParams({operatorSetId: 0, strategies: allocStrategies}); IAllocationManager(coreDeployment.allocationManager).setAVSRegistrar( - address(serviceManager), - IAVSRegistrar(address(slashingRegistryCoordinator)) + address(serviceManager), IAVSRegistrar(address(slashingRegistryCoordinator)) ); vm.stopPrank(); vm.startPrank(operatorWallet.key.addr); - IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + IAllocationManagerTypes.AllocateParams[] memory allocParams = + new IAllocationManagerTypes.AllocateParams[](1); allocParams[0] = IAllocationManagerTypes.AllocateParams({ operatorSet: operatorSet, strategies: allocStrategies, newMagnitudes: magnitudes }); - IAllocationManager(coreDeployment.allocationManager).modifyAllocations(operatorWallet.key.addr, allocParams); + IAllocationManager(coreDeployment.allocationManager).modifyAllocations( + operatorWallet.key.addr, allocParams + ); vm.roll(block.number + 100); uint32[] memory operatorSetIds = new uint32[](1); operatorSetIds[0] = 0; // Create BLS signing key params - bytes32 messageHash = slashingRegistryCoordinator.calculatePubkeyRegistrationMessageHash(operatorWallet.key.addr); - IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkeyParams = - IBLSApkRegistryTypes.PubkeyRegistrationParams({ + bytes32 messageHash = slashingRegistryCoordinator.calculatePubkeyRegistrationMessageHash( + operatorWallet.key.addr + ); + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkeyParams = IBLSApkRegistryTypes + .PubkeyRegistrationParams({ pubkeyRegistrationSignature: SigningKeyOperationsLib.sign( operatorWallet.signingKey, messageHash ), @@ -407,23 +416,25 @@ contract InstantSlasherTest is Test { // Encode registration data with socket and pubkey params bytes memory registrationData = abi.encode( - ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, - "socket", - pubkeyParams + ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, "socket", pubkeyParams ); - IAllocationManagerTypes.RegisterParams memory registerParams = IAllocationManagerTypes.RegisterParams({ + IAllocationManagerTypes.RegisterParams memory registerParams = IAllocationManagerTypes + .RegisterParams({ avs: address(serviceManager), operatorSetIds: operatorSetIds, data: registrationData }); - IAllocationManager(coreDeployment.allocationManager).registerForOperatorSets(operatorWallet.key.addr, registerParams); + IAllocationManager(coreDeployment.allocationManager).registerForOperatorSets( + operatorWallet.key.addr, registerParams + ); vm.stopPrank(); vm.roll(block.number + 100); // Create slashing params - IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes.SlashingParams({ + IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes + .SlashingParams({ operator: operatorWallet.key.addr, operatorSetId: 0, strategies: allocStrategies, @@ -432,7 +443,7 @@ contract InstantSlasherTest is Test { }); // Set each wad to slash to 1e18 (100% slash) - for(uint256 i = 0; i < params.wadsToSlash.length; i++) { + for (uint256 i = 0; i < params.wadsToSlash.length; i++) { params.wadsToSlash[i] = 1e18; } @@ -440,4 +451,4 @@ contract InstantSlasherTest is Test { vm.prank(slasher); instantSlasher.fulfillSlashingRequest(params); } -} \ No newline at end of file +} diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index 415a09f9..a96a7f76 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -3,7 +3,10 @@ pragma solidity ^0.8.27; import {Test} from "forge-std/Test.sol"; import {VetoableSlasher} from "../../src/slashers/VetoableSlasher.sol"; -import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import { + IAllocationManager, + IAllocationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; @@ -12,22 +15,32 @@ import {ISlasher, ISlasherTypes, ISlasherErrors} from "../../src/interfaces/ISla import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry, IStakeRegistryTypes} from "../../src/interfaces/IStakeRegistry.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; -import {PermissionController} from "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; -import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinatorTypes} from + "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; import {CoreDeploymentLib} from "../utils/CoreDeployLib.sol"; -import {OperatorWalletLib, Operator, Wallet, BLSWallet, SigningKeyOperationsLib} from "../utils/OperatorWalletLib.sol"; +import { + OperatorWalletLib, + Operator, + Wallet, + BLSWallet, + SigningKeyOperationsLib +} from "../utils/OperatorWalletLib.sol"; import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20Mock} from "@openzeppelin/contracts/mocks/ERC20Mock.sol"; @@ -109,7 +122,8 @@ contract VetoableSlasherTest is Test { configData.avsDirectory.initPausedStatus = 0; configData.rewardsCoordinator.initialOwner = proxyAdminOwner; - configData.rewardsCoordinator.rewardsUpdater = address(0x14dC79964da2C08b23698B3D3cc7Ca32193d9955); + configData.rewardsCoordinator.rewardsUpdater = + address(0x14dC79964da2C08b23698B3D3cc7Ca32193d9955); configData.rewardsCoordinator.initPausedStatus = 0; configData.rewardsCoordinator.activationDelay = 0; configData.rewardsCoordinator.defaultSplitBips = 1000; @@ -127,50 +141,40 @@ contract VetoableSlasherTest is Test { vm.stopPrank(); vm.startPrank(strategyManagerOwner); - IStrategyManager(coreDeployment.strategyManager).setStrategyWhitelister(coreDeployment.strategyFactory); + IStrategyManager(coreDeployment.strategyManager).setStrategyWhitelister( + coreDeployment.strategyFactory + ); vm.stopPrank(); vm.startPrank(proxyAdminOwner); - mockStrategy = IStrategy(StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy(IERC20(address(mockToken)))); + mockStrategy = IStrategy( + StrategyFactory(coreDeployment.strategyFactory).deployNewStrategy( + IERC20(address(mockToken)) + ) + ); // Deploy empty proxies for all registries stakeRegistry = StakeRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); blsApkRegistry = BLSApkRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); indexRegistry = IndexRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); socketRegistry = SocketRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); @@ -186,9 +190,7 @@ contract VetoableSlasherTest is Test { slashingRegistryCoordinator = SlashingRegistryCoordinator( address( new TransparentUpgradeableProxy( - address(slashingRegistryCoordinatorImplementation), - address(proxyAdmin), - "" + address(slashingRegistryCoordinatorImplementation), address(proxyAdmin), "" ) ) ); @@ -200,9 +202,12 @@ contract VetoableSlasherTest is Test { IAVSDirectory(coreDeployment.avsDirectory), IAllocationManager(coreDeployment.allocationManager) ); - BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); - IndexRegistry indexRegistryImplementation = new IndexRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); - SocketRegistry socketRegistryImplementation = new SocketRegistry(IRegistryCoordinator(address(slashingRegistryCoordinator))); + BLSApkRegistry blsApkRegistryImplementation = + new BLSApkRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + IndexRegistry indexRegistryImplementation = + new IndexRegistry(ISlashingRegistryCoordinator(address(slashingRegistryCoordinator))); + SocketRegistry socketRegistryImplementation = + new SocketRegistry(IRegistryCoordinator(address(slashingRegistryCoordinator))); // Upgrade all registry proxies proxyAdmin.upgrade( @@ -224,11 +229,7 @@ contract VetoableSlasherTest is Test { // Initialize the SlashingRegistryCoordinator first slashingRegistryCoordinator.initialize( - proxyAdminOwner, - churnApprover, - ejector, - 0, - serviceManager + proxyAdminOwner, churnApprover, ejector, 0, serviceManager ); vm.stopPrank(); @@ -241,11 +242,7 @@ contract VetoableSlasherTest is Test { vm.startPrank(proxyAdminOwner); vetoableSlasher = VetoableSlasher( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); @@ -281,13 +278,13 @@ contract VetoableSlasherTest is Test { uint96[] memory minimumStakes = new uint96[](1); minimumStakes[0] = 1 ether; - IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); - strategyParams[0] = IStakeRegistryTypes.StrategyParams({ - strategy: mockStrategy, - multiplier: 1 ether - }); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: mockStrategy, multiplier: 1 ether}); - ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 0, kickBIPsOfTotalStake: 0 @@ -295,17 +292,17 @@ contract VetoableSlasherTest is Test { vm.startPrank(proxyAdminOwner); slashingRegistryCoordinator.createSlashableStakeQuorum( - operatorSetParams, - 1 ether, - strategyParams, - 0 + operatorSetParams, 1 ether, strategyParams, 0 ); vm.stopPrank(); vm.label(address(vetoableSlasher), "VetoableSlasher Proxy"); vm.label(address(vetoableSlasherImplementation), "VetoableSlasher Implementation"); vm.label(address(slashingRegistryCoordinator), "SlashingRegistryCoordinator Proxy"); - vm.label(address(slashingRegistryCoordinatorImplementation), "SlashingRegistryCoordinator Implementation"); + vm.label( + address(slashingRegistryCoordinatorImplementation), + "SlashingRegistryCoordinator Implementation" + ); vm.label(address(proxyAdmin), "ProxyAdmin"); vm.label(coreDeployment.allocationManager, "AllocationManager Proxy"); } @@ -315,7 +312,11 @@ contract VetoableSlasherTest is Test { assertEq(vetoableSlasher.VETO_PERIOD(), VETO_PERIOD); } - function _createMockSlashingParams() internal view returns (IAllocationManagerTypes.SlashingParams memory) { + function _createMockSlashingParams() + internal + view + returns (IAllocationManagerTypes.SlashingParams memory) + { IStrategy[] memory strategies = new IStrategy[](1); strategies[0] = mockStrategy; @@ -343,8 +344,13 @@ contract VetoableSlasherTest is Test { vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); - ISlasherTypes.SlashingRequest memory request = ISlasherTypes.SlashingRequest(params, requestTimestamp, status); + ( + IAllocationManagerTypes.SlashingParams memory resultParams, + uint256 requestTimestamp, + ISlasherTypes.SlashingStatus status + ) = vetoableSlasher.slashingRequests(0); + ISlasherTypes.SlashingRequest memory request = + ISlasherTypes.SlashingRequest(params, requestTimestamp, status); assertEq(resultParams.operator, operatorWallet.key.addr); assertEq(resultParams.operatorSetId, 1); assertEq(resultParams.wadsToSlash[0], 0.5e18); @@ -385,7 +391,11 @@ contract VetoableSlasherTest is Test { vm.prank(vetoCommittee); vetoableSlasher.cancelSlashingRequest(0); - (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); + ( + IAllocationManagerTypes.SlashingParams memory resultParams, + uint256 requestTimestamp, + ISlasherTypes.SlashingStatus status + ) = vetoableSlasher.slashingRequests(0); assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Cancelled)); } @@ -403,15 +413,21 @@ contract VetoableSlasherTest is Test { function test_fulfillSlashingRequest() public { vm.skip(false); vm.startPrank(operatorWallet.key.addr); - IDelegationManager(coreDeployment.delegationManager).registerAsOperator(address(0), 1, "metadata"); + IDelegationManager(coreDeployment.delegationManager).registerAsOperator( + address(0), 1, "metadata" + ); uint256 depositAmount = 1 ether; mockToken.mint(operatorWallet.key.addr, depositAmount); mockToken.approve(address(coreDeployment.strategyManager), depositAmount); - IStrategyManager(coreDeployment.strategyManager).depositIntoStrategy(mockStrategy, mockToken, depositAmount); + IStrategyManager(coreDeployment.strategyManager).depositIntoStrategy( + mockStrategy, mockToken, depositAmount + ); uint32 minDelay = 1; - IAllocationManager(coreDeployment.allocationManager).setAllocationDelay(operatorWallet.key.addr, minDelay); + IAllocationManager(coreDeployment.allocationManager).setAllocationDelay( + operatorWallet.key.addr, minDelay + ); vm.stopPrank(); vm.roll(block.number + ALLOCATION_CONFIGURATION_DELAY + 1); @@ -422,40 +438,40 @@ contract VetoableSlasherTest is Test { uint64[] memory magnitudes = new uint64[](1); magnitudes[0] = uint64(1 ether); // Allocate full magnitude - OperatorSet memory operatorSet = OperatorSet({ - avs: address(serviceManager), - id: 0 - }); + OperatorSet memory operatorSet = OperatorSet({avs: address(serviceManager), id: 0}); vm.startPrank(serviceManager); - IAllocationManagerTypes.CreateSetParams[] memory createParams = new IAllocationManagerTypes.CreateSetParams[](1); - createParams[0] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: 0, - strategies: allocStrategies - }); + IAllocationManagerTypes.CreateSetParams[] memory createParams = + new IAllocationManagerTypes.CreateSetParams[](1); + createParams[0] = + IAllocationManagerTypes.CreateSetParams({operatorSetId: 0, strategies: allocStrategies}); IAllocationManager(coreDeployment.allocationManager).setAVSRegistrar( - address(serviceManager), - IAVSRegistrar(address(slashingRegistryCoordinator)) + address(serviceManager), IAVSRegistrar(address(slashingRegistryCoordinator)) ); vm.stopPrank(); vm.startPrank(operatorWallet.key.addr); - IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + IAllocationManagerTypes.AllocateParams[] memory allocParams = + new IAllocationManagerTypes.AllocateParams[](1); allocParams[0] = IAllocationManagerTypes.AllocateParams({ operatorSet: operatorSet, strategies: allocStrategies, newMagnitudes: magnitudes }); - IAllocationManager(coreDeployment.allocationManager).modifyAllocations(operatorWallet.key.addr, allocParams); + IAllocationManager(coreDeployment.allocationManager).modifyAllocations( + operatorWallet.key.addr, allocParams + ); vm.roll(block.number + 100); uint32[] memory operatorSetIds = new uint32[](1); operatorSetIds[0] = 0; - bytes32 messageHash = slashingRegistryCoordinator.calculatePubkeyRegistrationMessageHash(operatorWallet.key.addr); - IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkeyParams = - IBLSApkRegistryTypes.PubkeyRegistrationParams({ + bytes32 messageHash = slashingRegistryCoordinator.calculatePubkeyRegistrationMessageHash( + operatorWallet.key.addr + ); + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkeyParams = IBLSApkRegistryTypes + .PubkeyRegistrationParams({ pubkeyRegistrationSignature: SigningKeyOperationsLib.sign( operatorWallet.signingKey, messageHash ), @@ -464,23 +480,25 @@ contract VetoableSlasherTest is Test { }); bytes memory registrationData = abi.encode( - ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, - "socket", - pubkeyParams + ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, "socket", pubkeyParams ); - IAllocationManagerTypes.RegisterParams memory registerParams = IAllocationManagerTypes.RegisterParams({ + IAllocationManagerTypes.RegisterParams memory registerParams = IAllocationManagerTypes + .RegisterParams({ avs: address(serviceManager), operatorSetIds: operatorSetIds, data: registrationData }); - IAllocationManager(coreDeployment.allocationManager).registerForOperatorSets(operatorWallet.key.addr, registerParams); + IAllocationManager(coreDeployment.allocationManager).registerForOperatorSets( + operatorWallet.key.addr, registerParams + ); vm.stopPrank(); vm.roll(block.number + 100); // Create slashing params - IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes.SlashingParams({ + IAllocationManagerTypes.SlashingParams memory params = IAllocationManagerTypes + .SlashingParams({ operator: operatorWallet.key.addr, operatorSetId: 0, strategies: allocStrategies, @@ -489,7 +507,7 @@ contract VetoableSlasherTest is Test { }); // Set each wad to slash to 1e18 (100% slash) - for(uint256 i = 0; i < params.wadsToSlash.length; i++) { + for (uint256 i = 0; i < params.wadsToSlash.length; i++) { params.wadsToSlash[i] = 1e18; } @@ -502,7 +520,11 @@ contract VetoableSlasherTest is Test { vm.prank(slasher); vetoableSlasher.fulfillSlashingRequest(0); - (IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, ISlasherTypes.SlashingStatus status) = vetoableSlasher.slashingRequests(0); + ( + IAllocationManagerTypes.SlashingParams memory resultParams, + uint256 requestTimestamp, + ISlasherTypes.SlashingStatus status + ) = vetoableSlasher.slashingRequests(0); assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Completed)); } -} \ No newline at end of file +} From 1d455ef98657f5c51c5b6c1a1a35f0cae0c24d46 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 5 Feb 2025 17:37:14 -0500 Subject: [PATCH 13/13] chore: format and solve interfaces with inheritdoc --- src/interfaces/IInstantSlasher.sol | 24 ++++++++ src/interfaces/ISlasher.sol | 74 +++------------------- src/interfaces/IVetoableSlasher.sol | 92 ++++++++++++++++++++++++++++ src/slashers/InstantSlasher.sol | 16 +++-- src/slashers/VetoableSlasher.sol | 58 +++++++++--------- src/slashers/base/SlasherStorage.sol | 13 ++-- test/unit/VetoableSlasher.t.sol | 24 ++++---- 7 files changed, 179 insertions(+), 122 deletions(-) create mode 100644 src/interfaces/IInstantSlasher.sol create mode 100644 src/interfaces/IVetoableSlasher.sol diff --git a/src/interfaces/IInstantSlasher.sol b/src/interfaces/IInstantSlasher.sol new file mode 100644 index 00000000..c302f616 --- /dev/null +++ b/src/interfaces/IInstantSlasher.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlasher} from "./ISlasher.sol"; + +/// @title IInstantSlasher +/// @notice A slashing contract that immediately executes slashing requests without any delay or veto period +/// @dev Extends base interfaces to provide access controlled slashing functionality +interface IInstantSlasher is ISlasher { + /// @notice Initializes the contract with a slasher address + /// @param _slasher Address authorized to create and fulfill slashing requests + function initialize( + address _slasher + ) external; + + /// @notice Immediately executes a slashing request + /// @param _slashingParams Parameters defining the slashing request including operator and amount + /// @dev Can only be called by the authorized slasher + function fulfillSlashingRequest( + IAllocationManager.SlashingParams memory _slashingParams + ) external; +} diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol index fe388afb..3e87b762 100644 --- a/src/interfaces/ISlasher.sol +++ b/src/interfaces/ISlasher.sol @@ -1,85 +1,24 @@ // SPDX-License-Identifier: BUSL-1.1 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"; interface ISlasherErrors { - /// @notice Thrown when a caller without veto committee privileges attempts a restricted operation. - error OnlyVetoCommittee(); - /// @notice Thrown when a caller without slasher privileges attempts a restricted operation. + /// @notice Thrown when a caller without slasher privileges attempts a restricted operation error OnlySlasher(); - /// @notice Thrown when attempting to veto a slashing request after the veto period has expired. - error VetoPeriodPassed(); - /// @notice Thrown when attempting to execute a slashing request before the veto period has ended. - error VetoPeriodNotPassed(); - /// @notice Thrown when attempting to interact with a slashing request that has been cancelled. - error SlashingRequestIsCancelled(); - /// @notice Thrown when attempting to modify a slashing request that does not exist. - error SlashingRequestNotRequested(); } interface ISlasherTypes { - /** - * @notice Represents the current status of a slashing request. - * @dev The status of a slashing request can be one of the following: - * - Null: Default state, no request exists. - * - Requested: Slashing has been requested but not yet executed. - * - Completed: Slashing has been successfully executed. - * - Cancelled: Slashing request was cancelled by veto committee. - */ - enum SlashingStatus { - Null, - Requested, - Completed, - Cancelled - } - - /** - * @notice Contains all information related to a slashing request. - * @param params The slashing parameters from the allocation manager. - * @param requestTimestamp The timestamp when the slashing request was created. - * @param status The current status of the slashing request. - */ + /// @notice Structure containing details about a slashing request struct SlashingRequest { IAllocationManager.SlashingParams params; uint256 requestTimestamp; - SlashingStatus status; } } interface ISlasherEvents is ISlasherTypes { - /** - * @notice Emitted when a new slashing request is created. - * @param requestId The unique identifier for the slashing request (indexed). - * @param operator The address of the operator to be slashed (indexed). - * @param operatorSetId The ID of the operator set involved (indexed). - * @param wadsToSlash The amounts to slash from each strategy. - * @param description A human-readable description of the slashing reason. - */ - event SlashingRequested( - uint256 indexed requestId, - address indexed operator, - uint32 indexed operatorSetId, - uint256[] wadsToSlash, - string description - ); - - /** - * @notice Emitted when a slashing request is cancelled by the veto committee. - * @param requestId The unique identifier of the cancelled request (indexed). - */ - event SlashingRequestCancelled(uint256 indexed requestId); - - /** - * @notice Emitted when an operator is successfully slashed. - * @param slashingRequestId The ID of the executed slashing request (indexed). - * @param operator The address of the slashed operator (indexed). - * @param operatorSetId The ID of the operator set involved (indexed). - * @param wadsToSlash The amounts slashed from each strategy. - * @param description A human-readable description of why the operator was slashed. - */ + /// @notice Emitted when an operator is successfully slashed event OperatorSlashed( uint256 indexed slashingRequestId, address indexed operator, @@ -89,4 +28,9 @@ interface ISlasherEvents is ISlasherTypes { ); } -interface ISlasher is ISlasherErrors, ISlasherEvents {} +/// @title ISlasher +/// @notice Base interface containing shared functionality for all slasher implementations +interface ISlasher is ISlasherErrors, ISlasherEvents { + /// @notice Returns the address authorized to create and fulfill slashing requests + function slasher() external view returns (address); +} diff --git a/src/interfaces/IVetoableSlasher.sol b/src/interfaces/IVetoableSlasher.sol new file mode 100644 index 00000000..e6e34bd9 --- /dev/null +++ b/src/interfaces/IVetoableSlasher.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlasher} from "./ISlasher.sol"; + +interface IVetoableSlasherErrors { + /// @notice Thrown when a caller without veto committee privileges attempts a restricted operation + error OnlyVetoCommittee(); + /// @notice Thrown when attempting to veto a slashing request after the veto period has expired + error VetoPeriodPassed(); + /// @notice Thrown when attempting to execute a slashing request before the veto period has ended + error VetoPeriodNotPassed(); + /// @notice Thrown when attempting to interact with a slashing request that has been cancelled + error SlashingRequestIsCancelled(); + /// @notice Thrown when attempting to modify a slashing request that does not exist + error SlashingRequestNotRequested(); +} + +interface IVetoableSlasherTypes { + /// @notice Represents the status of a slashing request + enum SlashingStatus { + Requested, + Cancelled, + Completed + } + + /// @notice Structure containing details about a vetoable slashing request + struct VetoableSlashingRequest { + IAllocationManager.SlashingParams params; + uint256 requestTimestamp; + SlashingStatus status; + } +} + +interface IVetoableSlasherEvents { + /// @notice Emitted when a new slashing request is created + event SlashingRequested( + uint256 indexed requestId, + 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); +} + +/// @title IVetoableSlasher +/// @notice A slashing contract that implements a veto mechanism allowing a designated committee to cancel slashing requests +/// @dev Extends base interfaces and adds a veto period during which slashing requests can be cancelled +interface IVetoableSlasher is + ISlasher, + IVetoableSlasherErrors, + IVetoableSlasherTypes, + IVetoableSlasherEvents +{ + /// @notice Duration of the veto period during which the veto committee can cancel slashing requests + /// @dev Set to 3 days (259,200 seconds) + function VETO_PERIOD() external view returns (uint256); + + /// @notice Address of the committee that has veto power over slashing requests + function vetoCommittee() external view returns (address); + + /// @notice Initializes the contract with a veto committee and slasher address + /// @param _vetoCommittee Address of the committee that can veto slashing requests + /// @param _slasher Address authorized to create and fulfill slashing requests + function initialize(address _vetoCommittee, address _slasher) external; + + /// @notice Queues a new slashing request + /// @param params Parameters defining the slashing request including operator and amount + /// @dev Can only be called by the authorized slasher + function queueSlashingRequest( + IAllocationManager.SlashingParams calldata params + ) external; + + /// @notice Cancels a pending slashing request + /// @param requestId 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 + ) external; + + /// @notice Executes a slashing request after the veto period has passed + /// @param requestId 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 + ) external; +} diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol index 5827dd07..6aa93cab 100644 --- a/src/slashers/InstantSlasher.sol +++ b/src/slashers/InstantSlasher.sol @@ -6,31 +6,29 @@ import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; +import {IInstantSlasher} from "../interfaces/IInstantSlasher.sol"; /// @title InstantSlasher /// @notice A slashing contract that immediately executes slashing requests without any delay or veto period /// @dev Extends SlasherBase to provide access controlled slashing functionality -contract InstantSlasher is SlasherBase { +contract InstantSlasher is IInstantSlasher, SlasherBase { constructor( IAllocationManager _allocationManager, ISlashingRegistryCoordinator _slashingRegistryCoordinator, address _slasher ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} - /// @notice Initializes the contract with a slasher address - /// @param _slasher Address authorized to create and fulfill slashing requests + /// @inheritdoc IInstantSlasher function initialize( address _slasher - ) external initializer { + ) external override initializer { __SlasherBase_init(_slasher); } - /// @notice Immediately executes a slashing request - /// @param _slashingParams Parameters defining the slashing request including operator and amount - /// @dev Can only be called by the authorized slasher + /// @inheritdoc IInstantSlasher function fulfillSlashingRequest( - IAllocationManager.SlashingParams memory _slashingParams - ) external virtual onlySlasher { + IAllocationManager.SlashingParams calldata _slashingParams + ) external virtual override(IInstantSlasher) onlySlasher { uint256 requestId = nextRequestId++; _fulfillSlashingRequest(requestId, _slashingParams); } diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 4ac05974..7f7fc18c 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -6,20 +6,20 @@ import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; +import {IVetoableSlasher, IVetoableSlasherTypes} from "../interfaces/IVetoableSlasher.sol"; /// @title VetoableSlasher /// @notice A slashing contract that implements a veto mechanism allowing a designated committee to cancel slashing requests /// @dev Extends SlasherBase and adds a veto period during which slashing requests can be cancelled -contract VetoableSlasher is SlasherBase { - /// @notice Duration of the veto period during which the veto committee can cancel slashing requests - /// @dev Set to 3 days (259,200 seconds) - uint256 public constant VETO_PERIOD = 3 days; +contract VetoableSlasher is IVetoableSlasher, SlasherBase { + /// @inheritdoc IVetoableSlasher + uint256 public constant override VETO_PERIOD = 3 days; - /// @notice Address of the committee that has veto power over slashing requests - address public vetoCommittee; + /// @inheritdoc IVetoableSlasher + address public override vetoCommittee; /// @notice Mapping of request IDs to their corresponding slashing request details - mapping(uint256 => SlashingRequest) public slashingRequests; + mapping(uint256 => IVetoableSlasherTypes.VetoableSlashingRequest) public slashingRequests; /// @notice Modifier to restrict function access to only the veto committee modifier onlyVetoCommittee() { @@ -32,52 +32,50 @@ contract VetoableSlasher is SlasherBase { ISlashingRegistryCoordinator _slashingRegistryCoordinator ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} - /// @notice Initializes the contract with a veto committee and slasher address - /// @param _vetoCommittee Address of the committee that can veto slashing requests - /// @param _slasher Address authorized to create and fulfill slashing requests - function initialize(address _vetoCommittee, address _slasher) external virtual initializer { + /// @inheritdoc IVetoableSlasher + function initialize( + address _vetoCommittee, + address _slasher + ) external virtual override initializer { __SlasherBase_init(_slasher); vetoCommittee = _vetoCommittee; } - /// @notice Queues a new slashing request - /// @param params Parameters defining the slashing request including operator and amount - /// @dev Can only be called by the authorized slasher + /// @inheritdoc IVetoableSlasher function queueSlashingRequest( IAllocationManager.SlashingParams calldata params - ) external virtual onlySlasher { + ) external virtual override onlySlasher { _queueSlashingRequest(params); } - /// @notice Cancels a pending slashing request - /// @param requestId The ID of the slashing request to cancel - /// @dev Can only be called by the veto committee during the veto period + /// @inheritdoc IVetoableSlasher function cancelSlashingRequest( uint256 requestId - ) external virtual onlyVetoCommittee { + ) external virtual override onlyVetoCommittee { require( block.timestamp < slashingRequests[requestId].requestTimestamp + VETO_PERIOD, VetoPeriodPassed() ); require( - slashingRequests[requestId].status == SlashingStatus.Requested, + slashingRequests[requestId].status == IVetoableSlasherTypes.SlashingStatus.Requested, SlashingRequestNotRequested() ); _cancelSlashingRequest(requestId); } - /// @notice Executes a slashing request after the veto period has passed - /// @param requestId The ID of the slashing request to fulfill - /// @dev Can only be called by the authorized slasher after the veto period + /// @inheritdoc IVetoableSlasher function fulfillSlashingRequest( uint256 requestId - ) external virtual onlySlasher { - SlashingRequest storage request = slashingRequests[requestId]; + ) external virtual override onlySlasher { + IVetoableSlasherTypes.VetoableSlashingRequest storage request = slashingRequests[requestId]; require(block.timestamp >= request.requestTimestamp + VETO_PERIOD, VetoPeriodNotPassed()); - require(request.status == SlashingStatus.Requested, SlashingRequestIsCancelled()); + require( + request.status == IVetoableSlasherTypes.SlashingStatus.Requested, + SlashingRequestIsCancelled() + ); - request.status = SlashingStatus.Completed; + request.status = IVetoableSlasherTypes.SlashingStatus.Completed; _fulfillSlashingRequest(requestId, request.params); } @@ -88,10 +86,10 @@ contract VetoableSlasher is SlasherBase { IAllocationManager.SlashingParams calldata params ) internal virtual { uint256 requestId = nextRequestId++; - slashingRequests[requestId] = SlashingRequest({ + slashingRequests[requestId] = IVetoableSlasherTypes.VetoableSlashingRequest({ params: params, requestTimestamp: block.timestamp, - status: SlashingStatus.Requested + status: IVetoableSlasherTypes.SlashingStatus.Requested }); emit SlashingRequested( @@ -104,7 +102,7 @@ contract VetoableSlasher is SlasherBase { function _cancelSlashingRequest( uint256 requestId ) internal virtual { - slashingRequests[requestId].status = SlashingStatus.Cancelled; + slashingRequests[requestId].status = IVetoableSlasherTypes.SlashingStatus.Cancelled; emit SlashingRequestCancelled(requestId); } diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol index 54627326..ebef99aa 100644 --- a/src/slashers/base/SlasherStorage.sol +++ b/src/slashers/base/SlasherStorage.sol @@ -3,10 +3,13 @@ pragma solidity ^0.8.27; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {ISlasher} from "../../interfaces/ISlasher.sol"; import {ISlashingRegistryCoordinator} from "../../interfaces/ISlashingRegistryCoordinator.sol"; +import {ISlasher} from "../../interfaces/ISlasher.sol"; -contract SlasherStorage is ISlasher { +/// @title SlasherStorage +/// @notice Base storage contract for slashing functionality +/// @dev Provides storage variables and events for slashing operations +abstract contract SlasherStorage is ISlasher { /** * * CONSTANTS AND IMMUTABLES @@ -17,11 +20,7 @@ contract SlasherStorage is ISlasher { IAllocationManager public immutable allocationManager; /// @notice the SlashingRegistryCoordinator for this AVS ISlashingRegistryCoordinator public immutable slashingRegistryCoordinator; - /** - * - * STATE - * - */ + address public slasher; uint256 public nextRequestId; diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index a96a7f76..317d9fc9 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -48,8 +48,10 @@ import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/Str import {StakeRegistry} from "../../src/StakeRegistry.sol"; import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {IVetoableSlasherTypes} from "../../src/interfaces/IVetoableSlasher.sol"; import {SocketRegistry} from "../../src/SocketRegistry.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {IVetoableSlasherErrors} from "../../src/interfaces/IVetoableSlasher.sol"; contract VetoableSlasherTest is Test { VetoableSlasher public vetoableSlasher; @@ -347,15 +349,15 @@ contract VetoableSlasherTest is Test { ( IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, - ISlasherTypes.SlashingStatus status + IVetoableSlasherTypes.SlashingStatus status ) = vetoableSlasher.slashingRequests(0); - ISlasherTypes.SlashingRequest memory request = - ISlasherTypes.SlashingRequest(params, requestTimestamp, status); + IVetoableSlasherTypes.VetoableSlashingRequest memory request = + IVetoableSlasherTypes.VetoableSlashingRequest(params, requestTimestamp, status); assertEq(resultParams.operator, operatorWallet.key.addr); assertEq(resultParams.operatorSetId, 1); assertEq(resultParams.wadsToSlash[0], 0.5e18); assertEq(resultParams.description, "Test slashing"); - assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Requested)); + assertEq(uint8(status), uint8(IVetoableSlasherTypes.SlashingStatus.Requested)); assertEq(requestTimestamp, block.timestamp); } @@ -365,7 +367,7 @@ contract VetoableSlasherTest is Test { vm.prank(slasher); vetoableSlasher.queueSlashingRequest(params); - vm.expectRevert(ISlasherErrors.OnlyVetoCommittee.selector); + vm.expectRevert(IVetoableSlasherErrors.OnlyVetoCommittee.selector); vetoableSlasher.cancelSlashingRequest(0); } @@ -378,7 +380,7 @@ contract VetoableSlasherTest is Test { vm.warp(block.timestamp + VETO_PERIOD + 1); vm.prank(vetoCommittee); - vm.expectRevert(ISlasherErrors.VetoPeriodPassed.selector); + vm.expectRevert(IVetoableSlasherErrors.VetoPeriodPassed.selector); vetoableSlasher.cancelSlashingRequest(0); } @@ -394,9 +396,9 @@ contract VetoableSlasherTest is Test { ( IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, - ISlasherTypes.SlashingStatus status + IVetoableSlasherTypes.SlashingStatus status ) = vetoableSlasher.slashingRequests(0); - assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Cancelled)); + assertEq(uint8(status), uint8(IVetoableSlasherTypes.SlashingStatus.Cancelled)); } function test_fulfillSlashingRequest_revert_beforeVetoPeriod() public { @@ -406,7 +408,7 @@ contract VetoableSlasherTest is Test { vetoableSlasher.queueSlashingRequest(params); vm.prank(slasher); - vm.expectRevert(ISlasherErrors.VetoPeriodNotPassed.selector); + vm.expectRevert(IVetoableSlasherErrors.VetoPeriodNotPassed.selector); vetoableSlasher.fulfillSlashingRequest(0); } @@ -523,8 +525,8 @@ contract VetoableSlasherTest is Test { ( IAllocationManagerTypes.SlashingParams memory resultParams, uint256 requestTimestamp, - ISlasherTypes.SlashingStatus status + IVetoableSlasherTypes.SlashingStatus status ) = vetoableSlasher.slashingRequests(0); - assertEq(uint8(status), uint8(ISlasherTypes.SlashingStatus.Completed)); + assertEq(uint8(status), uint8(IVetoableSlasherTypes.SlashingStatus.Completed)); } }