diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index cef18ab5..c92adbf3 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -2,29 +2,14 @@ pragma solidity ^0.8.27; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IStrategy } from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IAllocationManager, OperatorSet, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; -import {BN254} from "./libraries/BN254.sol"; -import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; -import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; -import {AVSRegistrar} from "./AVSRegistrar.sol"; - -import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; - -import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; - import {SlashingRegistryCoordinator} from "./SlashingRegistryCoordinator.sol"; /** @@ -37,7 +22,9 @@ import {SlashingRegistryCoordinator} from "./SlashingRegistryCoordinator.sol"; */ contract RegistryCoordinator is SlashingRegistryCoordinator, IRegistryCoordinator { using BitmapUtils for *; - using BN254 for BN254.G1Point; + + /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts + IServiceManager public immutable serviceManager; constructor( IServiceManager _serviceManager, @@ -48,14 +35,15 @@ contract RegistryCoordinator is SlashingRegistryCoordinator, IRegistryCoordinato IPauserRegistry _pauserRegistry ) SlashingRegistryCoordinator( - _serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _allocationManager, _pauserRegistry ) - {} + { + serviceManager = _serviceManager; + } /** * @@ -192,4 +180,84 @@ contract RegistryCoordinator is SlashingRegistryCoordinator, IRegistryCoordinato // Enable operator sets mode isOperatorSetAVS = true; } + + /** + * + * INTERNAL FUNCTIONS + * + */ + + /** + * @notice Register the operator for one or more quorums. This method updates the + * operator's quorum bitmap, socket, and status, then registers them with each registry. + */ + function _registerOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + string memory socket, + SignatureWithSaltAndExpiry memory operatorSignature + ) internal virtual returns (RegisterResults memory results) { + /** + * Get bitmap of quorums to register for and operator's current bitmap. Validate that: + * - we're trying to register for at least 1 quorum + * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is not currently registered for any quorums we're registering for + * Then, calculate the operator's new bitmap after registration + */ + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + require( + !quorumsToAdd.isEmpty(), BitmapEmpty() + ); + require( + quorumsToAdd.noBitsInCommon(currentBitmap), + AlreadyRegisteredForQuorums() + ); + uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); + + // Check that the operator can reregister if ejected + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + CannotReregisterYet() + ); + + /** + * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: + * if we're `REGISTERED`, the operatorId and status are already correct. + */ + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + emit OperatorSocketUpdate(operatorId, socket); + + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = + OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); + + serviceManager.registerOperatorToAVS(operator, operatorSignature); + emit OperatorRegistered(operator, operatorId); + } + + // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry + blsApkRegistry.registerOperator(operator, quorumNumbers); + (results.operatorStakes, results.totalStakes) = + stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + + return results; + } + + /// @dev Hook to allow for any post-deregister logic + function _afterDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual override { + // If the operator is no longer registered for any quorums, update their status and deregister + // them from the AVS via the EigenLayer core contracts + if (newBitmap.isEmpty()) { + _operatorInfo[operator].status = OperatorStatus.DEREGISTERED; + serviceManager.deregisterOperatorFromAVS(operator); + emit OperatorDeregistered(operator, operatorId); + } + } } diff --git a/src/SlashingRegistryCoordinator.sol b/src/SlashingRegistryCoordinator.sol index 455afe56..c47832cf 100644 --- a/src/SlashingRegistryCoordinator.sol +++ b/src/SlashingRegistryCoordinator.sol @@ -9,7 +9,6 @@ import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; -import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -47,6 +46,11 @@ contract SlashingRegistryCoordinator is using BitmapUtils for *; using BN254 for BN254.G1Point; + modifier onlyAllocationManager() { + _checkAllocationManager(); + _; + } + modifier onlyEjector() { _checkEjector(); _; @@ -60,7 +64,6 @@ contract SlashingRegistryCoordinator is } constructor( - IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, @@ -68,7 +71,6 @@ contract SlashingRegistryCoordinator is IPauserRegistry _pauserRegistry ) SlashingRegistryCoordinatorStorage( - _serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, @@ -91,27 +93,18 @@ contract SlashingRegistryCoordinator is address _churnApprover, address _ejector, uint256 _initialPausedStatus, - OperatorSetParam[] memory _operatorSetParams, - uint96[] memory _minimumStakes, - IStakeRegistry.StrategyParams[][] memory _strategyParams, - StakeType[] memory _stakeTypes, - uint32[] memory _lookAheadPeriods + address _accountIdentifier ) external initializer { _transferOwnership(_initialOwner); _setChurnApprover(_churnApprover); _setPausedStatus(_initialPausedStatus); _setEjector(_ejector); - + _setAccountIdentifier(_accountIdentifier); // Add registry contracts to the registries array registries.push(address(stakeRegistry)); registries.push(address(blsApkRegistry)); registries.push(address(indexRegistry)); - // Create quorums - for (uint256 i = 0; i < _operatorSetParams.length; i++) { - _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i], _stakeTypes[i], _lookAheadPeriods[i]); - } - // Set the AVS to be OperatorSets compatible isOperatorSetAVS = true; } @@ -149,12 +142,11 @@ contract SlashingRegistryCoordinator is address operator, uint32[] memory operatorSetIds, bytes calldata data - ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(isOperatorSetAVS, OperatorSetsNotEnabled()); for (uint256 i = 0; i < operatorSetIds.length; i++) { require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - _checkAllocationManager(); bytes memory quorumNumbers = new bytes(operatorSetIds.length); for (uint256 i = 0; i < operatorSetIds.length; i++) { quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); @@ -194,12 +186,11 @@ contract SlashingRegistryCoordinator is function deregisterOperator( address operator, uint32[] memory operatorSetIds - ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(isOperatorSetAVS, OperatorSetsNotEnabled()); for (uint256 i = 0; i < operatorSetIds.length; i++) { require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - _checkAllocationManager(); bytes memory quorumNumbers = new bytes(operatorSetIds.length); for (uint256 i = 0; i < operatorSetIds.length; i++) { quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); @@ -384,6 +375,15 @@ contract SlashingRegistryCoordinator is _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 + */ + 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 @@ -400,70 +400,6 @@ contract SlashingRegistryCoordinator is * */ - /** - * @notice Register the operator for one or more quorums. This method updates the - * operator's quorum bitmap, socket, and status, then registers them with each registry. - */ - function _registerOperator( - address operator, - bytes32 operatorId, - bytes memory quorumNumbers, - string memory socket, - SignatureWithSaltAndExpiry memory operatorSignature - ) internal virtual returns (RegisterResults memory results) { - /** - * Get bitmap of quorums to register for and operator's current bitmap. Validate that: - * - we're trying to register for at least 1 quorum - * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - * - the operator is not currently registered for any quorums we're registering for - * Then, calculate the operator's new bitmap after registration - */ - uint192 quorumsToAdd = - uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require( - !quorumsToAdd.isEmpty(), BitmapEmpty() - ); - require( - quorumsToAdd.noBitsInCommon(currentBitmap), - AlreadyRegisteredForQuorums() - ); - uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); - - // Check that the operator can reregister if ejected - require( - lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, - CannotReregisterYet() - ); - - /** - * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: - * if we're `REGISTERED`, the operatorId and status are already correct. - */ - _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - - emit OperatorSocketUpdate(operatorId, socket); - - // If the operator wasn't registered for any quorums, update their status - // and register them with this AVS in EigenLayer core (DelegationManager) - if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { - _operatorInfo[operator] = - OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); - - serviceManager.registerOperatorToAVS(operator, operatorSignature); - emit OperatorRegistered(operator, operatorId); - - } - - // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry - blsApkRegistry.registerOperator(operator, quorumNumbers); - (results.operatorStakes, results.totalStakes) = - stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); - - return results; - } - /** * @notice Register the operator for one or more quorums. This method updates the * operator's quorum bitmap, socket, and status, then registers them with each registry. @@ -484,6 +420,10 @@ contract SlashingRegistryCoordinator is uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); + + // call hook to allow for any pre-register logic + _beforeRegisterOperator(operator, operatorId, quorumNumbers, currentBitmap); + require( !quorumsToAdd.isEmpty(), BitmapEmpty() ); @@ -519,6 +459,9 @@ contract SlashingRegistryCoordinator is stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + // call hook to allow for any post-register logic + _afterRegisterOperator(operator, operatorId, quorumNumbers, newBitmap); + return results; } @@ -579,6 +522,56 @@ contract SlashingRegistryCoordinator is } } + /** + * @dev Deregister the operator from one or more quorums + * This method updates the operator's quorum bitmap and status, then deregisters + * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry + */ + function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { + // Fetch the operator's info and ensure they are registered + OperatorInfo storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + + // call hook to allow for any pre-deregister logic + _beforeDeregisterOperator(operator, operatorId, quorumNumbers, currentBitmap); + + require( + operatorInfo.status == OperatorStatus.REGISTERED, + NotRegistered() + ); + + /** + * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: + * - we're trying to deregister from at least 1 quorum + * - the quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is currently registered for any quorums we're trying to deregister from + * Then, calculate the operator's new bitmap after deregistration + */ + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + require( + !quorumsToRemove.isEmpty(), + BitmapCannotBeZero() + ); + require( + quorumsToRemove.isSubsetOf(currentBitmap), + NotRegisteredForQuorum() + ); + uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); + + // Update operator's bitmap and status + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + // Deregister operator with each of the registry contracts + blsApkRegistry.deregisterOperator(operator, quorumNumbers); + stakeRegistry.deregisterOperator(operatorId, quorumNumbers); + indexRegistry.deregisterOperator(operatorId, quorumNumbers); + + // call hook to allow for any post-deregister logic + _afterDeregisterOperator(operator, operatorId, quorumNumbers, newBitmap); + } + /** * @notice Checks if the caller is the ejector * @dev Reverts if the caller is not the ejector @@ -673,57 +666,6 @@ contract SlashingRegistryCoordinator is ); } - /** - * @dev Deregister the operator from one or more quorums - * This method updates the operator's quorum bitmap and status, then deregisters - * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry - */ - function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { - // Fetch the operator's info and ensure they are registered - OperatorInfo storage operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - require( - operatorInfo.status == OperatorStatus.REGISTERED, - NotRegistered() - ); - - /** - * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: - * - we're trying to deregister from at least 1 quorum - * - the quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - * - the operator is currently registered for any quorums we're trying to deregister from - * Then, calculate the operator's new bitmap after deregistration - */ - uint192 quorumsToRemove = - uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require( - !quorumsToRemove.isEmpty(), - BitmapCannotBeZero() - ); - require( - quorumsToRemove.isSubsetOf(currentBitmap), - NotRegisteredForQuorum() - ); - uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); - - // Update operator's bitmap and status - _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - - // If the operator is no longer registered for any quorums, update their status and deregister - // them from the AVS via the EigenLayer core contracts - if (newBitmap.isEmpty()) { - operatorInfo.status = OperatorStatus.DEREGISTERED; - serviceManager.deregisterOperatorFromAVS(operator); - emit OperatorDeregistered(operator, operatorId); - } - - // Deregister operator with each of the registry contracts - blsApkRegistry.deregisterOperator(operator, quorumNumbers); - stakeRegistry.deregisterOperator(operatorId, quorumNumbers); - indexRegistry.deregisterOperator(operatorId, quorumNumbers); - } - /** * @notice Updates the StakeRegistry's view of the operator's stake in one or more quorums. * For any quorums where the StakeRegistry finds the operator is under the configured minimum @@ -852,7 +794,7 @@ contract SlashingRegistryCoordinator is strategies: strategies }); allocationManager.createOperatorSets({ - avs: address(serviceManager), + avs: accountIdentifier, params: createSetParams }); } @@ -911,6 +853,22 @@ contract SlashingRegistryCoordinator is ejector = newEjector; } + function _setAccountIdentifier(address _accountIdentifier) internal { + accountIdentifier = _accountIdentifier; + } + + /// @dev Hook to allow for any pre-register logic in `_registerOperator` + function _beforeRegisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 currentBitmap) internal virtual {} + + /// @dev Hook to allow for any post-register logic in `_registerOperator` + function _afterRegisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual {} + + /// @dev Hook to allow for any pre-deregister logic in `_deregisterOperator` + function _beforeDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 currentBitmap) internal virtual {} + + /// @dev Hook to allow for any post-deregister logic in `_deregisterOperator` + function _afterDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual {} + /** * * VIEW FUNCTIONS diff --git a/src/SlashingRegistryCoordinatorStorage.sol b/src/SlashingRegistryCoordinatorStorage.sol index 49b057db..9322238e 100644 --- a/src/SlashingRegistryCoordinatorStorage.sol +++ b/src/SlashingRegistryCoordinatorStorage.sol @@ -33,8 +33,6 @@ abstract contract SlashingRegistryCoordinatorStorage is ISlashingRegistryCoordin /// @notice The maximum number of quorums this contract supports uint8 internal constant MAX_QUORUM_COUNT = 192; - /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts - IServiceManager public immutable serviceManager; /// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' aggregate BLS public keys per quorum IBLSApkRegistry public immutable blsApkRegistry; /// @notice the Stake Registry contract that will keep track of operators' stakes @@ -79,18 +77,21 @@ abstract contract SlashingRegistryCoordinatorStorage is ISlashingRegistryCoordin /// @dev If true, operators must register to operator sets via the AllocationManager bool public isOperatorSetAVS; + /// @notice The account identifier for this AVS (used for UAM integration in EigenLayer) + /// @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. + /// This value should only be set once. + address public accountIdentifier; + /// @notice Mapping from quorum number to whether the quorum is an M2 quorum /// @dev M2 quorums are pre-operator sets and track total delegated stake only mapping(uint8 => bool) public isM2Quorum; constructor( - IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, IAllocationManager _allocationManager ) { - serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 2f132ae8..9632720d 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -27,13 +27,13 @@ contract StakeRegistry is StakeRegistryStorage { using BitmapUtils for *; - modifier onlyRegistryCoordinator() { - _checkRegistryCoordinator(); + modifier onlySlashingRegistryCoordinator() { + _checkSlashingRegistryCoordinator(); _; } modifier onlyCoordinatorOwner() { - _checkRegistryCoordinatorOwner(); + _checkSlashingRegistryCoordinatorOwner(); _; } @@ -43,12 +43,18 @@ contract StakeRegistry is StakeRegistryStorage { } constructor( - ISlashingRegistryCoordinator _registryCoordinator, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, IDelegationManager _delegationManager, IAVSDirectory _avsDirectory, IAllocationManager _allocationManager, IServiceManager _serviceManager - ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _avsDirectory, _allocationManager, _serviceManager) {} + ) StakeRegistryStorage( + _slashingRegistryCoordinator, + _delegationManager, + _avsDirectory, + _allocationManager, + _serviceManager + ) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -71,7 +77,7 @@ contract StakeRegistry is StakeRegistryStorage { address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator returns (uint96[] memory, uint96[] memory) { + ) public virtual onlySlashingRegistryCoordinator returns (uint96[] memory, uint96[] memory) { uint96[] memory currentStakes = new uint96[](quorumNumbers.length); uint96[] memory totalStakes = new uint96[](quorumNumbers.length); @@ -115,7 +121,7 @@ contract StakeRegistry is StakeRegistryStorage { function deregisterOperator( bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { /** * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal @@ -149,7 +155,7 @@ contract StakeRegistry is StakeRegistryStorage { address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) external onlyRegistryCoordinator returns (uint192) { + ) external onlySlashingRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; /** @@ -194,7 +200,7 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory _strategyParams - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); @@ -214,7 +220,7 @@ contract StakeRegistry is StakeRegistryStorage { uint96 minimumStake, uint32 lookAheadPeriod, StrategyParams[] memory _strategyParams - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); @@ -267,7 +273,7 @@ contract StakeRegistry is StakeRegistryStorage { strategiesToAdd[i] = _strategyParams[i].strategy; } allocationManager.addStrategiesToOperatorSet({ - avs: address(serviceManager), + avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), operatorSetId: quorumNumber, strategies: strategiesToAdd }); @@ -306,7 +312,7 @@ contract StakeRegistry is StakeRegistryStorage { if (isOperatorSetQuorum(quorumNumber)){ allocationManager.removeStrategiesFromOperatorSet({ - avs: address(serviceManager), + avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), operatorSetId: quorumNumber, strategies: _strategiesToRemove }); @@ -525,7 +531,7 @@ contract StakeRegistry is StakeRegistryStorage { uint32 beforeTimestamp = uint32(block.number + slashableStakeLookAheadPerQuorum[quorumNumber]); uint256[][] memory slashableShares = allocationManager.getMinimumSlashableStake( - OperatorSet(address(serviceManager), quorumNumber), + OperatorSet(ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), quorumNumber), operators, strategiesPerQuorum[quorumNumber], beforeTimestamp @@ -831,12 +837,12 @@ contract StakeRegistry is StakeRegistryStorage { } - function _checkRegistryCoordinator() internal view { - require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); + function _checkSlashingRegistryCoordinator() internal view { + require(msg.sender == registryCoordinator, OnlySlashingRegistryCoordinator()); } - function _checkRegistryCoordinatorOwner() internal view { - require(msg.sender == ISlashingRegistryCoordinator(registryCoordinator).owner(), OnlyRegistryCoordinatorOwner()); + function _checkSlashingRegistryCoordinatorOwner() internal view { + require(msg.sender == ISlashingRegistryCoordinator(registryCoordinator).owner(), OnlySlashingRegistryCoordinatorOwner()); } function _checkQuorumExists(uint8 quorumNumber) internal view { diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index fafcbc9c..8383dccc 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -65,13 +65,13 @@ abstract contract StakeRegistryStorage is IStakeRegistry { mapping(uint8 quorumNumber => uint32) public slashableStakeLookAheadPerQuorum; constructor( - ISlashingRegistryCoordinator _registryCoordinator, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, IDelegationManager _delegationManager, IAVSDirectory _avsDirectory, IAllocationManager _allocationManager, IServiceManager _serviceManager ) { - registryCoordinator = address(_registryCoordinator); + registryCoordinator = address(_slashingRegistryCoordinator); delegation = _delegationManager; avsDirectory = _avsDirectory; serviceManager = _serviceManager; diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index a0449476..de48c810 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -6,6 +6,11 @@ import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; import {ISlashingRegistryCoordinator} from "./ISlashingRegistryCoordinator.sol"; interface IRegistryCoordinator { + + /// Emits when an operator is registered + event OperatorRegistered(address indexed operator, bytes32 indexed operatorId); + /// Emits when an operator is deregistered + event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId); /** * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol index f9eb47fa..b63f22c1 100644 --- a/src/interfaces/ISlashingRegistryCoordinator.sol +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -39,12 +39,6 @@ interface IRegistryCoordinatorErrors { interface ISlashingRegistryCoordinator is IRegistryCoordinatorErrors{ // EVENTS - /// Emits when an operator is registered - event OperatorRegistered(address indexed operator, bytes32 indexed operatorId); - - /// Emits when an operator is deregistered - event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId); - event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams); event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); @@ -202,5 +196,10 @@ interface ISlashingRegistryCoordinator is IRegistryCoordinatorErrors{ /// @notice The owner of the registry coordinator function owner() external view returns (address); - function serviceManager() external view returns (IServiceManager); + /** + * @notice The account identifier for this AVS (used for UAM integration in EigenLayer) + * @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. + * This value should only be set once. + */ + function accountIdentifier() external view returns (address); } diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 85417ecc..93ade194 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -13,9 +13,9 @@ enum StakeType { interface IStakeRegistryErrors { /// @dev Thrown when the caller is not the registry coordinator - error OnlyRegistryCoordinator(); + error OnlySlashingRegistryCoordinator(); /// @dev Thrown when the caller is not the owner of the registry coordinator - error OnlyRegistryCoordinatorOwner(); + error OnlySlashingRegistryCoordinatorOwner(); /// @dev Thrown when the stake is below the minimum required for a quorum error BelowMinimumStakeRequirement(); /// @dev Thrown when a quorum being created already exists. diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol index 536259ba..3b5a7a3d 100644 --- a/src/slashers/InstantSlasher.sol +++ b/src/slashers/InstantSlasher.sol @@ -3,16 +3,16 @@ 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 {IServiceManager} from "../interfaces/IServiceManager.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; contract InstantSlasher is SlasherBase { constructor( IAllocationManager _allocationManager, - IServiceManager _serviceManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, address _slasher - ) SlasherBase(_allocationManager, _serviceManager) {} + ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} function initialize(address _slasher) external initializer { __SlasherBase_init(_slasher); diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 37cbb3b9..a9854cc3 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; -import {IServiceManager} from "../interfaces/IServiceManager.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; contract VetoableSlasher is SlasherBase { @@ -20,9 +20,9 @@ contract VetoableSlasher is SlasherBase { constructor( IAllocationManager _allocationManager, - IServiceManager _serviceManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, address _slasher - ) SlasherBase(_allocationManager, _serviceManager) {} + ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} function initialize( address _vetoCommittee, diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index 30a269f6..d2953069 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {IServiceManager} from "../../interfaces/IServiceManager.sol"; -import {SlasherStorage} from "./SlasherStorage.sol"; +import {SlasherStorage, ISlashingRegistryCoordinator} from "./SlasherStorage.sol"; import {IAllocationManagerTypes, IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; @@ -16,8 +15,8 @@ abstract contract SlasherBase is Initializable, SlasherStorage { constructor( IAllocationManager _allocationManager, - IServiceManager _serviceManager - ) SlasherStorage(_allocationManager, _serviceManager) { + ISlashingRegistryCoordinator _registryCoordinator + ) SlasherStorage(_allocationManager, _registryCoordinator) { _disableInitializers(); } @@ -30,7 +29,7 @@ abstract contract SlasherBase is Initializable, SlasherStorage { IAllocationManager.SlashingParams memory _params ) internal virtual { allocationManager.slashOperator({ - avs: address(serviceManager), + avs: slashingRegistryCoordinator.accountIdentifier(), params: _params }); emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.wadsToSlash, _params.description); diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol index 3e5e9860..72d871f9 100644 --- a/src/slashers/base/SlasherStorage.sol +++ b/src/slashers/base/SlasherStorage.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.27; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ISlasher} from "../../interfaces/ISlasher.sol"; -import {IServiceManager} from "../../interfaces/IServiceManager.sol"; +import {ISlashingRegistryCoordinator} from "../../interfaces/ISlashingRegistryCoordinator.sol"; contract SlasherStorage is ISlasher { /******************************************************************************* @@ -12,9 +12,8 @@ contract SlasherStorage is ISlasher { /// @notice the AllocationManager that tracks OperatorSets and Slashing in EigenLayer IAllocationManager public immutable allocationManager; - /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts - IServiceManager public immutable serviceManager; - + /// @notice the SlashingRegistryCoordinator for this AVS + ISlashingRegistryCoordinator public immutable slashingRegistryCoordinator; /******************************************************************************* STATE *******************************************************************************/ @@ -23,9 +22,9 @@ contract SlasherStorage is ISlasher { uint256 public nextRequestId; - constructor(IAllocationManager _allocationManager, IServiceManager _serviceManager) { + constructor(IAllocationManager _allocationManager, ISlashingRegistryCoordinator _slashingRegistryCoordinator) { allocationManager = _allocationManager; - serviceManager = _serviceManager; + slashingRegistryCoordinator = _slashingRegistryCoordinator; } uint256[48] private __gap;