Skip to content

Commit e7cf8a0

Browse files
committed
fix: call SM only based on m2 quorums
1 parent 9ace56d commit e7cf8a0

8 files changed

+52
-47
lines changed

src/AVSRegistrar.sol

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/RegistryCoordinator.sol

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ contract RegistryCoordinator is SlashingRegistryCoordinator, IRegistryCoordinato
146146
// Check that the quorum numbers are M2 quorums and not operator sets
147147
// if operator sets are enabled
148148
for (uint256 i = 0; i < quorumNumbers.length; i++) {
149-
require(!isOperatorSetAVS || isM2Quorum[uint8(quorumNumbers[i])], OperatorSetsEnabled());
149+
require(!isOperatorSetAVS || _isM2Quorum(uint8(quorumNumbers[i])), OperatorSetsEnabled());
150150
}
151151
_deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers});
152152
}
@@ -155,23 +155,31 @@ contract RegistryCoordinator is SlashingRegistryCoordinator, IRegistryCoordinato
155155
function enableOperatorSets() external onlyOwner {
156156
require(!isOperatorSetAVS, OperatorSetsEnabled());
157157

158-
// Set all existing quorums as m2 quorums
159-
for (uint8 i = 0; i < quorumCount; i++) {
160-
isM2Quorum[i] = true;
161-
}
158+
// Set the bitmap for M2 quorums
159+
M2quorumBitmap = _getQuorumBitmap(quorumCount);
162160

163161
// Enable operator sets mode
164162
isOperatorSetAVS = true;
165163
}
166164

167165
/// @dev Hook to allow for any post-deregister logic
168166
function _afterDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual override {
169-
// If the operator is no longer registered for any quorums, update their status and deregister
167+
uint256 operatorM2QuorumBitmap = newBitmap.minus(M2quorumBitmap);
168+
// If the operator is no longer registered for any M2 quorums, update their status and deregister
170169
// them from the AVS via the EigenLayer core contracts
171-
if (newBitmap.isEmpty()) {
172-
_operatorInfo[operator].status = OperatorStatus.DEREGISTERED;
170+
if (operatorM2QuorumBitmap.isEmpty()) {
173171
serviceManager.deregisterOperatorFromAVS(operator);
174-
emit OperatorDeregistered(operator, operatorId);
175172
}
176173
}
174+
175+
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers
176+
/// and differentiating between operator sets and M2 quorums
177+
function _getQuorumBitmap(uint256 quorumCount) internal pure returns (uint256) {
178+
// This creates a number where all bits up to quorumCount are set to 1
179+
// For example:
180+
// quorumCount = 3 -> 0111 (7 in decimal)
181+
// quorumCount = 5 -> 011111 (31 in decimal)
182+
// This is a safe operation since we limit MAX_QUORUM_COUNT to 192
183+
return (1 << quorumCount) - 1;
184+
}
177185
}

src/SlashingRegistryCoordinator.sol

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol";
1515
import {BN254} from "./libraries/BN254.sol";
1616
import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol";
1717
import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol";
18-
import {AVSRegistrar} from "./AVSRegistrar.sol";
1918

2019
import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
2120
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
@@ -39,7 +38,7 @@ contract SlashingRegistryCoordinator is
3938
Pausable,
4039
OwnableUpgradeable,
4140
SlashingRegistryCoordinatorStorage,
42-
AVSRegistrar,
41+
IAVSRegistrar,
4342
ISocketUpdater,
4443
ISignatureUtils
4544
{
@@ -213,6 +212,7 @@ contract SlashingRegistryCoordinator is
213212
// and register them with this AVS in EigenLayer core (DelegationManager)
214213
if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) {
215214
_operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED);
215+
emit OperatorRegistered(msg.sender, operatorId);
216216
}
217217
}
218218

@@ -582,6 +582,13 @@ contract SlashingRegistryCoordinator is
582582
// Update operator's bitmap and status
583583
_updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap});
584584

585+
// If the operator is no longer registered for any quorums, update their status and deregister
586+
// them from the AVS via the EigenLayer core contracts
587+
if (newBitmap.isEmpty()) {
588+
_operatorInfo[operator].status = OperatorStatus.DEREGISTERED;
589+
emit OperatorDeregistered(operator, operatorId);
590+
}
591+
585592
// Deregister operator with each of the registry contracts
586593
blsApkRegistry.deregisterOperator(operator, quorumNumbers);
587594
stakeRegistry.deregisterOperator(operatorId, quorumNumbers);
@@ -605,7 +612,7 @@ contract SlashingRegistryCoordinator is
605612
// Check each quorum's stake type
606613
for (uint256 i = 0; i < quorumNumbers.length; i++) {
607614
uint8 quorumNumber = uint8(quorumNumbers[i]);
608-
if (isM2Quorum[quorumNumber]) {
615+
if (_isM2Quorum(quorumNumber)) {
609616
operatorSetIds[numOperatorSetQuorums++] = quorumNumber;
610617
}
611618
}
@@ -832,7 +839,7 @@ contract SlashingRegistryCoordinator is
832839
_setOperatorSetParams(quorumNumber, operatorSetParams);
833840

834841
/// Update the AllocationManager if operatorSetQuorum
835-
if (isOperatorSetAVS && !isM2Quorum[quorumNumber]) {
842+
if (isOperatorSetAVS && !_isM2Quorum(quorumNumber)) {
836843
// Create array of CreateSetParams for the new quorum
837844
IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1);
838845

@@ -899,6 +906,12 @@ contract SlashingRegistryCoordinator is
899906
return quorumNumbers;
900907
}
901908

909+
/// @notice Returns true if the quorum number is an M2 quorum
910+
/// @dev We use bitwise and to check if the quorum number is an M2 quorum
911+
function _isM2Quorum(uint8 quorumNumber) internal view returns (bool) {
912+
return M2quorumBitmap.isSet(quorumNumber);
913+
}
914+
902915
function _setOperatorSetParams(
903916
uint8 quorumNumber,
904917
OperatorSetParam memory operatorSetParams
@@ -972,6 +985,11 @@ contract SlashingRegistryCoordinator is
972985
return _operatorInfo[operator].status;
973986
}
974987

988+
/// @notice Returns true if the quorum number is an M2 quorum
989+
function isM2Quorum(uint8 quorumNumber) external view returns (bool) {
990+
return _isM2Quorum(quorumNumber);
991+
}
992+
975993
/**
976994
* @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber`
977995
* @dev Reverts if any of the `operatorIds` was not (yet) registered at `blockNumber`

src/SlashingRegistryCoordinatorStorage.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ abstract contract SlashingRegistryCoordinatorStorage is ISlashingRegistryCoordin
8686
/// This value should only be set once.
8787
address public accountIdentifier;
8888

89-
/// @notice Mapping from quorum number to whether the quorum is an M2 quorum
90-
/// @dev M2 quorums are pre-operator sets and track total delegated stake only
91-
mapping(uint8 => bool) public isM2Quorum;
89+
/// @notice The bitmap containing all M2 quorums. This is only used for existing AVS middlewares that have M2 quorums
90+
/// and need to call `enableOperatorSets()` to enable operator sets mode.
91+
uint256 internal M2quorumBitmap;
9292

9393
constructor(
9494
IStakeRegistry _stakeRegistry,

src/interfaces/IRegistryCoordinator.sol

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ import {IBLSApkRegistry} from "./IBLSApkRegistry.sol";
66
import {ISlashingRegistryCoordinator} from "./ISlashingRegistryCoordinator.sol";
77

88
interface IRegistryCoordinator {
9-
10-
/// Emits when an operator is registered
11-
event OperatorRegistered(address indexed operator, bytes32 indexed operatorId);
12-
/// Emits when an operator is deregistered
13-
event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId);
149

1510
/**
1611
* @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum

src/interfaces/ISlashingRegistryCoordinator.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ interface IRegistryCoordinatorErrors {
3939
interface ISlashingRegistryCoordinator is IRegistryCoordinatorErrors {
4040
// EVENTS
4141

42+
/// Emits when an operator is registered
43+
event OperatorRegistered(address indexed operator, bytes32 indexed operatorId);
44+
/// Emits when an operator is deregistered
45+
event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId);
46+
4247
event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams);
4348

4449
event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover);

test/mocks/AVSRegistrarMock.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// SPDX-License-Identifier: BUSL-1.1
22
pragma solidity ^0.8.27;
33

4-
import {AVSRegistrar} from "../../src/AVSRegistrar.sol";
4+
import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol";
55

6-
contract AVSRegistrarMock is AVSRegistrar {
6+
7+
contract AVSRegistrarMock is IAVSRegistrar {
78
function registerOperator(
89
address operator,
910
uint32[] calldata operatorSetIds,

test/unit/RegistryCoordinatorUnit.t.sol

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,15 +1560,14 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord
15601560
emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators);
15611561

15621562
// Then expect the deregistration events
1563+
cheats.expectEmit(true, true, true, true, address(registryCoordinator));
1564+
emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId);
15631565
cheats.expectEmit(true, true, true, true, address(blsApkRegistry));
15641566
emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, operatorToKickId, quorumNumbers);
15651567
cheats.expectEmit(true, true, true, true, address(stakeRegistry));
15661568
emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0);
15671569
cheats.expectEmit(true, true, true, true, address(indexRegistry));
15681570
emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1);
1569-
cheats.expectEmit(true, true, true, true, address(registryCoordinator));
1570-
emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId);
1571-
15721571
cheats.expectEmit(true, true, true, true, address(registryCoordinator));
15731572
emit OperatorRegistered(operatorToRegister, operatorToRegisterId);
15741573

0 commit comments

Comments
 (0)