Skip to content

Commit

Permalink
fix: registryCoordinator and forcederegistration
Browse files Browse the repository at this point in the history
  • Loading branch information
8sunyuan committed Feb 9, 2025
1 parent c361eff commit 8f33b57
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 25 deletions.
44 changes: 42 additions & 2 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pragma solidity ^0.8.27;

import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {
IAllocationManager,
OperatorSet
} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IBLSApkRegistry, IBLSApkRegistryTypes} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
Expand Down Expand Up @@ -200,6 +202,44 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
}
}

/**
* @dev Helper function to update operator stakes and deregister loiterers
* Loiterers are AVS registered operators who have force deregistered from the OperatorSet/quorum
* in the core EigenLayer contract AllocationManager but not deregistered from the OperatorSet/quorum
* in this contract. Potentially due to out of gas errors in the deregistration callback. This function
* will handle that edge case by deregistering the operator from the AVS if they are no longer registered
* in the AllocationManager.
*/
function _updateStakesAndDeregisterLoiterers(
address[] memory operators,
bytes32[] memory operatorIds,
uint8 quorumNumber
) internal virtual override {
bytes memory singleQuorumNumber = new bytes(1);
singleQuorumNumber[0] = bytes1(quorumNumber);
bool[] memory doesNotMeetStakeThreshold =
stakeRegistry.updateOperatorsStake(operators, operatorIds, quorumNumber);
for (uint256 j = 0; j < operators.length; ++j) {
// whether the operator is registered in the core EigenLayer contract AllocationManager
bool registeredInCore = allocationManager.isMemberOfOperatorSet(
operators[j], OperatorSet({avs: accountIdentifier, id: uint32(quorumNumber)})
);

// If the operator does not have the minimum stake, they need to be force deregistered.
// Additionally, it is possible for an operator to have deregistered from an OperatorSet
// in the core EigenLayer contract AllocationManager but not have the deregistration
// callback succeed here in `deregisterOperator` due to out of gas errors. If that is the case,
// we need to deregister the operator from the OperatorSet in this contract
if (doesNotMeetStakeThreshold[j] || (!registeredInCore && !_isM2Quorum(quorumNumber))) {
_deregisterOperator({
operator: operators[j],
quorumNumbers: singleQuorumNumber,
shouldForceDeregister: registeredInCore && !_isM2Quorum(quorumNumber)
});
}
}
}

/// @notice Return bitmap representing all quorums(Legacy M2 and OperatorSet) quorums
function _getTotalQuorumBitmap() internal view returns (uint256) {
// This creates a number where all bits up to quorumCount are set to 1
Expand Down
10 changes: 9 additions & 1 deletion src/SlashingRegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -626,11 +626,19 @@ contract SlashingRegistryCoordinator is
);
}

/**
* @dev Helper function to update operator stakes and deregister loiterers
* Loiterers are AVS registered operators who have force deregistered from the OperatorSet/quorum
* in the core EigenLayer contract AllocationManager but not deregistered from the OperatorSet/quorum
* in this contract. Potentially due to out of gas errors in the deregistration callback. This function
* will handle that edge case by deregistering the operator from the AVS if they are no longer registered
* in the AllocationManager.
*/
function _updateStakesAndDeregisterLoiterers(
address[] memory operators,
bytes32[] memory operatorIds,
uint8 quorumNumber
) internal {
) internal virtual {
bytes memory singleQuorumNumber = new bytes(1);
singleQuorumNumber[0] = bytes1(quorumNumber);
bool[] memory doesNotMeetStakeThreshold =
Expand Down
23 changes: 1 addition & 22 deletions test/mocks/AllocationManagerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,25 +151,4 @@ contract AllocationManagerIntermediate is IAllocationManager {
) external view virtual returns (bool) {}
}

contract AllocationManagerMock is AllocationManagerIntermediate {
bool defaultIsMemberOfOperatorSet;

constructor() {
// Return true by default to so that the SlashingRegistryCoordinator won't force deregister
// an operator for every quorum when a `updateOperators`,`updateOperatorsForQuorum` call is made
defaultIsMemberOfOperatorSet = true;
}

function isMemberOfOperatorSet(
address operator,
OperatorSet memory operatorSet
) external view virtual override returns (bool) {
return defaultIsMemberOfOperatorSet;
}

function setDefaultIsMemberOfOperatorSet(
bool isMemberOfOperatorSet
) external {
defaultIsMemberOfOperatorSet = isMemberOfOperatorSet;
}
}
contract AllocationManagerMock is AllocationManagerIntermediate {}

0 comments on commit 8f33b57

Please sign in to comment.