diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 5341ef835..1dce4ed53 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 5341ef83500476c62a4406ff00cdde7f5c2cc11f +Subproject commit 1dce4ed53df3dfcba5878c9b7516006dcf330858 diff --git a/src/SlashingRegistryCoordinator.sol b/src/SlashingRegistryCoordinator.sol index a0ce8b56e..e4faceaf2 100644 --- a/src/SlashingRegistryCoordinator.sol +++ b/src/SlashingRegistryCoordinator.sol @@ -147,6 +147,7 @@ contract SlashingRegistryCoordinator is /// @inheritdoc ISlashingRegistryCoordinator function registerOperator( address operator, + address avs, uint32[] memory operatorSetIds, bytes calldata data ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { @@ -222,6 +223,7 @@ contract SlashingRegistryCoordinator is /// @inheritdoc ISlashingRegistryCoordinator function deregisterOperator( address operator, + address avs, uint32[] memory operatorSetIds ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { bytes memory quorumNumbers = _getQuorumNumbers(operatorSetIds); @@ -1147,4 +1149,8 @@ contract SlashingRegistryCoordinator is ) public view returns (bytes32) { return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))); } + + function supportsAVS(address _avs) external view virtual returns (bool) { + return _avs == address(avs); + } } diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol index 032bc08bb..7990a3127 100644 --- a/src/interfaces/ISlashingRegistryCoordinator.sol +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -306,14 +306,25 @@ interface ISlashingRegistryCoordinator is /** * @notice Registers an operator through the allocation manager for operator set quorums. * @param operator The operator address to register. + * @param avs The AVS contract address that the operator is registering for. * @param operatorSetIds The operator set IDs to register for (corresponds to quorum numbers). - * @param data Additional registration data containing the operator's socket and BLS public key parameters. + * @param data Additional registration data containing: + * - RegistrationType: NORMAL or CHURN + * - socket: The operator's socket string (typically an IP address) + * - PubkeyRegistrationParams: The operator's BLS public key parameters + * - OperatorKickParams[]: (Only for CHURN) Array of operators to kick from each quorum + * - SignatureWithSaltAndExpiry: (Only for CHURN) Signature from churn approver * @dev Can only be called by the allocation manager. - * @dev Will revert if operator sets are not enabled or if registering for M2 quorums. - * @dev This function implements the Slashing registration pathway specified by the IAVSRegistrar interface. + * @dev Will revert if: + * - Operator sets are not enabled + * - Registering for M2 quorums + * - Invalid registration type + * - Max operator count exceeded (for NORMAL registration) + * - Invalid churn parameters (for CHURN registration) */ function registerOperator( address operator, + address avs, uint32[] memory operatorSetIds, bytes memory data ) external; @@ -321,12 +332,16 @@ interface ISlashingRegistryCoordinator is /** * @notice Deregisters an operator through the allocation manager from operator set quorums. * @param operator The operator address to deregister. + * @param avs The AVS contract address that the operator is deregistering from. * @param operatorSetIds The operator set IDs to deregister from (corresponds to quorum numbers). * @dev Can only be called by the allocation manager. - * @dev Will revert if operator sets are not enabled or if deregistering from M2 quorums. + * @dev Will revert if: + * - Operator sets are not enabled + * - Deregistering from M2 quorums + * - Operator is not registered in the specified quorums * @dev This function implements the Slashing deregistration pathway specified by the IAVSRegistrar interface. */ - function deregisterOperator(address operator, uint32[] memory operatorSetIds) external; + function deregisterOperator(address operator, address avs, uint32[] memory operatorSetIds) external; /** * @notice Updates stake weights for specified operators. If any operator is found to be below diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index a09e88071..c85c8b245 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.27; import "../../src/RegistryCoordinator.sol"; - import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + import "forge-std/Test.sol"; @@ -85,4 +86,8 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { ) external { _m2QuorumBitmap = bitmap; } + + function supportsAVS(address avs) external view override (IAVSRegistrar, SlashingRegistryCoordinator) returns (bool) { + return avs == address(serviceManager); + } } diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index f6d5769a3..c201534a8 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -227,6 +227,9 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { } } + cheats.prank(address(serviceManager)); + allocationManager.updateAVSMetadataURI(address(serviceManager), "test-avs-metadata"); + cheats.prank(registryCoordinatorOwner); slashingRegistryCoordinator.createTotalDelegatedStakeQuorum({ operatorSetParams: operatorSet, diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 70488bc9b..9b19277bf 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -444,6 +444,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { allocationManager, pauserRegistry ); + cheats.prank(avsAccountIdentifier); + allocationManager.updateAVSMetadataURI(address(avsAccountIdentifier), "ipfs://mock-metadata-uri"); + proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(slashingRegistryCoordinator))), address(slashingRegistryCoordinatorImplementation), @@ -467,25 +470,32 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { target: address(allocationManager), selector: IAllocationManager.setAVSRegistrar.selector }); - // 2. create operator sets + + // 2. set AVS metadata + serviceManager.setAppointee({ + appointee: serviceManager.owner(), + target: address(allocationManager), + selector: IAllocationManager.updateAVSMetadataURI.selector + }); + // 3. create operator sets serviceManager.setAppointee({ appointee: address(registryCoordinator), target: address(allocationManager), selector: IAllocationManager.createOperatorSets.selector }); - // 3. deregister operator from operator sets + // 4. deregister operator from operator sets serviceManager.setAppointee({ appointee: address(registryCoordinator), target: address(allocationManager), selector: IAllocationManager.deregisterFromOperatorSets.selector }); - // 4. add strategies to operator sets + // 5. add strategies to operator sets serviceManager.setAppointee({ appointee: address(registryCoordinator), target: address(stakeRegistry), selector: IAllocationManager.addStrategiesToOperatorSet.selector }); - // 5. remove strategies from operator sets + // 6. remove strategies from operator sets serviceManager.setAppointee({ appointee: address(registryCoordinator), target: address(stakeRegistry), diff --git a/test/mocks/AVSRegistrarMock.sol b/test/mocks/AVSRegistrarMock.sol index 9a61b26ea..ae7d20771 100644 --- a/test/mocks/AVSRegistrarMock.sol +++ b/test/mocks/AVSRegistrarMock.sol @@ -6,12 +6,20 @@ import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSR contract AVSRegistrarMock is IAVSRegistrar { function registerOperator( address operator, + address avs, uint32[] calldata operatorSetIds, bytes calldata data ) external override {} function deregisterOperator( address operator, + address avs, uint32[] calldata operatorSetIds ) external override {} + + function supportsAVS( + address + ) external pure override returns (bool) { + return true; + } } diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index ef8648daa..9aadf9e21 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -232,7 +232,7 @@ contract DelegationIntermediate is IDelegationManager { IStrategy strategy, uint64 prevMaxMagnitude, uint64 newMaxMagnitude - ) external override {} + ) external {} function getQueuedWithdrawal( bytes32 withdrawalRoot diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index 5a76bd46e..d376db783 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -32,18 +32,18 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { function createOperatorDirectedOperatorSetRewardsSubmission( OperatorSet calldata operatorSet, OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions - ) external override {} + ) external {} function getOperatorSetSplit( address operator, OperatorSet calldata operatorSet - ) external view override returns (uint16) {} + ) external view returns (uint16) {} function setOperatorSetSplit( address operator, OperatorSet calldata operatorSet, uint16 split - ) external override {} + ) external {} function createOperatorDirectedAVSRewardsSubmission( address avs, diff --git a/test/unit/AVSRegistrar.t.sol b/test/unit/AVSRegistrar.t.sol index 1dd90226f..936588528 100644 --- a/test/unit/AVSRegistrar.t.sol +++ b/test/unit/AVSRegistrar.t.sol @@ -21,6 +21,8 @@ contract AVSRegistrarTest is MockAVSDeployer { function setUp() public virtual { _deployMockEigenLayerAndAVS(); + vm.prank(address(serviceManager)); + allocationManager.updateAVSMetadataURI(address(serviceManager), "test-avs-metadata"); avsRegistrarMock = new AVSRegistrarMock(); } diff --git a/test/unit/InstantSlasher.t.sol b/test/unit/InstantSlasher.t.sol index 657fbfed1..7aa9199d3 100644 --- a/test/unit/InstantSlasher.t.sol +++ b/test/unit/InstantSlasher.t.sol @@ -213,6 +213,13 @@ contract InstantSlasherTest is Test { AllocationManager.slashOperator.selector ); + PermissionController(coreDeployment.permissionController).setAppointee( + address(serviceManager), + proxyAdminOwner, + coreDeployment.allocationManager, + AllocationManager.updateAVSMetadataURI.selector + ); + vm.stopPrank(); uint8 quorumNumber = 0; @@ -235,6 +242,7 @@ contract InstantSlasherTest is Test { }); vm.startPrank(proxyAdminOwner); + IAllocationManager(coreDeployment.allocationManager).updateAVSMetadataURI(serviceManager, "fake-avs-metadata"); slashingRegistryCoordinator.createSlashableStakeQuorum( operatorSetParams, 1 ether, strategyParams, 0 ); diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 6fd226e09..bb3bd82d8 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -2335,8 +2335,9 @@ contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnit function test_registerALMHook_Reverts() public { cheats.prank(address(registryCoordinator.allocationManager())); cheats.expectRevert(); + /// TODO: registryCoordinator.registerOperator( - defaultOperator, new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams) + defaultOperator, address(0), new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams) ); } @@ -2345,7 +2346,8 @@ contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnit operatorSetIds[0] = 0; cheats.prank(address(registryCoordinator.allocationManager())); cheats.expectRevert(); - registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + /// TODO: + registryCoordinator.deregisterOperator(defaultOperator, address(0), operatorSetIds); } function test_CreateTotalDelegatedStakeQuorum() public { @@ -2577,7 +2579,8 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); cheats.prank(address(registryCoordinator.allocationManager())); - registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + /// TODO: + registryCoordinator.registerOperator(defaultOperator, address(serviceManager), operatorSetIds, data); } function test_registerHook_WithChurn() public { @@ -2634,7 +2637,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT // Prank as allocation manager and call register hook cheats.prank(address(registryCoordinator.allocationManager())); - registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + registryCoordinator.registerOperator(defaultOperator, address(serviceManager), operatorSetIds, data); } function test_updateStakesForQuorum() public { @@ -2703,9 +2706,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); cheats.startPrank(address(registryCoordinator.allocationManager())); - registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); - - // registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + registryCoordinator.registerOperator(defaultOperator, address(serviceManager), operatorSetIds, data); cheats.stopPrank(); } @@ -2748,7 +2749,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); vm.expectRevert(); - registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + registryCoordinator.registerOperator(defaultOperator, address(serviceManager), operatorSetIds, data); } function test_deregisterHook_Reverts_WhenNotALM() public { @@ -2792,10 +2793,10 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); cheats.prank(address(registryCoordinator.allocationManager())); - registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + registryCoordinator.registerOperator(defaultOperator, address(serviceManager), operatorSetIds, data); cheats.expectRevert(); - registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + registryCoordinator.deregisterOperator(defaultOperator, address(serviceManager), operatorSetIds); } function test_DeregisterHook_Reverts_WhenM2Quorum() public { diff --git a/test/unit/VetoableSlasher.t.sol b/test/unit/VetoableSlasher.t.sol index 633e13323..1a30dc885 100644 --- a/test/unit/VetoableSlasher.t.sol +++ b/test/unit/VetoableSlasher.t.sol @@ -235,6 +235,13 @@ contract VetoableSlasherTest is Test { AllocationManager.createOperatorSets.selector ); + PermissionController(coreDeployment.permissionController).setAppointee( + address(serviceManager), + proxyAdminOwner, + coreDeployment.allocationManager, + AllocationManager.updateAVSMetadataURI.selector + ); + vm.stopPrank(); uint8 quorumNumber = 0; @@ -257,6 +264,7 @@ contract VetoableSlasherTest is Test { }); vm.startPrank(proxyAdminOwner); + IAllocationManager(coreDeployment.allocationManager).updateAVSMetadataURI(serviceManager, "fake-avs-metadata"); slashingRegistryCoordinator.createSlashableStakeQuorum( operatorSetParams, 1 ether, strategyParams, 0 );