Skip to content

Commit 0a94b44

Browse files
committed
feat: integrate registrar interfaces
1 parent d5ea2e0 commit 0a94b44

File tree

6 files changed

+536
-84
lines changed

6 files changed

+536
-84
lines changed

src/AVSRegistrar.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol";
5+
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
6+
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
7+
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
8+
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
9+
10+
abstract contract AVSRegistrar is IAVSRegistrar {
11+
function registerOperator(
12+
address operator,
13+
uint32[] calldata operatorSetIds,
14+
bytes calldata data
15+
) external virtual;
16+
17+
function deregisterOperator(
18+
address operator,
19+
uint32[] calldata operatorSetIds
20+
) external virtual;
21+
}

src/RegistryCoordinator.sol

Lines changed: 123 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol";
1616
import {BN254} from "./libraries/BN254.sol";
1717
import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol";
1818
import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol";
19+
import {AVSRegistrar} from "./AVSRegistrar.sol";
1920

2021
import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
2122
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
@@ -38,6 +39,7 @@ contract RegistryCoordinator is
3839
Pausable,
3940
OwnableUpgradeable,
4041
RegistryCoordinatorStorage,
42+
AVSRegistrar,
4143
ISocketUpdater,
4244
ISignatureUtils
4345
{
@@ -134,9 +136,9 @@ contract RegistryCoordinator is
134136
* @dev `operatorSignature` is ignored if the operator's status is already REGISTERED
135137
*/
136138
function registerOperator(
137-
bytes calldata quorumNumbers,
138-
string calldata socket,
139-
IBLSApkRegistry.PubkeyRegistrationParams calldata params,
139+
bytes memory quorumNumbers,
140+
string memory socket,
141+
IBLSApkRegistry.PubkeyRegistrationParams memory params,
140142
SignatureWithSaltAndExpiry memory operatorSignature
141143
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
142144
/**
@@ -184,9 +186,9 @@ contract RegistryCoordinator is
184186
*/
185187
function registerOperatorWithChurn(
186188
bytes calldata quorumNumbers,
187-
string calldata socket,
188-
IBLSApkRegistry.PubkeyRegistrationParams calldata params,
189-
OperatorKickParam[] calldata operatorKickParams,
189+
string memory socket,
190+
IBLSApkRegistry.PubkeyRegistrationParams memory params,
191+
OperatorKickParam[] memory operatorKickParams,
190192
SignatureWithSaltAndExpiry memory churnApproverSignature,
191193
SignatureWithSaltAndExpiry memory operatorSignature
192194
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
@@ -250,20 +252,70 @@ contract RegistryCoordinator is
250252
* @notice Deregisters the caller from one or more quorums
251253
* @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from
252254
*/
253-
function deregisterOperator(bytes calldata quorumNumbers)
255+
function deregisterOperator(bytes memory quorumNumbers)
254256
external
255257
onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR)
256258
{
257259
_deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers});
258260
}
259261

262+
function registerOperator(
263+
address operator,
264+
uint32[] memory operatorSetIds,
265+
bytes memory data
266+
) external override {
267+
/// TODO: Make a mapping for quorums associated with operator sets / ones associated with m2 registrations
268+
/// TODO: only allow registration of operator sets that have been created in the core and don't conflict with existing quorum numbers
269+
require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators");
270+
271+
// Decode registration data from bytes
272+
(
273+
string memory socket,
274+
IBLSApkRegistry.PubkeyRegistrationParams memory params
275+
) = abi.decode(data, (string, IBLSApkRegistry.PubkeyRegistrationParams));
276+
277+
// Get operator ID from BLS registry
278+
bytes32 operatorId = _getOrCreateOperatorId(operator, params);
279+
bytes memory quorumNumbers = new bytes(operatorSetIds.length);
280+
for (uint256 i = 0; i < operatorSetIds.length; i++) {
281+
quorumNumbers[i] = bytes1(uint8(operatorSetIds[i]));
282+
}
283+
284+
// Register operator with decoded parameters
285+
_registerOperatorNew({
286+
operator: operator,
287+
operatorId: operatorId,
288+
quorumNumbers: quorumNumbers,
289+
socket: socket
290+
});
291+
292+
/// TODO: Correctly handle decoding the registration with churn and the normal registration flow parameters
293+
294+
}
295+
296+
function deregisterOperator(
297+
address operator,
298+
uint32[] memory operatorSetIds
299+
) external override {
300+
require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators");
301+
/// TODO: Make a mapping for quorums associated with operator sets / ones associated with m2 registrations
302+
/// TODO: Call _registerOperator to propogate changes to the other contracts
303+
/// TODO: only allow deregistration of operator sets that have been created in the core and don't conflict with existing quorum numbers
304+
bytes memory quorumNumbers = new bytes(operatorSetIds.length);
305+
for (uint256 i = 0; i < operatorSetIds.length; i++) {
306+
quorumNumbers[i] = bytes1(uint8(operatorSetIds[i]));
307+
}
308+
309+
_deregisterOperator(operator, quorumNumbers);
310+
}
311+
260312
/**
261313
* @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator
262314
* is found to be below the minimum stake for the quorum, they are deregistered.
263315
* @dev stakes are queried from the Eigenlayer core DelegationManager contract
264316
* @param operators a list of operator addresses to update
265317
*/
266-
function updateOperators(address[] calldata operators)
318+
function updateOperators(address[] memory operators)
267319
external
268320
onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR)
269321
{
@@ -294,7 +346,7 @@ contract RegistryCoordinator is
294346
* this method is broadcast (but before it is executed), the method will fail
295347
*/
296348
function updateOperatorsForQuorum(
297-
address[][] calldata operatorsPerQuorum,
349+
address[][] memory operatorsPerQuorum,
298350
bytes calldata quorumNumbers
299351
) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) {
300352
// Input validation
@@ -313,7 +365,7 @@ contract RegistryCoordinator is
313365
uint8 quorumNumber = uint8(quorumNumbers[i]);
314366

315367
// Ensure we've passed in the correct number of operators for this quorum
316-
address[] calldata currQuorumOperators = operatorsPerQuorum[i];
368+
address[] memory currQuorumOperators = operatorsPerQuorum[i];
317369
require(
318370
currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber),
319371
"RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total"
@@ -379,7 +431,7 @@ contract RegistryCoordinator is
379431
* @param quorumNumbers the quorum numbers to eject the operator from
380432
* @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset
381433
*/
382-
function ejectOperator(address operator, bytes calldata quorumNumbers) external onlyEjector {
434+
function ejectOperator(address operator, bytes memory quorumNumbers) external onlyEjector {
383435
lastEjectionTimestamp[operator] = block.timestamp;
384436

385437
OperatorInfo storage operatorInfo = _operatorInfo[operator];
@@ -487,7 +539,7 @@ contract RegistryCoordinator is
487539
function _registerOperator(
488540
address operator,
489541
bytes32 operatorId,
490-
bytes calldata quorumNumbers,
542+
bytes memory quorumNumbers,
491543
string memory socket,
492544
SignatureWithSaltAndExpiry memory operatorSignature
493545
) internal virtual returns (RegisterResults memory results) {
@@ -558,6 +610,64 @@ contract RegistryCoordinator is
558610
return results;
559611
}
560612

613+
/**
614+
* @notice Register the operator for one or more quorums. This method updates the
615+
* operator's quorum bitmap, socket, and status, then registers them with each registry.
616+
*/
617+
function _registerOperatorNew(
618+
address operator,
619+
bytes32 operatorId,
620+
bytes memory quorumNumbers,
621+
string memory socket
622+
) internal virtual returns (RegisterResults memory results) {
623+
/**
624+
* Get bitmap of quorums to register for and operator's current bitmap. Validate that:
625+
* - we're trying to register for at least 1 quorum
626+
* - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap)
627+
* - the operator is not currently registered for any quorums we're registering for
628+
* Then, calculate the operator's new bitmap after registration
629+
*/
630+
uint192 quorumsToAdd =
631+
uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
632+
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
633+
require(
634+
!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty"
635+
);
636+
require(
637+
quorumsToAdd.noBitsInCommon(currentBitmap),
638+
"RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"
639+
);
640+
uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd));
641+
642+
// Check that the operator can reregister if ejected
643+
require(
644+
lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp,
645+
"RegistryCoordinator._registerOperator: operator cannot reregister yet"
646+
);
647+
648+
/**
649+
* Update operator's bitmap, socket, and status. Only update operatorInfo if needed:
650+
* if we're `REGISTERED`, the operatorId and status are already correct.
651+
*/
652+
_updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap});
653+
654+
emit OperatorSocketUpdate(operatorId, socket);
655+
656+
// If the operator wasn't registered for any quorums, update their status
657+
// and register them with this AVS in EigenLayer core (DelegationManager)
658+
if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) {
659+
_operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED);
660+
}
661+
662+
// Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry
663+
blsApkRegistry.registerOperator(operator, quorumNumbers);
664+
(results.operatorStakes, results.totalStakes) =
665+
stakeRegistry.registerOperator(operator, operatorId, quorumNumbers);
666+
results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers);
667+
668+
return results;
669+
}
670+
561671
/**
562672
* @notice Checks if the caller is the ejector
563673
* @dev Reverts if the caller is not the ejector
@@ -587,7 +697,7 @@ contract RegistryCoordinator is
587697
*/
588698
function _getOrCreateOperatorId(
589699
address operator,
590-
IBLSApkRegistry.PubkeyRegistrationParams calldata params
700+
IBLSApkRegistry.PubkeyRegistrationParams memory params
591701
) internal returns (bytes32 operatorId) {
592702
operatorId = blsApkRegistry.getOperatorId(operator);
593703
if (operatorId == 0) {

test/mocks/AVSRegistrarMock.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import {AVSRegistrar} from "../../src/AVSRegistrar.sol";
5+
6+
contract AVSRegistrarMock is AVSRegistrar {
7+
function registerOperator(
8+
address operator,
9+
uint32[] calldata operatorSetIds,
10+
bytes calldata data
11+
) external override {}
12+
13+
function deregisterOperator(
14+
address operator,
15+
uint32[] calldata operatorSetIds
16+
) external override {}
17+
}

test/unit/AVSRegistrar.t.sol

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

44
import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol";
55
import {BN254} from "../../src/libraries/BN254.sol";
66
import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol";
77
import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol";
88
import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol";
9+
import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol";
10+
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
11+
import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
12+
import {AVSRegistrarMock} from "../mocks/AVSRegistrarMock.sol";
13+
import {console2 as console} from "forge-std/Test.sol";
914

1015
contract AVSRegistrarTest is MockAVSDeployer {
1116
using BN254 for BN254.G1Point;
1217

18+
AVSRegistrarMock public avsRegistrarMock;
19+
address internal operator = address(420);
20+
1321
function setUp() public virtual {
1422
_deployMockEigenLayerAndAVS();
23+
avsRegistrarMock = new AVSRegistrarMock();
24+
}
25+
26+
function testSetAVSRegistrar() public {
27+
vm.prank(address(serviceManager));
28+
allocationManager.setAVSRegistrar(IAVSRegistrar(address(avsRegistrarMock)));
29+
assertEq(address(allocationManager.getAVSRegistrar(address(serviceManager))), address(avsRegistrarMock));
30+
}
31+
32+
function testRegisterOperator() public {
33+
// Set up AVS registrar
34+
vm.prank(address(serviceManager));
35+
allocationManager.setAVSRegistrar(IAVSRegistrar(address(avsRegistrarMock)));
36+
37+
// Create operator set
38+
uint32 operatorSetId = 1;
39+
IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1);
40+
createSetParams[0] = IAllocationManagerTypes.CreateSetParams({
41+
operatorSetId: operatorSetId,
42+
strategies: new IStrategy[](0)
43+
});
44+
45+
// Create operator set
46+
vm.prank(address(serviceManager));
47+
allocationManager.createOperatorSets(createSetParams);
48+
49+
// Set up registration params
50+
uint32[] memory operatorSetIds = new uint32[](1);
51+
operatorSetIds[0] = operatorSetId;
52+
bytes memory emptyBytes;
53+
54+
// Mock that operator is registered in DelegationManager
55+
vm.mockCall(
56+
address(delegationMock),
57+
abi.encodeCall(delegationMock.isOperator, (operator)),
58+
abi.encode(true)
59+
);
60+
61+
// Register operator
62+
vm.prank(operator);
63+
allocationManager.registerForOperatorSets(
64+
IAllocationManagerTypes.RegisterParams(address(serviceManager), operatorSetIds, emptyBytes)
65+
);
1566
}
1667

17-
function testTrue() public {
18-
assertTrue(true);
68+
function testAllocationManagerDeployed() public {
69+
assertTrue(address(allocationManager) != address(0), "AllocationManager not deployed");
70+
assertTrue(address(allocationManagerImplementation) != address(0), "AllocationManager implementation not deployed");
1971
}
2072
}

0 commit comments

Comments
 (0)