From 603e95e274f3cd54f1c8b074c73ba7d5ba47daa4 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 22 Jan 2025 14:02:34 -0500 Subject: [PATCH] feat: add ejector support for operator sets --- src/RegistryCoordinator.sol | 23 ++++++++++++++++++ src/ServiceManagerBase.sol | 27 ++++++++++++++++------ src/ServiceManagerBaseStorage.sol | 6 ++++- src/interfaces/IServiceManager.sol | 8 +++++++ test/integration/CoreRegistration.t.sol | 3 ++- test/integration/IntegrationDeployer.t.sol | 3 ++- test/mocks/ECDSAServiceManagerMock.sol | 2 ++ test/mocks/ServiceManagerMock.sol | 6 +++-- test/unit/ServiceManagerBase.t.sol | 3 ++- test/unit/ServiceManagerRouter.t.sol | 3 ++- test/utils/MockAVSDeployer.sol | 3 ++- 11 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index c52c4410..84027688 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -553,6 +553,29 @@ contract RegistryCoordinator is operatorInfo.status == OperatorStatus.REGISTERED && !quorumsToRemove.isEmpty() && quorumsToRemove.isSubsetOf(currentBitmap) ) { + // If using operator sets, check for non-M2 quorums + if (isUsingOperatorSets()) { + uint32[] memory nonM2OperatorSetIds = new uint32[](quorumNumbers.length); + uint256 numNonM2Quorums; + + // Check each quorum's stake type + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + if (isM2Quorum[quorumNumber]) { + nonM2OperatorSetIds[numNonM2Quorums++] = quorumNumber; + } + } + + // If any non-M2 quorums found, deregister from AVS + if (numNonM2Quorums > 0) { + // Resize array to exact size needed + assembly { + mstore(nonM2OperatorSetIds, numNonM2Quorums) + } + serviceManager.deregisterOperatorFromOperatorSets(operator, nonM2OperatorSetIds); + } + } + _deregisterOperator({operator: operator, quorumNumbers: quorumNumbers}); } } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 72074630..412faf24 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -7,6 +7,8 @@ import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSD import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager, IAllocationManagerTypes} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; @@ -45,14 +47,16 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, IStakeRegistry __stakeRegistry, - IPermissionController __permissionController + IPermissionController __permissionController, + IAllocationManager __allocationManager ) ServiceManagerBaseStorage( __avsDirectory, __rewardsCoordinator, __registryCoordinator, __stakeRegistry, - __permissionController + __permissionController, + __allocationManager ) { _disableInitializers(); @@ -77,7 +81,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { /// @inheritdoc IServiceManager function removePendingAdmin(address pendingAdmin) external onlyOwner { _permissionController.removePendingAdmin({ - account: address(this), + account: address(this), admin: pendingAdmin }); } @@ -85,7 +89,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { /// @inheritdoc IServiceManager function removeAdmin(address admin) external onlyOwner { _permissionController.removeAdmin({ - account: address(this), + account: address(this), admin: admin }); } @@ -97,9 +101,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { bytes4 selector ) external onlyOwner { _permissionController.setAppointee({ - account: address(this), - appointee: appointee, - target: target, + account: address(this), + appointee: appointee, + target: target, selector: selector }); } @@ -177,6 +181,15 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _avsDirectory.deregisterOperatorFromAVS(operator); } + function deregisterOperatorFromOperatorSets(address operator, uint32[] memory operatorSetIds) public virtual onlyRegistryCoordinator { + IAllocationManager.DeregisterParams memory params = IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: address(this), + operatorSetIds: operatorSetIds + }); + _allocationManager.deregisterFromOperatorSets(params); + } + /** * @notice Sets the rewards initiator address * @param newRewardsInitiator The new rewards initiator address diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index 6a4ef819..ca548ea2 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -8,6 +8,7 @@ import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; @@ -24,6 +25,7 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab * */ IAVSDirectory internal immutable _avsDirectory; + IAllocationManager internal immutable _allocationManager; IRewardsCoordinator internal immutable _rewardsCoordinator; IRegistryCoordinator internal immutable _registryCoordinator; IStakeRegistry internal immutable _stakeRegistry; @@ -56,13 +58,15 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, IStakeRegistry __stakeRegistry, - IPermissionController __permissionController + IPermissionController __permissionController, + IAllocationManager __allocationManager ) { _avsDirectory = __avsDirectory; _rewardsCoordinator = __rewardsCoordinator; _registryCoordinator = __registryCoordinator; _stakeRegistry = __stakeRegistry; _permissionController = __permissionController; + _allocationManager = __allocationManager; } // storage gap for upgradeability diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 5f3ab220..bdf044a0 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -97,4 +97,12 @@ interface IServiceManager is IServiceManagerUI, IServiceManagerErrors { address target, bytes4 selector ) external; + + /** + * @notice Deregisters an operator from specified operator sets + * @param operator The address of the operator to deregister + * @param operatorSetIds The IDs of the operator sets to deregister from + * @dev Only callable by the RegistryCoordinator + */ + function deregisterOperatorFromOperatorSets(address operator, uint32[] memory operatorSetIds) external; } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 68f586a9..636c6e93 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -83,7 +83,8 @@ contract Test_CoreRegistration is MockAVSDeployer { rewardsCoordinatorMock, registryCoordinator, stakeRegistry, - permissionController + permissionController, + allocationManager ); registryCoordinatorImplementation = new RegistryCoordinatorHarness( diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index acb89afd..e7c02468 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -343,7 +343,8 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { rewardsCoordinator, IRegistryCoordinator(registryCoordinator), stakeRegistry, - permissionController + permissionController, + allocationManager ); proxyAdmin.upgrade( diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index df93cdd1..589ff6f2 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -45,4 +45,6 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { address target, bytes4 selector ) external {} + + function deregisterOperatorFromOperatorSets(address operator, uint32[] memory operatorSetIds) external {} } diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 423c08ec..68fbdf4c 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -9,14 +9,16 @@ contract ServiceManagerMock is ServiceManagerBase { IRewardsCoordinator _rewardsCoordinator, IRegistryCoordinator _registryCoordinator, IStakeRegistry _stakeRegistry, - IPermissionController _permissionController + IPermissionController _permissionController, + IAllocationManager _allocationManager ) ServiceManagerBase( _avsDirectory, _rewardsCoordinator, _registryCoordinator, _stakeRegistry, - _permissionController + _permissionController, + _allocationManager ) {} diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 572998ca..ff3181e4 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -90,7 +90,8 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve rewardsCoordinator, registryCoordinatorImplementation, stakeRegistryImplementation, - permissionControllerMock + permissionControllerMock, + allocationManagerMock ); serviceManager = ServiceManagerMock( diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 2e3d1e94..d14a2d6d 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -20,7 +20,8 @@ contract ServiceManagerRouter_UnitTests is MockAVSDeployer { rewardsCoordinatorImplementation, registryCoordinatorImplementation, stakeRegistryImplementation, - permissionControllerMock + permissionControllerMock, + allocationManagerMock ); _registerOperatorWithCoordinator(defaultOperator, MAX_QUORUM_BITMAP, defaultPubKey); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index a0ef478e..ae6229ea 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -229,7 +229,8 @@ contract MockAVSDeployer is Test { IRewardsCoordinator(address(rewardsCoordinatorMock)), registryCoordinator, stakeRegistry, - permissionControllerMock + permissionControllerMock, + allocationManagerMock ); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(serviceManager))),