Skip to content

Commit 8f33b57

Browse files
committed
fix: registryCoordinator and forcederegistration
1 parent c361eff commit 8f33b57

File tree

3 files changed

+52
-25
lines changed

3 files changed

+52
-25
lines changed

src/RegistryCoordinator.sol

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
pragma solidity ^0.8.27;
33

44
import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
5-
import {IAllocationManager} from
6-
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
5+
import {
6+
IAllocationManager,
7+
OperatorSet
8+
} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
79
import {IBLSApkRegistry, IBLSApkRegistryTypes} from "./interfaces/IBLSApkRegistry.sol";
810
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
911
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
@@ -200,6 +202,44 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
200202
}
201203
}
202204

205+
/**
206+
* @dev Helper function to update operator stakes and deregister loiterers
207+
* Loiterers are AVS registered operators who have force deregistered from the OperatorSet/quorum
208+
* in the core EigenLayer contract AllocationManager but not deregistered from the OperatorSet/quorum
209+
* in this contract. Potentially due to out of gas errors in the deregistration callback. This function
210+
* will handle that edge case by deregistering the operator from the AVS if they are no longer registered
211+
* in the AllocationManager.
212+
*/
213+
function _updateStakesAndDeregisterLoiterers(
214+
address[] memory operators,
215+
bytes32[] memory operatorIds,
216+
uint8 quorumNumber
217+
) internal virtual override {
218+
bytes memory singleQuorumNumber = new bytes(1);
219+
singleQuorumNumber[0] = bytes1(quorumNumber);
220+
bool[] memory doesNotMeetStakeThreshold =
221+
stakeRegistry.updateOperatorsStake(operators, operatorIds, quorumNumber);
222+
for (uint256 j = 0; j < operators.length; ++j) {
223+
// whether the operator is registered in the core EigenLayer contract AllocationManager
224+
bool registeredInCore = allocationManager.isMemberOfOperatorSet(
225+
operators[j], OperatorSet({avs: accountIdentifier, id: uint32(quorumNumber)})
226+
);
227+
228+
// If the operator does not have the minimum stake, they need to be force deregistered.
229+
// Additionally, it is possible for an operator to have deregistered from an OperatorSet
230+
// in the core EigenLayer contract AllocationManager but not have the deregistration
231+
// callback succeed here in `deregisterOperator` due to out of gas errors. If that is the case,
232+
// we need to deregister the operator from the OperatorSet in this contract
233+
if (doesNotMeetStakeThreshold[j] || (!registeredInCore && !_isM2Quorum(quorumNumber))) {
234+
_deregisterOperator({
235+
operator: operators[j],
236+
quorumNumbers: singleQuorumNumber,
237+
shouldForceDeregister: registeredInCore && !_isM2Quorum(quorumNumber)
238+
});
239+
}
240+
}
241+
}
242+
203243
/// @notice Return bitmap representing all quorums(Legacy M2 and OperatorSet) quorums
204244
function _getTotalQuorumBitmap() internal view returns (uint256) {
205245
// This creates a number where all bits up to quorumCount are set to 1

src/SlashingRegistryCoordinator.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,11 +626,19 @@ contract SlashingRegistryCoordinator is
626626
);
627627
}
628628

629+
/**
630+
* @dev Helper function to update operator stakes and deregister loiterers
631+
* Loiterers are AVS registered operators who have force deregistered from the OperatorSet/quorum
632+
* in the core EigenLayer contract AllocationManager but not deregistered from the OperatorSet/quorum
633+
* in this contract. Potentially due to out of gas errors in the deregistration callback. This function
634+
* will handle that edge case by deregistering the operator from the AVS if they are no longer registered
635+
* in the AllocationManager.
636+
*/
629637
function _updateStakesAndDeregisterLoiterers(
630638
address[] memory operators,
631639
bytes32[] memory operatorIds,
632640
uint8 quorumNumber
633-
) internal {
641+
) internal virtual {
634642
bytes memory singleQuorumNumber = new bytes(1);
635643
singleQuorumNumber[0] = bytes1(quorumNumber);
636644
bool[] memory doesNotMeetStakeThreshold =

test/mocks/AllocationManagerMock.sol

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -151,25 +151,4 @@ contract AllocationManagerIntermediate is IAllocationManager {
151151
) external view virtual returns (bool) {}
152152
}
153153

154-
contract AllocationManagerMock is AllocationManagerIntermediate {
155-
bool defaultIsMemberOfOperatorSet;
156-
157-
constructor() {
158-
// Return true by default to so that the SlashingRegistryCoordinator won't force deregister
159-
// an operator for every quorum when a `updateOperators`,`updateOperatorsForQuorum` call is made
160-
defaultIsMemberOfOperatorSet = true;
161-
}
162-
163-
function isMemberOfOperatorSet(
164-
address operator,
165-
OperatorSet memory operatorSet
166-
) external view virtual override returns (bool) {
167-
return defaultIsMemberOfOperatorSet;
168-
}
169-
170-
function setDefaultIsMemberOfOperatorSet(
171-
bool isMemberOfOperatorSet
172-
) external {
173-
defaultIsMemberOfOperatorSet = isMemberOfOperatorSet;
174-
}
175-
}
154+
contract AllocationManagerMock is AllocationManagerIntermediate {}

0 commit comments

Comments
 (0)