From d56fdb3bda0cc7586831f1535e49a88e4f1910a6 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Tue, 20 May 2025 16:45:44 -0400 Subject: [PATCH 01/18] feat: add avs registrar templates --- src/interfaces/IAVSRegistrarInternal.sol | 22 +++ src/interfaces/IAllowlist.sol | 54 +++++++ src/interfaces/IKeyRegistrar.sol | 19 +++ src/middlewareV2/registrar/AVSRegistrar.sol | 143 ++++++++++++++++++ .../registrar/AVSRegistrarStorage.sol | 50 ++++++ .../registrar/modules/Allowlist.sol | 56 +++++++ .../registrar/modules/AllowlistStorage.sol | 21 +++ .../registrar/modules/SocketRegistry.sol | 58 +++++++ .../modules/SocketRegistryStorage.sol | 26 ++++ .../presets/AVSRegistrarAllowlist.sol | 29 ++++ .../presets/AVSRegistrarAsIdentifier.sol | 38 +++++ .../presets/AVSRegistrarWithSocket.sol | 40 +++++ 12 files changed, 556 insertions(+) create mode 100644 src/interfaces/IAVSRegistrarInternal.sol create mode 100644 src/interfaces/IAllowlist.sol create mode 100644 src/interfaces/IKeyRegistrar.sol create mode 100644 src/middlewareV2/registrar/AVSRegistrar.sol create mode 100644 src/middlewareV2/registrar/AVSRegistrarStorage.sol create mode 100644 src/middlewareV2/registrar/modules/Allowlist.sol create mode 100644 src/middlewareV2/registrar/modules/AllowlistStorage.sol create mode 100644 src/middlewareV2/registrar/modules/SocketRegistry.sol create mode 100644 src/middlewareV2/registrar/modules/SocketRegistryStorage.sol create mode 100644 src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol create mode 100644 src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol create mode 100644 src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol diff --git a/src/interfaces/IAVSRegistrarInternal.sol b/src/interfaces/IAVSRegistrarInternal.sol new file mode 100644 index 00000000..9690ef5b --- /dev/null +++ b/src/interfaces/IAVSRegistrarInternal.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +interface IAVSRegistrarErrors { + /// @notice Thrown when a key is not registered + error KeyNotRegistered(uint32 operatorSetId); + /// @notice Thrown when the AVSRegistrar does not support the AVS + error InvalidAVS(); + /// @notice Thrown when the caller is not the allocation manager + error NotAllocationManager(); +} + +interface IAVSRegistrarEvents { + /// @notice Emitted when a new operator is registered + event OperatorRegistered(address indexed operator, uint32[] operatorSetIds); + + /// @notice Emitted when an operator is deregistered + event OperatorDeregistered(address indexed operator, uint32[] operatorSetIds); +} + +/// @notice Since we have already defined a public interface, we add the events and errors here +interface IAVSRegistrarInternal is IAVSRegistrarErrors, IAVSRegistrarEvents {} diff --git a/src/interfaces/IAllowlist.sol b/src/interfaces/IAllowlist.sol new file mode 100644 index 00000000..67fd00ee --- /dev/null +++ b/src/interfaces/IAllowlist.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +interface IAllowlistErrors { + /// @notice Thrown when the operator is already in the allowlist + error OperatorAlreadyInAllowlist(); + /// @notice Thrown when the operator is not in the allowlist + error OperatorNotInAllowlist(); +} + +interface IAllowlistEvents { + /// @notice Emitted when an operator is added to the allowlist + event OperatorAddedToAllowlist(address indexed operator); + /// @notice Emitted when an operator is removed from the allowlist + event OperatorRemovedFromAllowlist(address indexed operator); +} + +interface IAllowlist is IAllowlistErrors, IAllowlistEvents { + /** + * @notice Adds an operator to the allowlist + * @param operator The operator to add to the allowlist + * @dev Only callable by the owner + */ + function addOperatorToAllowlist( + address operator + ) external; + + /** + * @notice Removes an operator from the allowlist + * @param operator The operator to remove from the allowlist + * @dev If an operator is removed from the allowlist and is already registered, the avs + * must then handle state changes appropriately (ie. eject the operator) + * @dev Only callable by the owner + */ + function removeOperatorFromAllowlist( + address operator + ) external; + + /** + * @notice Checks if an operator is in the allowlist + * @param operator The operator to check + * @return True if the operator is in the allowlist, false otherwise + */ + function isOperatorAllowed( + address operator + ) external view returns (bool); + + /** + * @notice Returns all operators in the allowlist + * @return An array of all operators in the allowlist + * @dev This function should be used with caution, as it can be expensive to call on-chain + */ + function getAllowedOperators() external view returns (address[] memory); +} diff --git a/src/interfaces/IKeyRegistrar.sol b/src/interfaces/IKeyRegistrar.sol new file mode 100644 index 00000000..fe8befc2 --- /dev/null +++ b/src/interfaces/IKeyRegistrar.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; + +/// @notice A dummy interface for the KeyRegistrar +interface IKeyRegistrar { + enum CurveType { + ECDSA, + BN254 + } + + /// TODO: inherit from actual KeyRegistrar + function isRegistered( + address operator, + OperatorSet calldata operatorSet, + CurveType curveType + ) external view returns (bool); +} diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol new file mode 100644 index 00000000..a6c01ffa --- /dev/null +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import { + OperatorSetLib, + OperatorSet +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; + +import {AVSRegistrarStorage} from "./AVSRegistrarStorage.sol"; +import {IKeyRegistrar} from "../../interfaces/IKeyRegistrar.sol"; + +/// @notice A minimal AVSRegistrar contract that is used to register/deregister operators for an AVS +contract AVSRegistrar is AVSRegistrarStorage { + using OperatorSetLib for OperatorSet; + + modifier forThisAVS( + address avs + ) { + require(supportsAVS(avs), InvalidAVS()); + _; + } + + modifier onlyAllocationManager() { + require(msg.sender == address(allocationManager), NotAllocationManager()); + _; + } + + constructor( + address _avs, + IAllocationManager _allocationManager, + IKeyRegistrar _keyRegistrar, + IKeyRegistrar.CurveType _curveType + ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar, _curveType) {} + + /// @inheritdoc IAVSRegistrar + function registerOperator( + address operator, + address avs, + uint32[] calldata operatorSetIds, + bytes calldata data + ) external virtual forThisAVS(avs) onlyAllocationManager { + _beforeRegisterOperator(operator, operatorSetIds, data); + + // Check that the operator has a valid key + _validateOperatorKeys(operator, operatorSetIds); + + _afterRegisterOperator(operator, operatorSetIds, data); + + emit OperatorRegistered(operator, operatorSetIds); + } + + /// @inheritdoc IAVSRegistrar + function deregisterOperator( + address operator, + address avs, + uint32[] calldata operatorSetIds + ) external virtual forThisAVS(avs) onlyAllocationManager { + _beforeDeregisterOperator(operator, operatorSetIds); + + _afterDeregisterOperator(operator, operatorSetIds); + + emit OperatorDeregistered(operator, operatorSetIds); + } + + /// @inheritdoc IAVSRegistrar + function supportsAVS( + address _avs + ) public view virtual returns (bool) { + return _avs == avs; + } + + /* + * + * INTERNAL FUNCTIONS + * + */ + + /** + * @notice Validates that the operator has registered a key for the given operator sets + * @param operator The operator to validate + * @param operatorSetIds The operator sets to validate + * @dev This function assumes the operator has already registered a key in the Key Registrar + */ + function _validateOperatorKeys( + address operator, + uint32[] calldata operatorSetIds + ) internal view { + for (uint32 i = 0; i < operatorSetIds.length; i++) { + OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); + require( + keyRegistrar.isRegistered(operator, operatorSet, curveType), + KeyNotRegistered(operatorSetIds[i]) + ); + } + } + + /** + * @notice Hook called before the operator is registered + * @param operator The operator to register + * @param operatorSetIds The operator sets to register + * @param data The data to register + */ + function _beforeRegisterOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) internal virtual {} + + /** + * @notice Hook called after the operator is registered + * @param operator The operator to register + * @param operatorSetIds The operator sets to register + * @param data The data to register + */ + function _afterRegisterOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) internal virtual {} + + /** + * @notice Hook called before the operator is deregistered + * @param operator The operator to deregister + * @param operatorSetIds The operator sets to deregister + */ + function _beforeDeregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) internal virtual {} + + /** + * @notice Hook called after the operator is deregistered + * @param operator The operator to deregister + * @param operatorSetIds The operator sets to deregister + */ + function _afterDeregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) internal virtual {} +} diff --git a/src/middlewareV2/registrar/AVSRegistrarStorage.sol b/src/middlewareV2/registrar/AVSRegistrarStorage.sol new file mode 100644 index 00000000..1f899378 --- /dev/null +++ b/src/middlewareV2/registrar/AVSRegistrarStorage.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IKeyRegistrar} from "../../interfaces/IKeyRegistrar.sol"; +import {IAVSRegistrarInternal} from "../../interfaces/IAVSRegistrarInternal.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +/// @notice A minimal storage contract for the AVSRegistrar +abstract contract AVSRegistrarStorage is IAVSRegistrar, IAVSRegistrarInternal { + /** + * + * CONSTANTS AND IMMUTABLES + * + */ + + /// @notice The AVS that this registar is for + /// @dev In practice, the AVS address in EigenLayer core is + /// address that initialized the Metadata URI. + address public immutable avs; + + /// @notice The allocation manager in EigenLayer core + IAllocationManager public immutable allocationManager; + + /// @notice The curve type that the Key Registrar is using + IKeyRegistrar.CurveType public immutable curveType; + + /// @notice Pointer to the EigenLayer core Key Registrar + IKeyRegistrar public immutable keyRegistrar; + + constructor( + address _avs, + IAllocationManager _allocationManager, + IKeyRegistrar _keyRegistrar, + IKeyRegistrar.CurveType _curveType + ) { + avs = _avs; + allocationManager = _allocationManager; + keyRegistrar = _keyRegistrar; + curveType = _curveType; + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[50] private __GAP; +} diff --git a/src/middlewareV2/registrar/modules/Allowlist.sol b/src/middlewareV2/registrar/modules/Allowlist.sol new file mode 100644 index 00000000..ffc9de3b --- /dev/null +++ b/src/middlewareV2/registrar/modules/Allowlist.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllowlist} from "../../../interfaces/IAllowlist.sol"; +import {AllowlistStorage} from "./AllowlistStorage.sol"; + +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {OwnableUpgradeable} from + "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; +import {EnumerableSetUpgradeable} from + "openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol"; + +contract Allowlist is Initializable, OwnableUpgradeable, AllowlistStorage { + using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; + + constructor() { + _disableInitializers(); + } + + function initialize( + address _owner + ) external initializer { + __Ownable_init(); + _transferOwnership(_owner); + } + + /// @inheritdoc IAllowlist + function addOperatorToAllowlist( + address operator + ) external onlyOwner { + EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; + require(allowedOperators.add(operator), OperatorAlreadyInAllowlist()); + } + + /// @inheritdoc IAllowlist + function removeOperatorFromAllowlist( + address operator + ) external onlyOwner { + EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; + require(allowedOperators.remove(operator), OperatorNotInAllowlist()); + } + + /// @inheritdoc IAllowlist + function isOperatorAllowed( + address operator + ) public view returns (bool) { + EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; + return allowedOperators.contains(operator); + } + + /// @inheritdoc IAllowlist + function getAllowedOperators() external view returns (address[] memory) { + EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; + return allowedOperators.values(); + } +} diff --git a/src/middlewareV2/registrar/modules/AllowlistStorage.sol b/src/middlewareV2/registrar/modules/AllowlistStorage.sol new file mode 100644 index 00000000..6e697953 --- /dev/null +++ b/src/middlewareV2/registrar/modules/AllowlistStorage.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllowlist} from "../../../interfaces/IAllowlist.sol"; + +import {EnumerableSetUpgradeable} from + "openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol"; + +abstract contract AllowlistStorage is IAllowlist { + using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; + + /// @dev This data structure takes up 2 storage slots + EnumerableSetUpgradeable.AddressSet internal _allowedOperators; + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[48] private __GAP; +} diff --git a/src/middlewareV2/registrar/modules/SocketRegistry.sol b/src/middlewareV2/registrar/modules/SocketRegistry.sol new file mode 100644 index 00000000..262440f7 --- /dev/null +++ b/src/middlewareV2/registrar/modules/SocketRegistry.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {SocketRegistryStorage} from "./SocketRegistryStorage.sol"; +import {ISocketRegistry} from "../../../interfaces/ISocketRegistry.sol"; + +/// @notice A module that allows for the setting and removal of operator sockets +abstract contract SocketRegistry is SocketRegistryStorage { + /// @notice Emitted when an operator socket is set + event OperatorSocketSet(address indexed operator, string socket); + + /// @notice Emitted when an operator socket is removed + event OperatorSocketRemoved(address indexed operator); + + /** + * @notice Gets the socket for an operator. + * @param operator The operator to get the socket for. + * @return The socket for the operator. + */ + function getOperatorSocket( + address operator + ) external view returns (string memory) { + return operatorToSocket[operator]; + } + + /** + * @notice Updates the socket for the operator. + * @param socket The socket (any arbitrary string as deemed useful by an AVS) to set. + * @dev This function can only be called by the operator themselves. + */ + function updateSocket( + string memory socket + ) external { + _setOperatorSocket(msg.sender, socket); + } + + /** + * @notice Sets the socket for an operator. + * @param operator The address of the operator to set the socket for. + * @param socket The socket (any arbitrary string as deemed useful by an AVS) to set. + * @dev This function assumes a single socket per operator, for all operatorSets. + */ + function _setOperatorSocket(address operator, string memory socket) internal { + operatorToSocket[operator] = socket; + emit OperatorSocketSet(operator, socket); + } + + /** + * @notice Deletes the socket for an operator. + * @param operator The address of the operator to delete the socket for. + */ + function _removeOperatorSocket( + address operator + ) internal { + delete operatorToSocket[operator]; + emit OperatorSocketRemoved(operator); + } +} diff --git a/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol b/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol new file mode 100644 index 00000000..03875218 --- /dev/null +++ b/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {ISocketRegistry} from "../../../interfaces/ISocketRegistry.sol"; + +/** + * @title Storage variables for the `SocketRegistry` contract. + * @author Layr Labs, Inc. + */ +abstract contract SocketRegistryStorage { + /** + * + * STATE + * + */ + + /// @notice A mapping from operator addresses to their sockets + mapping(address => string) public operatorToSocket; + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[49] private __GAP; +} diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol new file mode 100644 index 00000000..e97593ad --- /dev/null +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; +import {AVSRegistrar} from "../AVSRegistrar.sol"; +import {Allowlist} from "../modules/Allowlist.sol"; + +contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { + constructor( + address _avs, + IAllocationManager _allocationManager, + IKeyRegistrar _keyRegistrar, + IKeyRegistrar.CurveType _curveType + ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) {} + + /// @notice Set the socket for the operator + function _beforeRegisterOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) internal override { + super._beforeRegisterOperator(operator, operatorSetIds, data); + + require(isOperatorAllowed(operator), "Operator not in allowlist"); + } +} diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol new file mode 100644 index 00000000..3ac276f7 --- /dev/null +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; + +import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; +import {AVSRegistrar} from "../AVSRegistrar.sol"; +import {SocketRegistry} from "../modules/SocketRegistry.sol"; + +/// @notice An AVSRegistrar that is the identifier for the AVS in EigenLayer core. +/// @dev Once deployed, the `admin` will control other parameters of the AVS, such as creating operatorSets, slashing, etc. +contract AVSRegistrarAsIdentifier is AVSRegistrar, SocketRegistry { + /// @notice The permission controller for the AVS + IPermissionController public immutable permissionController; + + constructor( + address _avs, + IAllocationManager _allocationManager, + IPermissionController _permissionController, + IKeyRegistrar _keyRegistrar, + IKeyRegistrar.CurveType _curveType, + string memory metadataURI, + address admin + ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) { + // Set the metadataURI and the registrar for the AVS to this registrar contract + allocationManager.updateAVSMetadataURI(address(this), metadataURI); + allocationManager.setAVSRegistrar(address(this), this); + + // Set the permission controller for future interactions + permissionController = _permissionController; + + // Set the admin for the AVS + permissionController.addPendingAdmin(address(this), admin); + } +} diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol new file mode 100644 index 00000000..56751f6f --- /dev/null +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; +import {AVSRegistrar} from "../AVSRegistrar.sol"; +import {SocketRegistry} from "../modules/SocketRegistry.sol"; + +contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { + constructor( + address _avs, + IAllocationManager _allocationManager, + IKeyRegistrar _keyRegistrar, + IKeyRegistrar.CurveType _curveType + ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) {} + + /// @notice Set the socket for the operator + function _afterRegisterOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) internal override { + super._afterRegisterOperator(operator, operatorSetIds, data); + + string memory socket = abi.decode(data, (string)); + _setOperatorSocket(operator, socket); + } + + /// @notice Remove the socket for the operator + function _afterDeregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) internal override { + super._afterDeregisterOperator(operator, operatorSetIds); + + _removeOperatorSocket(operator); + } +} From 14a752c6c53576f00c8c84d7aa7845ddf99dbe5d Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Tue, 20 May 2025 22:03:05 -0400 Subject: [PATCH 02/18] feat: add initializer --- src/middlewareV2/registrar/AVSRegistrar.sol | 19 ++++++++----------- .../registrar/AVSRegistrarStorage.sol | 3 +-- .../registrar/modules/Allowlist.sol | 15 +++++++++------ .../presets/AVSRegistrarAllowlist.sol | 6 ++++++ .../presets/AVSRegistrarAsIdentifier.sol | 16 +++++++++------- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index a6c01ffa..ea90125f 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -12,17 +12,12 @@ import { import {AVSRegistrarStorage} from "./AVSRegistrarStorage.sol"; import {IKeyRegistrar} from "../../interfaces/IKeyRegistrar.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; + /// @notice A minimal AVSRegistrar contract that is used to register/deregister operators for an AVS -contract AVSRegistrar is AVSRegistrarStorage { +contract AVSRegistrar is Initializable, AVSRegistrarStorage { using OperatorSetLib for OperatorSet; - modifier forThisAVS( - address avs - ) { - require(supportsAVS(avs), InvalidAVS()); - _; - } - modifier onlyAllocationManager() { require(msg.sender == address(allocationManager), NotAllocationManager()); _; @@ -33,7 +28,9 @@ contract AVSRegistrar is AVSRegistrarStorage { IAllocationManager _allocationManager, IKeyRegistrar _keyRegistrar, IKeyRegistrar.CurveType _curveType - ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar, _curveType) {} + ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar, _curveType) { + _disableInitializers(); + } /// @inheritdoc IAVSRegistrar function registerOperator( @@ -41,7 +38,7 @@ contract AVSRegistrar is AVSRegistrarStorage { address avs, uint32[] calldata operatorSetIds, bytes calldata data - ) external virtual forThisAVS(avs) onlyAllocationManager { + ) external virtual onlyAllocationManager { _beforeRegisterOperator(operator, operatorSetIds, data); // Check that the operator has a valid key @@ -57,7 +54,7 @@ contract AVSRegistrar is AVSRegistrarStorage { address operator, address avs, uint32[] calldata operatorSetIds - ) external virtual forThisAVS(avs) onlyAllocationManager { + ) external virtual onlyAllocationManager { _beforeDeregisterOperator(operator, operatorSetIds); _afterDeregisterOperator(operator, operatorSetIds); diff --git a/src/middlewareV2/registrar/AVSRegistrarStorage.sol b/src/middlewareV2/registrar/AVSRegistrarStorage.sol index 1f899378..0e0f097f 100644 --- a/src/middlewareV2/registrar/AVSRegistrarStorage.sol +++ b/src/middlewareV2/registrar/AVSRegistrarStorage.sol @@ -16,8 +16,7 @@ abstract contract AVSRegistrarStorage is IAVSRegistrar, IAVSRegistrarInternal { */ /// @notice The AVS that this registar is for - /// @dev In practice, the AVS address in EigenLayer core is - /// address that initialized the Metadata URI. + /// @dev In practice, the AVS address in EigenLayer core is address that initialized the Metadata URI. address public immutable avs; /// @notice The allocation manager in EigenLayer core diff --git a/src/middlewareV2/registrar/modules/Allowlist.sol b/src/middlewareV2/registrar/modules/Allowlist.sol index ffc9de3b..960a91bf 100644 --- a/src/middlewareV2/registrar/modules/Allowlist.sol +++ b/src/middlewareV2/registrar/modules/Allowlist.sol @@ -4,22 +4,25 @@ pragma solidity ^0.8.27; import {IAllowlist} from "../../../interfaces/IAllowlist.sol"; import {AllowlistStorage} from "./AllowlistStorage.sol"; -import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; import {EnumerableSetUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol"; -contract Allowlist is Initializable, OwnableUpgradeable, AllowlistStorage { +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; + +abstract contract Allowlist is OwnableUpgradeable, AllowlistStorage { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; - constructor() { - _disableInitializers(); + function initialize( + address _owner + ) public virtual initializer { + _initializeAllowlist(_owner); } - function initialize( + function _initializeAllowlist( address _owner - ) external initializer { + ) internal onlyInitializing { __Ownable_init(); _transferOwnership(_owner); } diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol index e97593ad..f955941d 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol @@ -16,6 +16,12 @@ contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { IKeyRegistrar.CurveType _curveType ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) {} + function initialize( + address admin + ) public override initializer { + _initializeAllowlist(admin); + } + /// @notice Set the socket for the operator function _beforeRegisterOperator( address operator, diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol index 3ac276f7..a7dbb4f4 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol @@ -10,9 +10,11 @@ import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; import {AVSRegistrar} from "../AVSRegistrar.sol"; import {SocketRegistry} from "../modules/SocketRegistry.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; + /// @notice An AVSRegistrar that is the identifier for the AVS in EigenLayer core. /// @dev Once deployed, the `admin` will control other parameters of the AVS, such as creating operatorSets, slashing, etc. -contract AVSRegistrarAsIdentifier is AVSRegistrar, SocketRegistry { +contract AVSRegistrarAsIdentifier is Initializable, AVSRegistrar, SocketRegistry { /// @notice The permission controller for the AVS IPermissionController public immutable permissionController; @@ -21,17 +23,17 @@ contract AVSRegistrarAsIdentifier is AVSRegistrar, SocketRegistry { IAllocationManager _allocationManager, IPermissionController _permissionController, IKeyRegistrar _keyRegistrar, - IKeyRegistrar.CurveType _curveType, - string memory metadataURI, - address admin + IKeyRegistrar.CurveType _curveType ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) { + // Set the permission controller for future interactions + permissionController = _permissionController; + } + + function initialize(address admin, string memory metadataURI) public initializer { // Set the metadataURI and the registrar for the AVS to this registrar contract allocationManager.updateAVSMetadataURI(address(this), metadataURI); allocationManager.setAVSRegistrar(address(this), this); - // Set the permission controller for future interactions - permissionController = _permissionController; - // Set the admin for the AVS permissionController.addPendingAdmin(address(this), admin); } From 25d058c333ad9b82dd58656ae6583cee40befe7c Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 16:51:49 -0400 Subject: [PATCH 03/18] chore: update submodule --- lib/eigenlayer-contracts | 2 +- test/unit/middlewareV2/AVSRegistrar.t.sol | 0 test/unit/middlewareV2/MockDeployer.sol | 41 +++ test/utils/Random.sol | 344 ++++++++++++++++++++++ 4 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 test/unit/middlewareV2/AVSRegistrar.t.sol create mode 100644 test/unit/middlewareV2/MockDeployer.sol create mode 100644 test/utils/Random.sol diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 9a9707b2..97d61830 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 9a9707b28fa6acbd512d8983ad7a71d7ecb222ad +Subproject commit 97d61830d34b55ae39a650cf15b72896dd75a675 diff --git a/test/unit/middlewareV2/AVSRegistrar.t.sol b/test/unit/middlewareV2/AVSRegistrar.t.sol new file mode 100644 index 00000000..e69de29b diff --git a/test/unit/middlewareV2/MockDeployer.sol b/test/unit/middlewareV2/MockDeployer.sol new file mode 100644 index 00000000..0c6a288e --- /dev/null +++ b/test/unit/middlewareV2/MockDeployer.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ITransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {AllocationManagerMock} from "eigenlayer-contracts/src/test/mocks/AllocationManagerMock.sol"; +import {Randomness, Random} from "../../utils/Random.sol"; + +import "forge-std/Test.sol"; + +abstract contract MockEigenLayerDeployer is Test { + /// @dev addresses that should be excluded from fuzzing + mapping(address => bool) public isExcludedFuzzAddress; + + modifier filterFuzzedAddressInputs(address addr) { + cheats.assume(!isExcludedFuzzAddress[addr]); + _; + } + + /// @dev set the random seed for the current test + modifier rand(Randomness r) { + r.set(); + _; + } + + function random() internal returns (Randomness) { + return Randomness.wrap(Random.SEED).shuffle(); + } + + Vm cheats = Vm(VM_ADDRESS); + + // State Variables + AllocationManagerMock public allocationManagerMock; + + function _deployMockEigenLayer() internal { + allocationManagerMock = new AllocationManagerMock(); + } +} diff --git a/test/utils/Random.sol b/test/utils/Random.sol new file mode 100644 index 00000000..27131b3a --- /dev/null +++ b/test/utils/Random.sol @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "src/contracts/interfaces/IAllocationManager.sol"; +import "src/contracts/interfaces/IStrategy.sol"; +import "src/contracts/libraries/OperatorSetLib.sol"; + +type Randomness is uint; + +using Random for Randomness global; + +library Random { + /// ----------------------------------------------------------------------- + /// Constants + /// ----------------------------------------------------------------------- + + /// @dev Equivalent to: `uint256(keccak256("RANDOMNESS.SEED"))`. + uint constant SEED = 0x93bfe7cafd9427243dc4fe8c6e706851eb6696ba8e48960dd74ecc96544938ce; + + /// @dev Equivalent to: `uint256(keccak256("RANDOMNESS.SLOT"))`. + uint constant SLOT = 0xd0660badbab446a974e6a19901c78a2ad88d7e4f1710b85e1cfc0878477344fd; + + /// ----------------------------------------------------------------------- + /// Helpers + /// ----------------------------------------------------------------------- + + function set(Randomness r) internal returns (Randomness) { + /// @solidity memory-safe-assembly + assembly { + sstore(SLOT, r) + } + return r; + } + + function shuffle(Randomness r) internal returns (Randomness) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, sload(SLOT)) + mstore(0x20, r) + r := keccak256(0x00, 0x20) + } + return r.set(); + } + + /// ----------------------------------------------------------------------- + /// Native Types + /// ----------------------------------------------------------------------- + + function Int256(Randomness r, int min, int max) internal returns (int) { + return max <= min ? min : r.Int256() % (max - min) + min; + } + + function Int256(Randomness r) internal returns (int) { + return r.unwrap() % 2 == 0 ? int(r.Uint256()) : -int(r.Uint256()); + } + + function Int128(Randomness r, int128 min, int128 max) internal returns (int128) { + return int128(Int256(r, min, max)); + } + + function Int128(Randomness r) internal returns (int128) { + return int128(Int256(r)); + } + + function Int64(Randomness r, int64 min, int64 max) internal returns (int64) { + return int64(Int256(r, min, max)); + } + + function Int64(Randomness r) internal returns (int64) { + return int64(Int256(r)); + } + + function Int32(Randomness r, int32 min, int32 max) internal returns (int32) { + return int32(Int256(r, min, max)); + } + + function Uint256(Randomness r, uint min, uint max) internal returns (uint) { + return max <= min ? min : r.Uint256() % (max - min) + min; + } + + function Uint256(Randomness r) internal returns (uint) { + return r.shuffle().unwrap(); + } + + function Uint128(Randomness r, uint128 min, uint128 max) internal returns (uint128) { + return uint128(Uint256(r, min, max)); + } + + function Uint128(Randomness r) internal returns (uint128) { + return uint128(Uint256(r)); + } + + function Uint64(Randomness r, uint64 min, uint64 max) internal returns (uint64) { + return uint64(Uint256(r, min, max)); + } + + function Uint64(Randomness r) internal returns (uint64) { + return uint64(Uint256(r)); + } + + function Uint32(Randomness r, uint32 min, uint32 max) internal returns (uint32) { + return uint32(Uint256(r, min, max)); + } + + function Uint32(Randomness r) internal returns (uint32) { + return uint32(Uint256(r)); + } + + function Bytes32(Randomness r) internal returns (bytes32) { + return bytes32(r.Uint256()); + } + + function Address(Randomness r) internal returns (address) { + return address(uint160(r.Uint256(1, type(uint160).max))); + } + + function Boolean(Randomness r) internal returns (bool) { + return r.Uint256() % 2 == 0; + } + + /// ----------------------------------------------------------------------- + /// Arrays + /// ----------------------------------------------------------------------- + + function Int256Array(Randomness r, uint len, int min, int max) internal returns (int[] memory arr) { + arr = new int[](len); + for (uint i; i < len; ++i) { + arr[i] = r.Int256(min, max); + } + } + + function Int128Array(Randomness r, uint len, int128 min, int128 max) internal returns (int128[] memory arr) { + arr = new int128[](len); + for (uint i; i < len; ++i) { + arr[i] = r.Int128(min, max); + } + } + + function Int64Array(Randomness r, uint len, int64 min, int64 max) internal returns (int64[] memory arr) { + arr = new int64[](len); + for (uint i; i < len; ++i) { + arr[i] = r.Int64(min, max); + } + } + + function Int32Array(Randomness r, uint len, int32 min, int32 max) internal returns (int32[] memory arr) { + arr = new int32[](len); + for (uint i; i < len; ++i) { + arr[i] = r.Int32(min, max); + } + } + + function Uint256Array(Randomness r, uint len, uint min, uint max) internal returns (uint[] memory arr) { + arr = new uint[](len); + for (uint i; i < len; ++i) { + arr[i] = uint(r.Uint256(min, max)); + } + } + + /// ----------------------------------------------------------------------- + /// General Types + /// ----------------------------------------------------------------------- + + function StakerArray(Randomness r, uint len) internal returns (address[] memory stakers) { + stakers = new address[](len); + for (uint i; i < len; ++i) { + stakers[i] = r.Address(); + } + } + + function StrategyArray(Randomness r, uint len) internal returns (IStrategy[] memory strategies) { + strategies = new IStrategy[](len); + for (uint i; i < len; ++i) { + strategies[i] = IStrategy(r.Address()); + } + } + + function OperatorSetArray(Randomness r, address avs, uint len) internal returns (OperatorSet[] memory operatorSets) { + operatorSets = new OperatorSet[](len); + for (uint i; i < len; ++i) { + operatorSets[i] = OperatorSet(avs, r.Uint32()); + } + } + + /// ----------------------------------------------------------------------- + /// `AllocationManager` Types + /// ----------------------------------------------------------------------- + + /// @dev Usage: `r.createSetParams(r, numOpSets, numStrats)`. + function CreateSetParams(Randomness r, uint numOpSets, uint numStrats) + internal + returns (IAllocationManagerTypes.CreateSetParams[] memory params) + { + params = new IAllocationManagerTypes.CreateSetParams[](numOpSets); + for (uint i; i < numOpSets; ++i) { + params[i].operatorSetId = r.Uint32(1, type(uint32).max); + params[i].strategies = r.StrategyArray(numStrats); + } + } + + /// @dev Usage: + /// ``` + /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); + /// cheats.prank(avs); + /// allocationManager.createOperatorSets(r.createSetParams(allocateParams)); + /// ``` + function CreateSetParams(Randomness, IAllocationManagerTypes.AllocateParams[] memory allocateParams) + internal + pure + returns (IAllocationManagerTypes.CreateSetParams[] memory params) + { + params = new IAllocationManagerTypes.CreateSetParams[](allocateParams.length); + for (uint i; i < allocateParams.length; ++i) { + params[i] = IAllocationManagerTypes.CreateSetParams(allocateParams[i].operatorSet.id, allocateParams[i].strategies); + } + } + + /// @dev Usage: + /// ``` + /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); + /// CreateSetParams[] memory createSetParams = r.createSetParams(allocateParams); + /// + /// cheats.prank(avs); + /// allocationManager.createOperatorSets(createSetParams); + /// + /// cheats.prank(operator); + /// allocationManager.modifyAllocations(allocateParams); + /// ``` + function AllocateParams(Randomness r, address avs, uint numAllocations, uint numStrats) + internal + returns (IAllocationManagerTypes.AllocateParams[] memory allocateParams) + { + allocateParams = new IAllocationManagerTypes.AllocateParams[](numAllocations); + + // TODO: Randomize magnitudes such that they sum to 1e18 (100%). + uint64 magnitudePerSet = uint64(WAD / numStrats); + + for (uint i; i < numAllocations; ++i) { + allocateParams[i].operatorSet = OperatorSet(avs, r.Uint32()); + allocateParams[i].strategies = r.StrategyArray(numStrats); + allocateParams[i].newMagnitudes = new uint64[](numStrats); + + for (uint j; j < numStrats; ++j) { + allocateParams[i].newMagnitudes[j] = magnitudePerSet; + } + } + } + + /// @dev Usage: + /// ``` + /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); + /// AllocateParams[] memory deallocateParams = r.deallocateParams(allocateParams); + /// CreateSetParams[] memory createSetParams = r.createSetParams(allocateParams); + /// + /// cheats.prank(avs); + /// allocationManager.createOperatorSets(createSetParams); + /// + /// cheats.prank(operator); + /// allocationManager.modifyAllocations(allocateParams); + /// + /// cheats.prank(operator) + /// allocationManager.modifyAllocations(deallocateParams); + /// ``` + function DeallocateParams(Randomness r, IAllocationManagerTypes.AllocateParams[] memory allocateParams) + internal + returns (IAllocationManagerTypes.AllocateParams[] memory deallocateParams) + { + uint numDeallocations = allocateParams.length; + + deallocateParams = new IAllocationManagerTypes.AllocateParams[](numDeallocations); + + for (uint i; i < numDeallocations; ++i) { + deallocateParams[i].operatorSet = allocateParams[i].operatorSet; + deallocateParams[i].strategies = allocateParams[i].strategies; + + deallocateParams[i].newMagnitudes = new uint64[](allocateParams[i].strategies.length); + for (uint j; j < allocateParams[i].strategies.length; ++j) { + deallocateParams[i].newMagnitudes[j] = r.Uint64(0, allocateParams[i].newMagnitudes[j] - 1); + } + } + } + + /// @dev Usage: + /// ``` + /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); + /// CreateSetParams[] memory createSetParams = r.createSetParams(allocateParams); + /// RegisterParams memory registerParams = r.registerParams(allocateParams); + /// + /// cheats.prank(avs); + /// allocationManager.createOperatorSets(createSetParams); + /// + /// cheats.prank(operator); + /// allocationmanager.registerForOperatorSets(registerParams); + /// ``` + function RegisterParams(Randomness r, IAllocationManagerTypes.AllocateParams[] memory allocateParams) + internal + returns (IAllocationManagerTypes.RegisterParams memory params) + { + params.avs = allocateParams[0].operatorSet.avs; + params.operatorSetIds = new uint32[](allocateParams.length); + for (uint i; i < allocateParams.length; ++i) { + params.operatorSetIds[i] = allocateParams[i].operatorSet.id; + } + params.data = abi.encode(r.Bytes32()); + } + + function SlashingParams(Randomness r, address operator, IAllocationManagerTypes.AllocateParams memory allocateParams) + internal + returns (IAllocationManagerTypes.SlashingParams memory params) + { + IStrategy[] memory strategies = allocateParams.strategies; + + params.operator = operator; + params.operatorSetId = allocateParams.operatorSet.id; + + // Randomly select a subset of strategies to slash. + uint len = r.Uint256({min: 1, max: strategies.length}); + + // Update length of strategies array. + assembly { + mstore(strategies, len) + } + + params.strategies = strategies; + params.wadsToSlash = new uint[](len); + + // Randomly select a `wadToSlash` for each strategy. + for (uint i; i < len; ++i) { + params.wadsToSlash[i] = r.Uint256({min: 0.001 ether, max: 1 ether}); + } + } + + /// ----------------------------------------------------------------------- + /// Helpers + /// ----------------------------------------------------------------------- + + function wrap(uint r) internal pure returns (Randomness) { + return Randomness.wrap(r); + } + + function unwrap(Randomness r) internal pure returns (uint) { + return Randomness.unwrap(r); + } +} From 3b9b7cccb1c7bdda03a7fe53aedc92500b6a1be6 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 18:08:13 -0400 Subject: [PATCH 04/18] chore: update for new key registrar interface --- src/interfaces/IKeyRegistrar.sol | 14 +- src/middlewareV2/registrar/AVSRegistrar.sol | 27 +- .../presets/AVSRegistrarAllowlist.sol | 5 +- .../presets/AVSRegistrarAsIdentifier.sol | 5 +- .../presets/AVSRegistrarWithSocket.sol | 5 +- test/mocks/KeyRegistrarMock.sol | 42 +++ test/unit/middlewareV2/MockDeployer.sol | 3 +- test/utils/Random.sol | 344 ------------------ 8 files changed, 83 insertions(+), 362 deletions(-) create mode 100644 test/mocks/KeyRegistrarMock.sol delete mode 100644 test/utils/Random.sol diff --git a/src/interfaces/IKeyRegistrar.sol b/src/interfaces/IKeyRegistrar.sol index fe8befc2..c0dc88d4 100644 --- a/src/interfaces/IKeyRegistrar.sol +++ b/src/interfaces/IKeyRegistrar.sol @@ -10,10 +10,18 @@ interface IKeyRegistrar { BN254 } - /// TODO: inherit from actual KeyRegistrar + function checkAndUpdateKey( + OperatorSet calldata operatorSet, + address operator + ) external returns (bool); + + function removeKey( + OperatorSet calldata operatorSet, + address operator + ) external; + function isRegistered( - address operator, OperatorSet calldata operatorSet, - CurveType curveType + address operator ) external view returns (bool); } diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index ea90125f..316353bd 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -26,9 +26,8 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { constructor( address _avs, IAllocationManager _allocationManager, - IKeyRegistrar _keyRegistrar, - IKeyRegistrar.CurveType _curveType - ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar, _curveType) { + IKeyRegistrar _keyRegistrar + ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar, IKeyRegistrar.CurveType.BN254) { _disableInitializers(); } @@ -41,7 +40,7 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { ) external virtual onlyAllocationManager { _beforeRegisterOperator(operator, operatorSetIds, data); - // Check that the operator has a valid key + // Check that the operator has a valid key and update key if needed _validateOperatorKeys(operator, operatorSetIds); _afterRegisterOperator(operator, operatorSetIds, data); @@ -57,6 +56,9 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { ) external virtual onlyAllocationManager { _beforeDeregisterOperator(operator, operatorSetIds); + // Remove operator keys from the key registrar + _removeOperatorKeys(operator, operatorSetIds); + _afterDeregisterOperator(operator, operatorSetIds); emit OperatorDeregistered(operator, operatorSetIds); @@ -88,12 +90,27 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { for (uint32 i = 0; i < operatorSetIds.length; i++) { OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); require( - keyRegistrar.isRegistered(operator, operatorSet, curveType), + keyRegistrar.checkAndUpdateKey(operatorSet, operator), KeyNotRegistered(operatorSetIds[i]) ); } } + /** + * @notice Removes the operator keys from the key registrar + * @param operator The operator to remove + * @param operatorSetIds The operator sets to remove + */ + function _removeOperatorKeys( + address operator, + uint32[] calldata operatorSetIds + ) internal { + for (uint32 i = 0; i < operatorSetIds.length; i++) { + OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); + keyRegistrar.removeKey(operatorSet, operator); + } + } + /** * @notice Hook called before the operator is registered * @param operator The operator to register diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol index f955941d..a64c6bcf 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol @@ -12,9 +12,8 @@ contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { constructor( address _avs, IAllocationManager _allocationManager, - IKeyRegistrar _keyRegistrar, - IKeyRegistrar.CurveType _curveType - ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) {} + IKeyRegistrar _keyRegistrar + ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar) {} function initialize( address admin diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol index a7dbb4f4..2f8c7691 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol @@ -22,9 +22,8 @@ contract AVSRegistrarAsIdentifier is Initializable, AVSRegistrar, SocketRegistry address _avs, IAllocationManager _allocationManager, IPermissionController _permissionController, - IKeyRegistrar _keyRegistrar, - IKeyRegistrar.CurveType _curveType - ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) { + IKeyRegistrar _keyRegistrar + ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar) { // Set the permission controller for future interactions permissionController = _permissionController; } diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol index 56751f6f..a617a1be 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol @@ -12,9 +12,8 @@ contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { constructor( address _avs, IAllocationManager _allocationManager, - IKeyRegistrar _keyRegistrar, - IKeyRegistrar.CurveType _curveType - ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar, _curveType) {} + IKeyRegistrar _keyRegistrar + ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar) {} /// @notice Set the socket for the operator function _afterRegisterOperator( diff --git a/test/mocks/KeyRegistrarMock.sol b/test/mocks/KeyRegistrarMock.sol new file mode 100644 index 00000000..f126c49e --- /dev/null +++ b/test/mocks/KeyRegistrarMock.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {OperatorSet, OperatorSetLib} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; + + +contract KeyRegistrarMock is IKeyRegistrar { + using OperatorSetLib for OperatorSet; + + mapping(bytes32 operatorSetKey => mapping(address => bool)) internal _operatorRegistered; + + function setIsRegistered( + address operator, + OperatorSet calldata operatorSet, + bool _isRegistered + ) external { + bytes32 operatorSetKey = operatorSet.key(); + _operatorRegistered[operatorSetKey][operator] = _isRegistered; + } + + function isRegistered( + OperatorSet calldata operatorSet, + address operator + ) external view returns (bool) { + return _operatorRegistered[operatorSet.key()][operator]; + } + + function checkAndUpdateKey( + OperatorSet calldata operatorSet, + address operator + ) external view returns (bool) { + return _operatorRegistered[operatorSet.key()][operator]; + } + + function removeKey( + OperatorSet calldata operatorSet, + address operator + ) external { + _operatorRegistered[operatorSet.key()][operator] = false; + } +} \ No newline at end of file diff --git a/test/unit/middlewareV2/MockDeployer.sol b/test/unit/middlewareV2/MockDeployer.sol index 0c6a288e..ac67c547 100644 --- a/test/unit/middlewareV2/MockDeployer.sol +++ b/test/unit/middlewareV2/MockDeployer.sol @@ -7,7 +7,8 @@ import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {AllocationManagerMock} from "eigenlayer-contracts/src/test/mocks/AllocationManagerMock.sol"; -import {Randomness, Random} from "../../utils/Random.sol"; +import {KeyRegistrarMock} from "../../mocks/KeyRegistrarMock.sol"; +import {Randomness, Random} from "eigenlayer-contracts/src/test/utils/Random.sol"; import "forge-std/Test.sol"; diff --git a/test/utils/Random.sol b/test/utils/Random.sol deleted file mode 100644 index 27131b3a..00000000 --- a/test/utils/Random.sol +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.27; - -import "src/contracts/interfaces/IAllocationManager.sol"; -import "src/contracts/interfaces/IStrategy.sol"; -import "src/contracts/libraries/OperatorSetLib.sol"; - -type Randomness is uint; - -using Random for Randomness global; - -library Random { - /// ----------------------------------------------------------------------- - /// Constants - /// ----------------------------------------------------------------------- - - /// @dev Equivalent to: `uint256(keccak256("RANDOMNESS.SEED"))`. - uint constant SEED = 0x93bfe7cafd9427243dc4fe8c6e706851eb6696ba8e48960dd74ecc96544938ce; - - /// @dev Equivalent to: `uint256(keccak256("RANDOMNESS.SLOT"))`. - uint constant SLOT = 0xd0660badbab446a974e6a19901c78a2ad88d7e4f1710b85e1cfc0878477344fd; - - /// ----------------------------------------------------------------------- - /// Helpers - /// ----------------------------------------------------------------------- - - function set(Randomness r) internal returns (Randomness) { - /// @solidity memory-safe-assembly - assembly { - sstore(SLOT, r) - } - return r; - } - - function shuffle(Randomness r) internal returns (Randomness) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, sload(SLOT)) - mstore(0x20, r) - r := keccak256(0x00, 0x20) - } - return r.set(); - } - - /// ----------------------------------------------------------------------- - /// Native Types - /// ----------------------------------------------------------------------- - - function Int256(Randomness r, int min, int max) internal returns (int) { - return max <= min ? min : r.Int256() % (max - min) + min; - } - - function Int256(Randomness r) internal returns (int) { - return r.unwrap() % 2 == 0 ? int(r.Uint256()) : -int(r.Uint256()); - } - - function Int128(Randomness r, int128 min, int128 max) internal returns (int128) { - return int128(Int256(r, min, max)); - } - - function Int128(Randomness r) internal returns (int128) { - return int128(Int256(r)); - } - - function Int64(Randomness r, int64 min, int64 max) internal returns (int64) { - return int64(Int256(r, min, max)); - } - - function Int64(Randomness r) internal returns (int64) { - return int64(Int256(r)); - } - - function Int32(Randomness r, int32 min, int32 max) internal returns (int32) { - return int32(Int256(r, min, max)); - } - - function Uint256(Randomness r, uint min, uint max) internal returns (uint) { - return max <= min ? min : r.Uint256() % (max - min) + min; - } - - function Uint256(Randomness r) internal returns (uint) { - return r.shuffle().unwrap(); - } - - function Uint128(Randomness r, uint128 min, uint128 max) internal returns (uint128) { - return uint128(Uint256(r, min, max)); - } - - function Uint128(Randomness r) internal returns (uint128) { - return uint128(Uint256(r)); - } - - function Uint64(Randomness r, uint64 min, uint64 max) internal returns (uint64) { - return uint64(Uint256(r, min, max)); - } - - function Uint64(Randomness r) internal returns (uint64) { - return uint64(Uint256(r)); - } - - function Uint32(Randomness r, uint32 min, uint32 max) internal returns (uint32) { - return uint32(Uint256(r, min, max)); - } - - function Uint32(Randomness r) internal returns (uint32) { - return uint32(Uint256(r)); - } - - function Bytes32(Randomness r) internal returns (bytes32) { - return bytes32(r.Uint256()); - } - - function Address(Randomness r) internal returns (address) { - return address(uint160(r.Uint256(1, type(uint160).max))); - } - - function Boolean(Randomness r) internal returns (bool) { - return r.Uint256() % 2 == 0; - } - - /// ----------------------------------------------------------------------- - /// Arrays - /// ----------------------------------------------------------------------- - - function Int256Array(Randomness r, uint len, int min, int max) internal returns (int[] memory arr) { - arr = new int[](len); - for (uint i; i < len; ++i) { - arr[i] = r.Int256(min, max); - } - } - - function Int128Array(Randomness r, uint len, int128 min, int128 max) internal returns (int128[] memory arr) { - arr = new int128[](len); - for (uint i; i < len; ++i) { - arr[i] = r.Int128(min, max); - } - } - - function Int64Array(Randomness r, uint len, int64 min, int64 max) internal returns (int64[] memory arr) { - arr = new int64[](len); - for (uint i; i < len; ++i) { - arr[i] = r.Int64(min, max); - } - } - - function Int32Array(Randomness r, uint len, int32 min, int32 max) internal returns (int32[] memory arr) { - arr = new int32[](len); - for (uint i; i < len; ++i) { - arr[i] = r.Int32(min, max); - } - } - - function Uint256Array(Randomness r, uint len, uint min, uint max) internal returns (uint[] memory arr) { - arr = new uint[](len); - for (uint i; i < len; ++i) { - arr[i] = uint(r.Uint256(min, max)); - } - } - - /// ----------------------------------------------------------------------- - /// General Types - /// ----------------------------------------------------------------------- - - function StakerArray(Randomness r, uint len) internal returns (address[] memory stakers) { - stakers = new address[](len); - for (uint i; i < len; ++i) { - stakers[i] = r.Address(); - } - } - - function StrategyArray(Randomness r, uint len) internal returns (IStrategy[] memory strategies) { - strategies = new IStrategy[](len); - for (uint i; i < len; ++i) { - strategies[i] = IStrategy(r.Address()); - } - } - - function OperatorSetArray(Randomness r, address avs, uint len) internal returns (OperatorSet[] memory operatorSets) { - operatorSets = new OperatorSet[](len); - for (uint i; i < len; ++i) { - operatorSets[i] = OperatorSet(avs, r.Uint32()); - } - } - - /// ----------------------------------------------------------------------- - /// `AllocationManager` Types - /// ----------------------------------------------------------------------- - - /// @dev Usage: `r.createSetParams(r, numOpSets, numStrats)`. - function CreateSetParams(Randomness r, uint numOpSets, uint numStrats) - internal - returns (IAllocationManagerTypes.CreateSetParams[] memory params) - { - params = new IAllocationManagerTypes.CreateSetParams[](numOpSets); - for (uint i; i < numOpSets; ++i) { - params[i].operatorSetId = r.Uint32(1, type(uint32).max); - params[i].strategies = r.StrategyArray(numStrats); - } - } - - /// @dev Usage: - /// ``` - /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); - /// cheats.prank(avs); - /// allocationManager.createOperatorSets(r.createSetParams(allocateParams)); - /// ``` - function CreateSetParams(Randomness, IAllocationManagerTypes.AllocateParams[] memory allocateParams) - internal - pure - returns (IAllocationManagerTypes.CreateSetParams[] memory params) - { - params = new IAllocationManagerTypes.CreateSetParams[](allocateParams.length); - for (uint i; i < allocateParams.length; ++i) { - params[i] = IAllocationManagerTypes.CreateSetParams(allocateParams[i].operatorSet.id, allocateParams[i].strategies); - } - } - - /// @dev Usage: - /// ``` - /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); - /// CreateSetParams[] memory createSetParams = r.createSetParams(allocateParams); - /// - /// cheats.prank(avs); - /// allocationManager.createOperatorSets(createSetParams); - /// - /// cheats.prank(operator); - /// allocationManager.modifyAllocations(allocateParams); - /// ``` - function AllocateParams(Randomness r, address avs, uint numAllocations, uint numStrats) - internal - returns (IAllocationManagerTypes.AllocateParams[] memory allocateParams) - { - allocateParams = new IAllocationManagerTypes.AllocateParams[](numAllocations); - - // TODO: Randomize magnitudes such that they sum to 1e18 (100%). - uint64 magnitudePerSet = uint64(WAD / numStrats); - - for (uint i; i < numAllocations; ++i) { - allocateParams[i].operatorSet = OperatorSet(avs, r.Uint32()); - allocateParams[i].strategies = r.StrategyArray(numStrats); - allocateParams[i].newMagnitudes = new uint64[](numStrats); - - for (uint j; j < numStrats; ++j) { - allocateParams[i].newMagnitudes[j] = magnitudePerSet; - } - } - } - - /// @dev Usage: - /// ``` - /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); - /// AllocateParams[] memory deallocateParams = r.deallocateParams(allocateParams); - /// CreateSetParams[] memory createSetParams = r.createSetParams(allocateParams); - /// - /// cheats.prank(avs); - /// allocationManager.createOperatorSets(createSetParams); - /// - /// cheats.prank(operator); - /// allocationManager.modifyAllocations(allocateParams); - /// - /// cheats.prank(operator) - /// allocationManager.modifyAllocations(deallocateParams); - /// ``` - function DeallocateParams(Randomness r, IAllocationManagerTypes.AllocateParams[] memory allocateParams) - internal - returns (IAllocationManagerTypes.AllocateParams[] memory deallocateParams) - { - uint numDeallocations = allocateParams.length; - - deallocateParams = new IAllocationManagerTypes.AllocateParams[](numDeallocations); - - for (uint i; i < numDeallocations; ++i) { - deallocateParams[i].operatorSet = allocateParams[i].operatorSet; - deallocateParams[i].strategies = allocateParams[i].strategies; - - deallocateParams[i].newMagnitudes = new uint64[](allocateParams[i].strategies.length); - for (uint j; j < allocateParams[i].strategies.length; ++j) { - deallocateParams[i].newMagnitudes[j] = r.Uint64(0, allocateParams[i].newMagnitudes[j] - 1); - } - } - } - - /// @dev Usage: - /// ``` - /// AllocateParams[] memory allocateParams = r.allocateParams(avs, numAllocations, numStrats); - /// CreateSetParams[] memory createSetParams = r.createSetParams(allocateParams); - /// RegisterParams memory registerParams = r.registerParams(allocateParams); - /// - /// cheats.prank(avs); - /// allocationManager.createOperatorSets(createSetParams); - /// - /// cheats.prank(operator); - /// allocationmanager.registerForOperatorSets(registerParams); - /// ``` - function RegisterParams(Randomness r, IAllocationManagerTypes.AllocateParams[] memory allocateParams) - internal - returns (IAllocationManagerTypes.RegisterParams memory params) - { - params.avs = allocateParams[0].operatorSet.avs; - params.operatorSetIds = new uint32[](allocateParams.length); - for (uint i; i < allocateParams.length; ++i) { - params.operatorSetIds[i] = allocateParams[i].operatorSet.id; - } - params.data = abi.encode(r.Bytes32()); - } - - function SlashingParams(Randomness r, address operator, IAllocationManagerTypes.AllocateParams memory allocateParams) - internal - returns (IAllocationManagerTypes.SlashingParams memory params) - { - IStrategy[] memory strategies = allocateParams.strategies; - - params.operator = operator; - params.operatorSetId = allocateParams.operatorSet.id; - - // Randomly select a subset of strategies to slash. - uint len = r.Uint256({min: 1, max: strategies.length}); - - // Update length of strategies array. - assembly { - mstore(strategies, len) - } - - params.strategies = strategies; - params.wadsToSlash = new uint[](len); - - // Randomly select a `wadToSlash` for each strategy. - for (uint i; i < len; ++i) { - params.wadsToSlash[i] = r.Uint256({min: 0.001 ether, max: 1 ether}); - } - } - - /// ----------------------------------------------------------------------- - /// Helpers - /// ----------------------------------------------------------------------- - - function wrap(uint r) internal pure returns (Randomness) { - return Randomness.wrap(r); - } - - function unwrap(Randomness r) internal pure returns (uint) { - return Randomness.unwrap(r); - } -} From 24a6eb379a3001df5496ab8508c307c126190cc1 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 19:14:05 -0400 Subject: [PATCH 05/18] test: add basic avs registrar tests --- src/interfaces/IAVSRegistrarInternal.sol | 4 +- src/interfaces/IKeyRegistrar.sol | 7 +- src/middlewareV2/registrar/AVSRegistrar.sol | 17 +- test/mocks/KeyRegistrarMock.sol | 11 +- test/unit/middlewareV2/AVSRegistrar.t.sol | 0 test/unit/middlewareV2/AVSRegistrarUnit.t.sol | 120 +++++++++ test/unit/middlewareV2/MockDeployer.sol | 28 ++- test/utils/Random.sol | 232 ++++++++++++++++++ 8 files changed, 380 insertions(+), 39 deletions(-) delete mode 100644 test/unit/middlewareV2/AVSRegistrar.t.sol create mode 100644 test/unit/middlewareV2/AVSRegistrarUnit.t.sol create mode 100644 test/utils/Random.sol diff --git a/src/interfaces/IAVSRegistrarInternal.sol b/src/interfaces/IAVSRegistrarInternal.sol index 9690ef5b..f3e2cf6f 100644 --- a/src/interfaces/IAVSRegistrarInternal.sol +++ b/src/interfaces/IAVSRegistrarInternal.sol @@ -3,9 +3,7 @@ pragma solidity ^0.8.27; interface IAVSRegistrarErrors { /// @notice Thrown when a key is not registered - error KeyNotRegistered(uint32 operatorSetId); - /// @notice Thrown when the AVSRegistrar does not support the AVS - error InvalidAVS(); + error KeyNotRegistered(); /// @notice Thrown when the caller is not the allocation manager error NotAllocationManager(); } diff --git a/src/interfaces/IKeyRegistrar.sol b/src/interfaces/IKeyRegistrar.sol index c0dc88d4..cb496e67 100644 --- a/src/interfaces/IKeyRegistrar.sol +++ b/src/interfaces/IKeyRegistrar.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; /// @notice A dummy interface for the KeyRegistrar interface IKeyRegistrar { @@ -15,10 +15,7 @@ interface IKeyRegistrar { address operator ) external returns (bool); - function removeKey( - OperatorSet calldata operatorSet, - address operator - ) external; + function removeKey(OperatorSet calldata operatorSet, address operator) external; function isRegistered( OperatorSet calldata operatorSet, diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index 316353bd..2cf6e9f4 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -10,7 +10,7 @@ import { } from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; import {AVSRegistrarStorage} from "./AVSRegistrarStorage.sol"; -import {IKeyRegistrar} from "../../interfaces/IKeyRegistrar.sol"; +import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -83,16 +83,10 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { * @param operatorSetIds The operator sets to validate * @dev This function assumes the operator has already registered a key in the Key Registrar */ - function _validateOperatorKeys( - address operator, - uint32[] calldata operatorSetIds - ) internal view { + function _validateOperatorKeys(address operator, uint32[] calldata operatorSetIds) internal { for (uint32 i = 0; i < operatorSetIds.length; i++) { OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); - require( - keyRegistrar.checkAndUpdateKey(operatorSet, operator), - KeyNotRegistered(operatorSetIds[i]) - ); + require(keyRegistrar.checkAndUpdateKey(operatorSet, operator), KeyNotRegistered()); } } @@ -101,10 +95,7 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { * @param operator The operator to remove * @param operatorSetIds The operator sets to remove */ - function _removeOperatorKeys( - address operator, - uint32[] calldata operatorSetIds - ) internal { + function _removeOperatorKeys(address operator, uint32[] calldata operatorSetIds) internal { for (uint32 i = 0; i < operatorSetIds.length; i++) { OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); keyRegistrar.removeKey(operatorSet, operator); diff --git a/test/mocks/KeyRegistrarMock.sol b/test/mocks/KeyRegistrarMock.sol index f126c49e..af33930c 100644 --- a/test/mocks/KeyRegistrarMock.sol +++ b/test/mocks/KeyRegistrarMock.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {OperatorSet, OperatorSetLib} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; -import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; - +import "src/interfaces/IKeyRegistrar.sol"; contract KeyRegistrarMock is IKeyRegistrar { using OperatorSetLib for OperatorSet; @@ -33,10 +31,7 @@ contract KeyRegistrarMock is IKeyRegistrar { return _operatorRegistered[operatorSet.key()][operator]; } - function removeKey( - OperatorSet calldata operatorSet, - address operator - ) external { + function removeKey(OperatorSet calldata operatorSet, address operator) external { _operatorRegistered[operatorSet.key()][operator] = false; } -} \ No newline at end of file +} diff --git a/test/unit/middlewareV2/AVSRegistrar.t.sol b/test/unit/middlewareV2/AVSRegistrar.t.sol deleted file mode 100644 index e69de29b..00000000 diff --git a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol new file mode 100644 index 00000000..1fdc9795 --- /dev/null +++ b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {MockEigenLayerDeployer} from "./MockDeployer.sol"; +import {IAVSRegistrarErrors, IAVSRegistrarEvents} from "src/interfaces/IAVSRegistrarInternal.sol"; +import {AVSRegistrar} from "src/middlewareV2/registrar/AVSRegistrar.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; +import { + OperatorSet, + OperatorSetLib +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import {ArrayLib} from "eigenlayer-contracts/src/test/utils/ArrayLib.sol"; +import "test/utils/Random.sol"; + +contract AVSRegistrarUnitTests is + MockEigenLayerDeployer, + IAVSRegistrarErrors, + IAVSRegistrarEvents +{ + AVSRegistrar internal avsRegistrarImplementation; + AVSRegistrar internal avsRegistrar; + + address internal constant defaultOperator = address(0x123); + address internal constant AVS = address(0x456); + uint32 internal constant defaultOperatorSetId = 0; + + function setUp() public { + _deployMockEigenLayer(); + + // Deploy the AVSRegistrar + avsRegistrarImplementation = new AVSRegistrar( + AVS, + IAllocationManager(address(allocationManagerMock)), + IKeyRegistrar(address(keyRegistrarMock)) + ); + + avsRegistrar = AVSRegistrar( + address( + new TransparentUpgradeableProxy( + address(avsRegistrarImplementation), address(proxyAdmin), "" + ) + ) + ); + } + + function _registerKey(address operator, uint32[] memory operatorSetIds) internal { + for (uint32 i; i < operatorSetIds.length; ++i) { + keyRegistrarMock.setIsRegistered( + operator, OperatorSet({avs: AVS, id: operatorSetIds[i]}), true + ); + } + } +} + +contract AVSRegistrarUnitTests_RegisterOperator is AVSRegistrarUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notAllocationManager( + address notAllocationManager + ) public { + cheats.assume(notAllocationManager != address(allocationManagerMock)); + + cheats.prank(notAllocationManager); + cheats.expectRevert(NotAllocationManager.selector); + avsRegistrar.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + } + + function test_revert_keyNotRegistered() public { + cheats.expectRevert(KeyNotRegistered.selector); + cheats.prank(address(allocationManagerMock)); + avsRegistrar.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids & register keys + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + _registerKey(defaultOperator, operatorSetIds); + + // Register operator + cheats.expectEmit(true, true, true, true); + emit OperatorRegistered(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); + avsRegistrar.registerOperator(defaultOperator, AVS, operatorSetIds, "0x"); + } +} + +contract AVSRegistrarUnitTests_DeregisterOperator is AVSRegistrarUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notAllocationManager( + address notAllocationManager + ) public { + cheats.assume(notAllocationManager != address(allocationManagerMock)); + + cheats.prank(notAllocationManager); + cheats.expectRevert(NotAllocationManager.selector); + avsRegistrar.deregisterOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32()); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + + // Deregister operator + cheats.expectEmit(true, true, true, true); + emit OperatorDeregistered(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); + avsRegistrar.deregisterOperator(defaultOperator, AVS, operatorSetIds); + } +} diff --git a/test/unit/middlewareV2/MockDeployer.sol b/test/unit/middlewareV2/MockDeployer.sol index ac67c547..a872047d 100644 --- a/test/unit/middlewareV2/MockDeployer.sol +++ b/test/unit/middlewareV2/MockDeployer.sol @@ -2,27 +2,30 @@ pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {ITransparentUpgradeableProxy} from - "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {AllocationManagerMock} from "eigenlayer-contracts/src/test/mocks/AllocationManagerMock.sol"; -import {KeyRegistrarMock} from "../../mocks/KeyRegistrarMock.sol"; -import {Randomness, Random} from "eigenlayer-contracts/src/test/utils/Random.sol"; +import "test/mocks/KeyRegistrarMock.sol"; +import "test/mocks/AllocationManagerMock.sol"; +import "test/utils/Random.sol"; import "forge-std/Test.sol"; abstract contract MockEigenLayerDeployer is Test { + Vm cheats = Vm(VM_ADDRESS); + /// @dev addresses that should be excluded from fuzzing mapping(address => bool) public isExcludedFuzzAddress; - modifier filterFuzzedAddressInputs(address addr) { + modifier filterFuzzedAddressInputs( + address addr + ) { cheats.assume(!isExcludedFuzzAddress[addr]); _; } /// @dev set the random seed for the current test - modifier rand(Randomness r) { + modifier rand( + Randomness r + ) { r.set(); _; } @@ -31,12 +34,17 @@ abstract contract MockEigenLayerDeployer is Test { return Randomness.wrap(Random.SEED).shuffle(); } - Vm cheats = Vm(VM_ADDRESS); - // State Variables + ProxyAdmin public proxyAdmin; AllocationManagerMock public allocationManagerMock; + KeyRegistrarMock public keyRegistrarMock; function _deployMockEigenLayer() internal { + // Deploy the proxy admin + proxyAdmin = new ProxyAdmin(); + + // Deploy mocks allocationManagerMock = new AllocationManagerMock(); + keyRegistrarMock = new KeyRegistrarMock(); } } diff --git a/test/utils/Random.sol b/test/utils/Random.sol new file mode 100644 index 00000000..bb5a2bcc --- /dev/null +++ b/test/utils/Random.sol @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +type Randomness is uint256; + +using Random for Randomness global; + +library Random { + /// ----------------------------------------------------------------------- + /// Constants + /// ----------------------------------------------------------------------- + + /// @dev Equivalent to: `uint256(keccak256("RANDOMNESS.SEED"))`. + uint256 constant SEED = 0x93bfe7cafd9427243dc4fe8c6e706851eb6696ba8e48960dd74ecc96544938ce; + + /// @dev Equivalent to: `uint256(keccak256("RANDOMNESS.SLOT"))`. + uint256 constant SLOT = 0xd0660badbab446a974e6a19901c78a2ad88d7e4f1710b85e1cfc0878477344fd; + + /// ----------------------------------------------------------------------- + /// Helpers + /// ----------------------------------------------------------------------- + + function set( + Randomness r + ) internal returns (Randomness) { + /// @solidity memory-safe-assembly + assembly { + sstore(SLOT, r) + } + return r; + } + + function shuffle( + Randomness r + ) internal returns (Randomness) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, sload(SLOT)) + mstore(0x20, r) + r := keccak256(0x00, 0x20) + } + return r.set(); + } + + /// ----------------------------------------------------------------------- + /// Native Types + /// ----------------------------------------------------------------------- + + function Int256(Randomness r, int256 min, int256 max) internal returns (int256) { + return max <= min ? min : r.Int256() % (max - min) + min; + } + + function Int256( + Randomness r + ) internal returns (int256) { + return r.unwrap() % 2 == 0 ? int256(r.Uint256()) : -int256(r.Uint256()); + } + + function Int128(Randomness r, int128 min, int128 max) internal returns (int128) { + return int128(Int256(r, min, max)); + } + + function Int128( + Randomness r + ) internal returns (int128) { + return int128(Int256(r)); + } + + function Int64(Randomness r, int64 min, int64 max) internal returns (int64) { + return int64(Int256(r, min, max)); + } + + function Int64( + Randomness r + ) internal returns (int64) { + return int64(Int256(r)); + } + + function Int32(Randomness r, int32 min, int32 max) internal returns (int32) { + return int32(Int256(r, min, max)); + } + + function Uint256(Randomness r, uint256 min, uint256 max) internal returns (uint256) { + return max <= min ? min : r.Uint256() % (max - min) + min; + } + + function Uint256( + Randomness r + ) internal returns (uint256) { + return r.shuffle().unwrap(); + } + + function Uint128(Randomness r, uint128 min, uint128 max) internal returns (uint128) { + return uint128(Uint256(r, min, max)); + } + + function Uint128( + Randomness r + ) internal returns (uint128) { + return uint128(Uint256(r)); + } + + function Uint64(Randomness r, uint64 min, uint64 max) internal returns (uint64) { + return uint64(Uint256(r, min, max)); + } + + function Uint64( + Randomness r + ) internal returns (uint64) { + return uint64(Uint256(r)); + } + + function Uint32(Randomness r, uint32 min, uint32 max) internal returns (uint32) { + return uint32(Uint256(r, min, max)); + } + + function Uint32( + Randomness r + ) internal returns (uint32) { + return uint32(Uint256(r)); + } + + function Bytes32( + Randomness r + ) internal returns (bytes32) { + return bytes32(r.Uint256()); + } + + function Address( + Randomness r + ) internal returns (address) { + return address(uint160(r.Uint256(1, type(uint160).max))); + } + + function Boolean( + Randomness r + ) internal returns (bool) { + return r.Uint256() % 2 == 0; + } + + /// ----------------------------------------------------------------------- + /// Arrays + /// ----------------------------------------------------------------------- + + function Int256Array( + Randomness r, + uint256 len, + int256 min, + int256 max + ) internal returns (int256[] memory arr) { + arr = new int256[](len); + for (uint256 i; i < len; ++i) { + arr[i] = r.Int256(min, max); + } + } + + function Int128Array( + Randomness r, + uint256 len, + int128 min, + int128 max + ) internal returns (int128[] memory arr) { + arr = new int128[](len); + for (uint256 i; i < len; ++i) { + arr[i] = r.Int128(min, max); + } + } + + function Int64Array( + Randomness r, + uint256 len, + int64 min, + int64 max + ) internal returns (int64[] memory arr) { + arr = new int64[](len); + for (uint256 i; i < len; ++i) { + arr[i] = r.Int64(min, max); + } + } + + function Int32Array( + Randomness r, + uint256 len, + int32 min, + int32 max + ) internal returns (int32[] memory arr) { + arr = new int32[](len); + for (uint256 i; i < len; ++i) { + arr[i] = r.Int32(min, max); + } + } + + function Uint256Array( + Randomness r, + uint256 len, + uint256 min, + uint256 max + ) internal returns (uint256[] memory arr) { + arr = new uint256[](len); + for (uint256 i; i < len; ++i) { + arr[i] = uint256(r.Uint256(min, max)); + } + } + + function Uint32Array( + Randomness r, + uint256 len, + uint32 min, + uint32 max + ) internal returns (uint32[] memory arr) { + arr = new uint32[](len); + for (uint256 i; i < len; ++i) { + arr[i] = r.Uint32(min, max); + } + } + + /// ----------------------------------------------------------------------- + /// Helpers + /// ----------------------------------------------------------------------- + + function wrap( + uint256 r + ) internal pure returns (Randomness) { + return Randomness.wrap(r); + } + + function unwrap( + Randomness r + ) internal pure returns (uint256) { + return Randomness.unwrap(r); + } +} From 7a8bd0686b8f3d581e3f139b0ce22ed96e8d20df Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 20:02:04 -0400 Subject: [PATCH 06/18] test: add allowlist unit tests --- src/interfaces/IAllowlist.sol | 17 +- .../registrar/modules/Allowlist.sol | 23 +- .../registrar/modules/AllowlistStorage.sol | 6 +- .../presets/AVSRegistrarAllowlist.sol | 7 +- .../AVSRegistrarAllowlistUnit.t.sol | 248 ++++++++++++++++++ test/unit/middlewareV2/AVSRegistrarBase.t.sol | 38 +++ test/unit/middlewareV2/AVSRegistrarUnit.t.sol | 42 +-- 7 files changed, 326 insertions(+), 55 deletions(-) create mode 100644 test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol create mode 100644 test/unit/middlewareV2/AVSRegistrarBase.t.sol diff --git a/src/interfaces/IAllowlist.sol b/src/interfaces/IAllowlist.sol index 67fd00ee..4b266be4 100644 --- a/src/interfaces/IAllowlist.sol +++ b/src/interfaces/IAllowlist.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; + interface IAllowlistErrors { /// @notice Thrown when the operator is already in the allowlist error OperatorAlreadyInAllowlist(); @@ -10,45 +12,54 @@ interface IAllowlistErrors { interface IAllowlistEvents { /// @notice Emitted when an operator is added to the allowlist - event OperatorAddedToAllowlist(address indexed operator); + event OperatorAddedToAllowlist(OperatorSet indexed operatorSet, address indexed operator); /// @notice Emitted when an operator is removed from the allowlist - event OperatorRemovedFromAllowlist(address indexed operator); + event OperatorRemovedFromAllowlist(OperatorSet indexed operatorSet, address indexed operator); } interface IAllowlist is IAllowlistErrors, IAllowlistEvents { /** * @notice Adds an operator to the allowlist + * @param operatorSet The operator set to add the operator to * @param operator The operator to add to the allowlist * @dev Only callable by the owner */ function addOperatorToAllowlist( + OperatorSet memory operatorSet, address operator ) external; /** * @notice Removes an operator from the allowlist + * @param operatorSet The operator set to remove the operator from * @param operator The operator to remove from the allowlist * @dev If an operator is removed from the allowlist and is already registered, the avs * must then handle state changes appropriately (ie. eject the operator) * @dev Only callable by the owner */ function removeOperatorFromAllowlist( + OperatorSet memory operatorSet, address operator ) external; /** * @notice Checks if an operator is in the allowlist + * @param operatorSet The operator set to check the operator in * @param operator The operator to check * @return True if the operator is in the allowlist, false otherwise */ function isOperatorAllowed( + OperatorSet memory operatorSet, address operator ) external view returns (bool); /** * @notice Returns all operators in the allowlist + * @param operatorSet The operator set to get the allowed operators from * @return An array of all operators in the allowlist * @dev This function should be used with caution, as it can be expensive to call on-chain */ - function getAllowedOperators() external view returns (address[] memory); + function getAllowedOperators( + OperatorSet memory operatorSet + ) external view returns (address[] memory); } diff --git a/src/middlewareV2/registrar/modules/Allowlist.sol b/src/middlewareV2/registrar/modules/Allowlist.sol index 960a91bf..fe7643a3 100644 --- a/src/middlewareV2/registrar/modules/Allowlist.sol +++ b/src/middlewareV2/registrar/modules/Allowlist.sol @@ -10,8 +10,10 @@ import {EnumerableSetUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {OperatorSet, OperatorSetLib} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; abstract contract Allowlist is OwnableUpgradeable, AllowlistStorage { + using OperatorSetLib for OperatorSet; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; function initialize( @@ -29,31 +31,34 @@ abstract contract Allowlist is OwnableUpgradeable, AllowlistStorage { /// @inheritdoc IAllowlist function addOperatorToAllowlist( + OperatorSet memory operatorSet, address operator ) external onlyOwner { - EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; - require(allowedOperators.add(operator), OperatorAlreadyInAllowlist()); + require(_allowedOperators[operatorSet.key()].add(operator), OperatorAlreadyInAllowlist()); + emit OperatorAddedToAllowlist(operatorSet, operator); } /// @inheritdoc IAllowlist function removeOperatorFromAllowlist( + OperatorSet memory operatorSet, address operator ) external onlyOwner { - EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; - require(allowedOperators.remove(operator), OperatorNotInAllowlist()); + require(_allowedOperators[operatorSet.key()].remove(operator), OperatorNotInAllowlist()); + emit OperatorRemovedFromAllowlist(operatorSet, operator); } /// @inheritdoc IAllowlist function isOperatorAllowed( + OperatorSet memory operatorSet, address operator ) public view returns (bool) { - EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; - return allowedOperators.contains(operator); + return _allowedOperators[operatorSet.key()].contains(operator); } /// @inheritdoc IAllowlist - function getAllowedOperators() external view returns (address[] memory) { - EnumerableSetUpgradeable.AddressSet storage allowedOperators = _allowedOperators; - return allowedOperators.values(); + function getAllowedOperators( + OperatorSet memory operatorSet + ) external view returns (address[] memory) { + return _allowedOperators[operatorSet.key()].values(); } } diff --git a/src/middlewareV2/registrar/modules/AllowlistStorage.sol b/src/middlewareV2/registrar/modules/AllowlistStorage.sol index 6e697953..8df8b8c6 100644 --- a/src/middlewareV2/registrar/modules/AllowlistStorage.sol +++ b/src/middlewareV2/registrar/modules/AllowlistStorage.sol @@ -9,13 +9,13 @@ import {EnumerableSetUpgradeable} from abstract contract AllowlistStorage is IAllowlist { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; - /// @dev This data structure takes up 2 storage slots - EnumerableSetUpgradeable.AddressSet internal _allowedOperators; + /// @dev Mapping from operatorSet to the allowed operators for that operatorSet + mapping(bytes32 operatorSetKey => EnumerableSetUpgradeable.AddressSet allowedOperators) internal _allowedOperators; /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[48] private __GAP; + uint256[49] private __GAP; } diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol index a64c6bcf..f1015ec3 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol @@ -7,6 +7,7 @@ import {IAllocationManager} from import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; import {AVSRegistrar} from "../AVSRegistrar.sol"; import {Allowlist} from "../modules/Allowlist.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { constructor( @@ -21,7 +22,7 @@ contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { _initializeAllowlist(admin); } - /// @notice Set the socket for the operator + /// @notice Before registering operator, check if the operator is in the allowlist function _beforeRegisterOperator( address operator, uint32[] calldata operatorSetIds, @@ -29,6 +30,8 @@ contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { ) internal override { super._beforeRegisterOperator(operator, operatorSetIds, data); - require(isOperatorAllowed(operator), "Operator not in allowlist"); + for (uint32 i; i < operatorSetIds.length; ++i) { + require(isOperatorAllowed(OperatorSet({avs: avs, id: operatorSetIds[i]}), operator), OperatorNotInAllowlist()); + } } } diff --git a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol new file mode 100644 index 00000000..6981cc83 --- /dev/null +++ b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "./AVSRegistrarBase.t.sol"; +import {AVSRegistrarWithAllowlist} from "src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol"; +import {IAllowlistErrors, IAllowlistEvents} from "src/interfaces/IAllowlist.sol"; + +contract AVSRegistrarAllowlistUnitTests is AVSRegistrarBase, IAllowlistErrors, IAllowlistEvents { + + AVSRegistrarWithAllowlist public avsRegistrarWithAllowlist; + address public allowlistAdmin = address(this); + + function setUp() public override { + super.setUp(); + + avsRegistrarImplementation = new AVSRegistrarWithAllowlist( + AVS, + IAllocationManager(address(allocationManagerMock)), + IKeyRegistrar(address(keyRegistrarMock)) + ); + + avsRegistrarWithAllowlist = AVSRegistrarWithAllowlist( + address( + new TransparentUpgradeableProxy( + address(avsRegistrarImplementation), + address(proxyAdmin), + abi.encodeWithSelector(AVSRegistrarWithAllowlist.initialize.selector, address(this)) + ) + ) + ); + } + + function _addOperatorToAllowlist( + address operator, + uint32[] memory operatorSetIds + ) internal { + for (uint32 i; i < operatorSetIds.length; ++i) { + cheats.prank(allowlistAdmin); + avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), operator); + } + } +} + +contract AVSRegistrarAllowlistUnitTests_initialize is AVSRegistrarAllowlistUnitTests { + function test_initialization() public view { + // Check the admin is set + assertEq(avsRegistrarWithAllowlist.owner(), allowlistAdmin, "Initialization: owner incorrect"); + } + + function test_revert_alreadyInitialized() public { + cheats.expectRevert("Initializable: contract is already initialized"); + avsRegistrarWithAllowlist.initialize(allowlistAdmin); + } +} + +contract AVSRegistrarAllowlistUnitTests_addOperatorToAllowlist is AVSRegistrarAllowlistUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notOwner( + address notOwner + ) public { + cheats.assume(notOwner != allowlistAdmin); + + cheats.expectRevert("Ownable: caller is not the owner"); + cheats.prank(notOwner); + avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: 0}), defaultOperator); + } + + function test_revert_operatorAlreadyInAllowlist() public { + _addOperatorToAllowlist(defaultOperator, defaultOperatorSetId.toArrayU32()); + + cheats.expectRevert(OperatorAlreadyInAllowlist.selector); + cheats.prank(allowlistAdmin); + avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: 0}), defaultOperator); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + + // Add operator to allowlist + for (uint32 i; i < operatorSetIds.length; ++i) { + cheats.expectEmit(true, true, true, true); + emit OperatorAddedToAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + cheats.prank(allowlistAdmin); + avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + } + + // Check the operator is in the allowlist + for (uint32 i; i < operatorSetIds.length; ++i) { + assertTrue(avsRegistrarWithAllowlist.isOperatorAllowed(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator), "Operator not in allowlist"); + } + } +} + +contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is AVSRegistrarAllowlistUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notOwner( + address notOwner + ) public { + cheats.assume(notOwner != allowlistAdmin); + } + + function test_revert_operatorNotInAllowlist() public { + cheats.expectRevert(OperatorNotInAllowlist.selector); + cheats.prank(allowlistAdmin); + avsRegistrarWithAllowlist.removeOperatorFromAllowlist(OperatorSet({avs: AVS, id: 0}), defaultOperator); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + + // Add operator to allowlist + _addOperatorToAllowlist(defaultOperator, operatorSetIds); + + // Remove operator from allowlist + for (uint32 i; i < operatorSetIds.length; ++i) { + cheats.expectEmit(true, true, true, true); + emit OperatorRemovedFromAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + cheats.prank(allowlistAdmin); + avsRegistrarWithAllowlist.removeOperatorFromAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + } + + // Check the operator is not in the allowlist + for (uint32 i; i < operatorSetIds.length; ++i) { + assertFalse(avsRegistrarWithAllowlist.isOperatorAllowed(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator), "Operator still in allowlist"); + } + } +} + +contract AVSRegistrarAllowistUnitTest_getRegisteredOperators is AVSRegistrarAllowlistUnitTests { + using ArrayLib for *; + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random addresses + uint32 numAddresses = r.Uint32(1, 50); + address[] memory operators = new address[](numAddresses); + for (uint32 i; i < numAddresses; ++i) { + operators[i] = r.Address(); + } + + // Generate random operator set ids + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + + // Add operators to allowlist + for (uint32 i; i < operators.length; ++i) { + _addOperatorToAllowlist(operators[i], operatorSetIds); + } + + // Get the allowed operators + for (uint32 i; i < operatorSetIds.length; ++i) { + // Note: although ordering is not guaranteed generally, it works here since we do not do any removes. + address[] memory allowedOperators = avsRegistrarWithAllowlist.getAllowedOperators(OperatorSet({avs: AVS, id: operatorSetIds[i]})); + assertEq(allowedOperators.length, operators.length, "Incorrect number of allowed operators"); + for (uint32 j; j < allowedOperators.length; ++j) { + assertTrue(allowedOperators[j] == operators[j], "Allowed operator incorrect"); + } + } + } +} + +contract AVSRegistrarAllowListUnitTests_registerOperator is AVSRegistrarAllowlistUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notAllocationManager( + address notAllocationManager + ) public { + cheats.assume(notAllocationManager != address(allocationManagerMock)); + + cheats.prank(notAllocationManager); + cheats.expectRevert(NotAllocationManager.selector); + avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + } + + function test_revert_operatorNotInAllowlist() public { + // Register operator + cheats.expectRevert(OperatorNotInAllowlist.selector); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + } + + function test_revert_keyNotRegistered() public { + // Add operator to allowlist + _addOperatorToAllowlist(defaultOperator, defaultOperatorSetId.toArrayU32()); + + // Register operator + cheats.expectRevert(KeyNotRegistered.selector); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids & register keys + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + _registerKey(defaultOperator, operatorSetIds); + + // Add operator to allowlist + _addOperatorToAllowlist(defaultOperator, operatorSetIds); + + // Register operator + cheats.expectEmit(true, true, true, true); + emit OperatorRegistered(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, operatorSetIds, "0x"); + } +} + +contract AVSRegistrarAllowListUnitTests_deregisterOperator is AVSRegistrarAllowlistUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notAllocationManager( + address notAllocationManager + ) public { + cheats.assume(notAllocationManager != address(allocationManagerMock)); + + cheats.prank(notAllocationManager); + cheats.expectRevert(NotAllocationManager.selector); + avsRegistrar.deregisterOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32()); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + + // Deregister operator + cheats.expectEmit(true, true, true, true); + emit OperatorDeregistered(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); + avsRegistrar.deregisterOperator(defaultOperator, AVS, operatorSetIds); + } +} \ No newline at end of file diff --git a/test/unit/middlewareV2/AVSRegistrarBase.t.sol b/test/unit/middlewareV2/AVSRegistrarBase.t.sol new file mode 100644 index 00000000..43332f54 --- /dev/null +++ b/test/unit/middlewareV2/AVSRegistrarBase.t.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {MockEigenLayerDeployer} from "./MockDeployer.sol"; +import {IAVSRegistrarErrors, IAVSRegistrarEvents} from "src/interfaces/IAVSRegistrarInternal.sol"; +import {AVSRegistrar} from "src/middlewareV2/registrar/AVSRegistrar.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; +import { + OperatorSet, + OperatorSetLib +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import {ArrayLib} from "eigenlayer-contracts/src/test/utils/ArrayLib.sol"; +import "test/utils/Random.sol"; + +abstract contract AVSRegistrarBase is MockEigenLayerDeployer, IAVSRegistrarErrors, IAVSRegistrarEvents { + AVSRegistrar internal avsRegistrar; + AVSRegistrar internal avsRegistrarImplementation; + + address internal constant defaultOperator = address(0x123); + address internal constant AVS = address(0x456); + uint32 internal constant defaultOperatorSetId = 0; + + function setUp() public virtual { + _deployMockEigenLayer(); + } + + function _registerKey(address operator, uint32[] memory operatorSetIds) internal { + for (uint32 i; i < operatorSetIds.length; ++i) { + keyRegistrarMock.setIsRegistered( + operator, OperatorSet({avs: AVS, id: operatorSetIds[i]}), true + ); + } + } +} \ No newline at end of file diff --git a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol index 1fdc9795..ea9b3780 100644 --- a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol @@ -1,37 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {TransparentUpgradeableProxy} from - "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import {MockEigenLayerDeployer} from "./MockDeployer.sol"; -import {IAVSRegistrarErrors, IAVSRegistrarEvents} from "src/interfaces/IAVSRegistrarInternal.sol"; -import {AVSRegistrar} from "src/middlewareV2/registrar/AVSRegistrar.sol"; -import {IAllocationManager} from - "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; -import { - OperatorSet, - OperatorSetLib -} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; -import {ArrayLib} from "eigenlayer-contracts/src/test/utils/ArrayLib.sol"; -import "test/utils/Random.sol"; +import "./AVSRegistrarBase.t.sol"; +contract AVSRegistrarUnitTests is AVSRegistrarBase { + function setUp() public override { + super.setUp(); -contract AVSRegistrarUnitTests is - MockEigenLayerDeployer, - IAVSRegistrarErrors, - IAVSRegistrarEvents -{ - AVSRegistrar internal avsRegistrarImplementation; - AVSRegistrar internal avsRegistrar; - - address internal constant defaultOperator = address(0x123); - address internal constant AVS = address(0x456); - uint32 internal constant defaultOperatorSetId = 0; - - function setUp() public { - _deployMockEigenLayer(); - - // Deploy the AVSRegistrar avsRegistrarImplementation = new AVSRegistrar( AVS, IAllocationManager(address(allocationManagerMock)), @@ -46,14 +20,6 @@ contract AVSRegistrarUnitTests is ) ); } - - function _registerKey(address operator, uint32[] memory operatorSetIds) internal { - for (uint32 i; i < operatorSetIds.length; ++i) { - keyRegistrarMock.setIsRegistered( - operator, OperatorSet({avs: AVS, id: operatorSetIds[i]}), true - ); - } - } } contract AVSRegistrarUnitTests_RegisterOperator is AVSRegistrarUnitTests { From ea6407ade8f99d540239ce1c2ad7bd259e1d7344 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 20:36:35 -0400 Subject: [PATCH 07/18] feat: add socket registry tests --- src/interfaces/IAllowlist.sol | 5 +- src/interfaces/ISocketRegistryV2.sol | 45 ++++ .../registrar/modules/Allowlist.sol | 5 +- .../registrar/modules/AllowlistStorage.sol | 3 +- .../registrar/modules/SocketRegistry.sol | 53 +++-- .../modules/SocketRegistryStorage.sol | 9 +- .../presets/AVSRegistrarAllowlist.sol | 5 +- .../presets/AVSRegistrarWithSocket.sol | 17 +- .../AVSRegistrarAllowlistUnit.t.sol | 93 +++++--- test/unit/middlewareV2/AVSRegistrarBase.t.sol | 8 +- .../middlewareV2/AVSRegistrarSocketUnit.t.sol | 214 ++++++++++++++++++ test/unit/middlewareV2/AVSRegistrarUnit.t.sol | 1 + 12 files changed, 388 insertions(+), 70 deletions(-) create mode 100644 src/interfaces/ISocketRegistryV2.sol create mode 100644 test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol diff --git a/src/interfaces/IAllowlist.sol b/src/interfaces/IAllowlist.sol index 4b266be4..558c4734 100644 --- a/src/interfaces/IAllowlist.sol +++ b/src/interfaces/IAllowlist.sol @@ -24,10 +24,7 @@ interface IAllowlist is IAllowlistErrors, IAllowlistEvents { * @param operator The operator to add to the allowlist * @dev Only callable by the owner */ - function addOperatorToAllowlist( - OperatorSet memory operatorSet, - address operator - ) external; + function addOperatorToAllowlist(OperatorSet memory operatorSet, address operator) external; /** * @notice Removes an operator from the allowlist diff --git a/src/interfaces/ISocketRegistryV2.sol b/src/interfaces/ISocketRegistryV2.sol new file mode 100644 index 00000000..6e2ffca1 --- /dev/null +++ b/src/interfaces/ISocketRegistryV2.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; + +interface ISocketRegistryErrors { + /// @notice Thrown when the caller is not the operator + error CallerNotOperator(); + + /// @notice Thrown when the data length mismatch + error DataLengthMismatch(); +} + +interface ISocketRegistryEvents { + /// @notice Emitted when an operator socket is set + event OperatorSocketSet(address indexed operator, OperatorSet operatorSet, string socket); + + /// @notice Emitted when an operator socket is removed + event OperatorSocketRemoved(address indexed operator, OperatorSet operatorSet); +} + +interface ISocketRegistry is ISocketRegistryErrors, ISocketRegistryEvents { + /** + * @notice Gets the socket for an operator. + * @param operator The operator to get the socket for. + * @return The socket for the operator. + */ + function getOperatorSocket( + address operator, + OperatorSet memory operatorSet + ) external view returns (string memory); + + /** + * @notice Updates the socket for an operator. + * @param operator The operator to set the socket for. + * @param operatorSet The operator set to set the socket for. + * @param socket The socket to set for the operator. + * @dev This function can only be called by the operator themselves. + */ + function updateSocket( + address operator, + OperatorSet memory operatorSet, + string memory socket + ) external; +} diff --git a/src/middlewareV2/registrar/modules/Allowlist.sol b/src/middlewareV2/registrar/modules/Allowlist.sol index fe7643a3..6dd6f2ef 100644 --- a/src/middlewareV2/registrar/modules/Allowlist.sol +++ b/src/middlewareV2/registrar/modules/Allowlist.sol @@ -10,7 +10,10 @@ import {EnumerableSetUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {OperatorSet, OperatorSetLib} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import { + OperatorSet, + OperatorSetLib +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; abstract contract Allowlist is OwnableUpgradeable, AllowlistStorage { using OperatorSetLib for OperatorSet; diff --git a/src/middlewareV2/registrar/modules/AllowlistStorage.sol b/src/middlewareV2/registrar/modules/AllowlistStorage.sol index 8df8b8c6..3c419f8c 100644 --- a/src/middlewareV2/registrar/modules/AllowlistStorage.sol +++ b/src/middlewareV2/registrar/modules/AllowlistStorage.sol @@ -10,7 +10,8 @@ abstract contract AllowlistStorage is IAllowlist { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; /// @dev Mapping from operatorSet to the allowed operators for that operatorSet - mapping(bytes32 operatorSetKey => EnumerableSetUpgradeable.AddressSet allowedOperators) internal _allowedOperators; + mapping(bytes32 operatorSetKey => EnumerableSetUpgradeable.AddressSet allowedOperators) internal + _allowedOperators; /** * @dev This empty reserved space is put in place to allow future versions to add new diff --git a/src/middlewareV2/registrar/modules/SocketRegistry.sol b/src/middlewareV2/registrar/modules/SocketRegistry.sol index 262440f7..d6d187e7 100644 --- a/src/middlewareV2/registrar/modules/SocketRegistry.sol +++ b/src/middlewareV2/registrar/modules/SocketRegistry.sol @@ -1,58 +1,57 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; +import {ISocketRegistry} from "../../../interfaces/ISocketRegistryV2.sol"; import {SocketRegistryStorage} from "./SocketRegistryStorage.sol"; -import {ISocketRegistry} from "../../../interfaces/ISocketRegistry.sol"; +import { + OperatorSetLib, + OperatorSet +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; /// @notice A module that allows for the setting and removal of operator sockets abstract contract SocketRegistry is SocketRegistryStorage { - /// @notice Emitted when an operator socket is set - event OperatorSocketSet(address indexed operator, string socket); + using OperatorSetLib for OperatorSet; - /// @notice Emitted when an operator socket is removed - event OperatorSocketRemoved(address indexed operator); - - /** - * @notice Gets the socket for an operator. - * @param operator The operator to get the socket for. - * @return The socket for the operator. - */ + /// @inheritdoc ISocketRegistry function getOperatorSocket( - address operator + address operator, + OperatorSet memory operatorSet ) external view returns (string memory) { - return operatorToSocket[operator]; + return _operatorToSocket[operator][operatorSet.key()]; } - /** - * @notice Updates the socket for the operator. - * @param socket The socket (any arbitrary string as deemed useful by an AVS) to set. - * @dev This function can only be called by the operator themselves. - */ + /// @inheritdoc ISocketRegistry function updateSocket( + address operator, + OperatorSet memory operatorSet, string memory socket ) external { - _setOperatorSocket(msg.sender, socket); + require(msg.sender == operator, CallerNotOperator()); + _setOperatorSocket(operator, operatorSet, socket); } /** * @notice Sets the socket for an operator. * @param operator The address of the operator to set the socket for. + * @param operatorSet The operator set to set the socket for. * @param socket The socket (any arbitrary string as deemed useful by an AVS) to set. * @dev This function assumes a single socket per operator, for all operatorSets. */ - function _setOperatorSocket(address operator, string memory socket) internal { - operatorToSocket[operator] = socket; - emit OperatorSocketSet(operator, socket); + function _setOperatorSocket( + address operator, + OperatorSet memory operatorSet, + string memory socket + ) internal { + _operatorToSocket[operator][operatorSet.key()] = socket; + emit OperatorSocketSet(operator, operatorSet, socket); } /** * @notice Deletes the socket for an operator. * @param operator The address of the operator to delete the socket for. */ - function _removeOperatorSocket( - address operator - ) internal { - delete operatorToSocket[operator]; - emit OperatorSocketRemoved(operator); + function _removeOperatorSocket(address operator, OperatorSet memory operatorSet) internal { + delete _operatorToSocket[operator][operatorSet.key()]; + emit OperatorSocketRemoved(operator, operatorSet); } } diff --git a/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol b/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol index 03875218..1f495767 100644 --- a/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol +++ b/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol @@ -1,21 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {ISocketRegistry} from "../../../interfaces/ISocketRegistry.sol"; +import {ISocketRegistry} from "../../../interfaces/ISocketRegistryV2.sol"; /** * @title Storage variables for the `SocketRegistry` contract. * @author Layr Labs, Inc. */ -abstract contract SocketRegistryStorage { +abstract contract SocketRegistryStorage is ISocketRegistry { /** * * STATE * */ - /// @notice A mapping from operator addresses to their sockets - mapping(address => string) public operatorToSocket; + /// @notice A mapping from operator addresses to operatorSet to socker + mapping(address operator => mapping(bytes32 operatorSetKey => string operatorSocket)) internal + _operatorToSocket; /** * @dev This empty reserved space is put in place to allow future versions to add new diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol index f1015ec3..dede005f 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol @@ -31,7 +31,10 @@ contract AVSRegistrarWithAllowlist is AVSRegistrar, Allowlist { super._beforeRegisterOperator(operator, operatorSetIds, data); for (uint32 i; i < operatorSetIds.length; ++i) { - require(isOperatorAllowed(OperatorSet({avs: avs, id: operatorSetIds[i]}), operator), OperatorNotInAllowlist()); + require( + isOperatorAllowed(OperatorSet({avs: avs, id: operatorSetIds[i]}), operator), + OperatorNotInAllowlist() + ); } } } diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol index a617a1be..5f722f6e 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol @@ -7,6 +7,10 @@ import {IAllocationManager} from import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; import {AVSRegistrar} from "../AVSRegistrar.sol"; import {SocketRegistry} from "../modules/SocketRegistry.sol"; +import { + OperatorSetLib, + OperatorSet +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { constructor( @@ -23,8 +27,13 @@ contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { ) internal override { super._afterRegisterOperator(operator, operatorSetIds, data); - string memory socket = abi.decode(data, (string)); - _setOperatorSocket(operator, socket); + // Decode data and validate length + string[] memory sockets = abi.decode(data, (string[])); + require(sockets.length == operatorSetIds.length, DataLengthMismatch()); + + for (uint32 i; i < operatorSetIds.length; ++i) { + _setOperatorSocket(operator, OperatorSet({avs: avs, id: operatorSetIds[i]}), sockets[i]); + } } /// @notice Remove the socket for the operator @@ -34,6 +43,8 @@ contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { ) internal override { super._afterDeregisterOperator(operator, operatorSetIds); - _removeOperatorSocket(operator); + for (uint32 i; i < operatorSetIds.length; ++i) { + _removeOperatorSocket(operator, OperatorSet({avs: avs, id: operatorSetIds[i]})); + } } } diff --git a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol index 6981cc83..f8c1a517 100644 --- a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.27; import "./AVSRegistrarBase.t.sol"; -import {AVSRegistrarWithAllowlist} from "src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol"; +import {AVSRegistrarWithAllowlist} from + "src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol"; import {IAllowlistErrors, IAllowlistEvents} from "src/interfaces/IAllowlist.sol"; contract AVSRegistrarAllowlistUnitTests is AVSRegistrarBase, IAllowlistErrors, IAllowlistEvents { - AVSRegistrarWithAllowlist public avsRegistrarWithAllowlist; address public allowlistAdmin = address(this); @@ -22,21 +22,22 @@ contract AVSRegistrarAllowlistUnitTests is AVSRegistrarBase, IAllowlistErrors, I avsRegistrarWithAllowlist = AVSRegistrarWithAllowlist( address( new TransparentUpgradeableProxy( - address(avsRegistrarImplementation), + address(avsRegistrarImplementation), address(proxyAdmin), - abi.encodeWithSelector(AVSRegistrarWithAllowlist.initialize.selector, address(this)) + abi.encodeWithSelector( + AVSRegistrarWithAllowlist.initialize.selector, address(this) + ) ) ) ); } - function _addOperatorToAllowlist( - address operator, - uint32[] memory operatorSetIds - ) internal { + function _addOperatorToAllowlist(address operator, uint32[] memory operatorSetIds) internal { for (uint32 i; i < operatorSetIds.length; ++i) { cheats.prank(allowlistAdmin); - avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), operator); + avsRegistrarWithAllowlist.addOperatorToAllowlist( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), operator + ); } } } @@ -44,7 +45,9 @@ contract AVSRegistrarAllowlistUnitTests is AVSRegistrarBase, IAllowlistErrors, I contract AVSRegistrarAllowlistUnitTests_initialize is AVSRegistrarAllowlistUnitTests { function test_initialization() public view { // Check the admin is set - assertEq(avsRegistrarWithAllowlist.owner(), allowlistAdmin, "Initialization: owner incorrect"); + assertEq( + avsRegistrarWithAllowlist.owner(), allowlistAdmin, "Initialization: owner incorrect" + ); } function test_revert_alreadyInitialized() public { @@ -63,7 +66,9 @@ contract AVSRegistrarAllowlistUnitTests_addOperatorToAllowlist is AVSRegistrarAl cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(notOwner); - avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: 0}), defaultOperator); + avsRegistrarWithAllowlist.addOperatorToAllowlist( + OperatorSet({avs: AVS, id: 0}), defaultOperator + ); } function test_revert_operatorAlreadyInAllowlist() public { @@ -71,7 +76,9 @@ contract AVSRegistrarAllowlistUnitTests_addOperatorToAllowlist is AVSRegistrarAl cheats.expectRevert(OperatorAlreadyInAllowlist.selector); cheats.prank(allowlistAdmin); - avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: 0}), defaultOperator); + avsRegistrarWithAllowlist.addOperatorToAllowlist( + OperatorSet({avs: AVS, id: 0}), defaultOperator + ); } function testFuzz_correctness( @@ -84,19 +91,30 @@ contract AVSRegistrarAllowlistUnitTests_addOperatorToAllowlist is AVSRegistrarAl // Add operator to allowlist for (uint32 i; i < operatorSetIds.length; ++i) { cheats.expectEmit(true, true, true, true); - emit OperatorAddedToAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + emit OperatorAddedToAllowlist( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator + ); cheats.prank(allowlistAdmin); - avsRegistrarWithAllowlist.addOperatorToAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + avsRegistrarWithAllowlist.addOperatorToAllowlist( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator + ); } // Check the operator is in the allowlist for (uint32 i; i < operatorSetIds.length; ++i) { - assertTrue(avsRegistrarWithAllowlist.isOperatorAllowed(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator), "Operator not in allowlist"); + assertTrue( + avsRegistrarWithAllowlist.isOperatorAllowed( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator + ), + "Operator not in allowlist" + ); } } } -contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is AVSRegistrarAllowlistUnitTests { +contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is + AVSRegistrarAllowlistUnitTests +{ using ArrayLib for *; function testFuzz_revert_notOwner( @@ -108,7 +126,9 @@ contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is AVSRegist function test_revert_operatorNotInAllowlist() public { cheats.expectRevert(OperatorNotInAllowlist.selector); cheats.prank(allowlistAdmin); - avsRegistrarWithAllowlist.removeOperatorFromAllowlist(OperatorSet({avs: AVS, id: 0}), defaultOperator); + avsRegistrarWithAllowlist.removeOperatorFromAllowlist( + OperatorSet({avs: AVS, id: 0}), defaultOperator + ); } function testFuzz_correctness( @@ -124,14 +144,23 @@ contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is AVSRegist // Remove operator from allowlist for (uint32 i; i < operatorSetIds.length; ++i) { cheats.expectEmit(true, true, true, true); - emit OperatorRemovedFromAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + emit OperatorRemovedFromAllowlist( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator + ); cheats.prank(allowlistAdmin); - avsRegistrarWithAllowlist.removeOperatorFromAllowlist(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator); + avsRegistrarWithAllowlist.removeOperatorFromAllowlist( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator + ); } // Check the operator is not in the allowlist for (uint32 i; i < operatorSetIds.length; ++i) { - assertFalse(avsRegistrarWithAllowlist.isOperatorAllowed(OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator), "Operator still in allowlist"); + assertFalse( + avsRegistrarWithAllowlist.isOperatorAllowed( + OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultOperator + ), + "Operator still in allowlist" + ); } } } @@ -160,9 +189,13 @@ contract AVSRegistrarAllowistUnitTest_getRegisteredOperators is AVSRegistrarAllo // Get the allowed operators for (uint32 i; i < operatorSetIds.length; ++i) { - // Note: although ordering is not guaranteed generally, it works here since we do not do any removes. - address[] memory allowedOperators = avsRegistrarWithAllowlist.getAllowedOperators(OperatorSet({avs: AVS, id: operatorSetIds[i]})); - assertEq(allowedOperators.length, operators.length, "Incorrect number of allowed operators"); + // Note: although ordering is not guaranteed generally, it works here since we do not do any removes. + address[] memory allowedOperators = avsRegistrarWithAllowlist.getAllowedOperators( + OperatorSet({avs: AVS, id: operatorSetIds[i]}) + ); + assertEq( + allowedOperators.length, operators.length, "Incorrect number of allowed operators" + ); for (uint32 j; j < allowedOperators.length; ++j) { assertTrue(allowedOperators[j] == operators[j], "Allowed operator incorrect"); } @@ -180,14 +213,18 @@ contract AVSRegistrarAllowListUnitTests_registerOperator is AVSRegistrarAllowlis cheats.prank(notAllocationManager); cheats.expectRevert(NotAllocationManager.selector); - avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + avsRegistrarWithAllowlist.registerOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x" + ); } function test_revert_operatorNotInAllowlist() public { // Register operator cheats.expectRevert(OperatorNotInAllowlist.selector); cheats.prank(address(allocationManagerMock)); - avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + avsRegistrarWithAllowlist.registerOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x" + ); } function test_revert_keyNotRegistered() public { @@ -197,7 +234,9 @@ contract AVSRegistrarAllowListUnitTests_registerOperator is AVSRegistrarAllowlis // Register operator cheats.expectRevert(KeyNotRegistered.selector); cheats.prank(address(allocationManagerMock)); - avsRegistrarWithAllowlist.registerOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x"); + avsRegistrarWithAllowlist.registerOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), "0x" + ); } function testFuzz_correctness( @@ -245,4 +284,4 @@ contract AVSRegistrarAllowListUnitTests_deregisterOperator is AVSRegistrarAllowl cheats.prank(address(allocationManagerMock)); avsRegistrar.deregisterOperator(defaultOperator, AVS, operatorSetIds); } -} \ No newline at end of file +} diff --git a/test/unit/middlewareV2/AVSRegistrarBase.t.sol b/test/unit/middlewareV2/AVSRegistrarBase.t.sol index 43332f54..075b912d 100644 --- a/test/unit/middlewareV2/AVSRegistrarBase.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarBase.t.sol @@ -16,7 +16,11 @@ import { import {ArrayLib} from "eigenlayer-contracts/src/test/utils/ArrayLib.sol"; import "test/utils/Random.sol"; -abstract contract AVSRegistrarBase is MockEigenLayerDeployer, IAVSRegistrarErrors, IAVSRegistrarEvents { +abstract contract AVSRegistrarBase is + MockEigenLayerDeployer, + IAVSRegistrarErrors, + IAVSRegistrarEvents +{ AVSRegistrar internal avsRegistrar; AVSRegistrar internal avsRegistrarImplementation; @@ -35,4 +39,4 @@ abstract contract AVSRegistrarBase is MockEigenLayerDeployer, IAVSRegistrarError ); } } -} \ No newline at end of file +} diff --git a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol new file mode 100644 index 00000000..de3525a0 --- /dev/null +++ b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "./AVSRegistrarBase.t.sol"; +import {AVSRegistrarWithSocket} from "src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol"; +import {ISocketRegistryEvents, ISocketRegistryErrors} from "src/interfaces/ISocketRegistryV2.sol"; + +contract AVSRegistrarSocketUnitTests is + AVSRegistrarBase, + ISocketRegistryEvents, + ISocketRegistryErrors +{ + AVSRegistrarWithSocket public avsRegistrarWithSocket; + + string defaultSocket = "Socket"; + bytes socketData; + + function setUp() public override { + super.setUp(); + + avsRegistrarImplementation = new AVSRegistrarWithSocket( + AVS, + IAllocationManager(address(allocationManagerMock)), + IKeyRegistrar(address(keyRegistrarMock)) + ); + + avsRegistrarWithSocket = AVSRegistrarWithSocket( + address( + new TransparentUpgradeableProxy( + address(avsRegistrarImplementation), address(proxyAdmin), "" + ) + ) + ); + + // Encode defaultSocker into data + string[] memory sockets = new string[](1); + sockets[0] = defaultSocket; + socketData = abi.encode(sockets); + } + + function _registerOperator( + uint32[] memory operatorSetIds + ) internal { + // Generate an array of sockets + string[] memory sockets = new string[](operatorSetIds.length); + for (uint32 i; i < operatorSetIds.length; ++i) { + sockets[i] = defaultSocket; + } + socketData = abi.encode(sockets); + + // Register operator + _registerKey(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); + } +} + +contract AVSRegistrarSocketUnitTests_registerOperator is AVSRegistrarSocketUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notAllocationManager( + address notAllocationManager + ) public { + cheats.assume(notAllocationManager != address(allocationManagerMock)); + + cheats.prank(notAllocationManager); + cheats.expectRevert(NotAllocationManager.selector); + avsRegistrarWithSocket.registerOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), socketData + ); + } + + function test_revert_keyNotRegistered() public { + cheats.expectRevert(KeyNotRegistered.selector); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithSocket.registerOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32(), socketData + ); + } + + function testFuzz_revert_dataLengthMismatch() public { + // Generate random operator set ids & register keys + uint32[] memory operatorSetIds = new uint32[](2); + operatorSetIds[0] = defaultOperatorSetId; + operatorSetIds[1] = defaultOperatorSetId + 1; + _registerKey(defaultOperator, operatorSetIds); + + // Register operator + cheats.expectRevert(DataLengthMismatch.selector); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids & register keys + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + _registerKey(defaultOperator, operatorSetIds); + + // Generate an array of sockets + string[] memory sockets = new string[](numOperatorSetIds); + for (uint32 i; i < numOperatorSetIds; ++i) { + sockets[i] = defaultSocket; + } + socketData = abi.encode(sockets); + + // Register operator + cheats.expectEmit(true, true, true, true); + emit OperatorRegistered(defaultOperator, operatorSetIds); + for (uint32 i; i < numOperatorSetIds; ++i) { + cheats.expectEmit(true, true, true, true); + emit OperatorSocketSet( + defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultSocket + ); + } + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); + + // Check that the sockets are set + for (uint32 i; i < numOperatorSetIds; ++i) { + string memory socket = avsRegistrarWithSocket.getOperatorSocket( + defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}) + ); + assertEq(socket, sockets[i], "Socket mismatch"); + } + } +} + +contract AVSRegistrarSocketUnitTests_DeregisterOperator is AVSRegistrarSocketUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notAllocationManager( + address notAllocationManager + ) public { + cheats.assume(notAllocationManager != address(allocationManagerMock)); + + cheats.prank(notAllocationManager); + cheats.expectRevert(NotAllocationManager.selector); + avsRegistrarWithSocket.deregisterOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32() + ); + } + + function testFuzz_correctness( + Randomness r + ) public rand(r) { + // Generate random operator set ids + uint32 numOperatorSetIds = r.Uint32(1, 50); + uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); + + // Set sockets + _registerOperator(operatorSetIds); + + // Deregister operator + for (uint32 i; i < numOperatorSetIds; ++i) { + cheats.expectEmit(true, true, true, true); + emit OperatorSocketRemoved( + defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}) + ); + } + cheats.expectEmit(true, true, true, true); + emit OperatorDeregistered(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); + avsRegistrarWithSocket.deregisterOperator(defaultOperator, AVS, operatorSetIds); + + // Check that the sockets are removed + for (uint32 i; i < numOperatorSetIds; ++i) { + string memory socket = avsRegistrarWithSocket.getOperatorSocket( + defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}) + ); + assertEq(socket, "", "Socket mismatch"); + } + } +} + +contract AVSRegistrarSocketUnitTests_updateSocket is AVSRegistrarSocketUnitTests { + using ArrayLib for *; + + function testFuzz_revert_notOperator( + address notOperator + ) public { + _registerOperator(defaultOperatorSetId.toArrayU32()); + cheats.assume(notOperator != defaultOperator); + + cheats.prank(notOperator); + cheats.expectRevert(CallerNotOperator.selector); + avsRegistrarWithSocket.updateSocket( + defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}), defaultSocket + ); + } + + function test_updateSocket() public { + _registerOperator(defaultOperatorSetId.toArrayU32()); + + string memory newSocket = "NewSocket"; + + cheats.expectEmit(true, true, true, true); + emit OperatorSocketSet( + defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}), newSocket + ); + cheats.prank(defaultOperator); + avsRegistrarWithSocket.updateSocket( + defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}), newSocket + ); + + // Check that the socket is updated + string memory socket = avsRegistrarWithSocket.getOperatorSocket( + defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}) + ); + assertEq(socket, newSocket, "Socket mismatch"); + } +} diff --git a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol index ea9b3780..74c9e110 100644 --- a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.27; import "./AVSRegistrarBase.t.sol"; + contract AVSRegistrarUnitTests is AVSRegistrarBase { function setUp() public override { super.setUp(); From c97e5e961be8e6a8b24ce8f7f65468463af8b4ae Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 20:45:08 -0400 Subject: [PATCH 08/18] chore: fix test --- test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol | 4 ++-- test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol index f8c1a517..f7a31e6d 100644 --- a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol @@ -268,7 +268,7 @@ contract AVSRegistrarAllowListUnitTests_deregisterOperator is AVSRegistrarAllowl cheats.prank(notAllocationManager); cheats.expectRevert(NotAllocationManager.selector); - avsRegistrar.deregisterOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32()); + avsRegistrarWithAllowlist.deregisterOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32()); } function testFuzz_correctness( @@ -282,6 +282,6 @@ contract AVSRegistrarAllowListUnitTests_deregisterOperator is AVSRegistrarAllowl cheats.expectEmit(true, true, true, true); emit OperatorDeregistered(defaultOperator, operatorSetIds); cheats.prank(address(allocationManagerMock)); - avsRegistrar.deregisterOperator(defaultOperator, AVS, operatorSetIds); + avsRegistrarWithAllowlist.deregisterOperator(defaultOperator, AVS, operatorSetIds); } } diff --git a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol index de3525a0..1a55f0c6 100644 --- a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol @@ -107,14 +107,15 @@ contract AVSRegistrarSocketUnitTests_registerOperator is AVSRegistrarSocketUnitT socketData = abi.encode(sockets); // Register operator - cheats.expectEmit(true, true, true, true); - emit OperatorRegistered(defaultOperator, operatorSetIds); for (uint32 i; i < numOperatorSetIds; ++i) { cheats.expectEmit(true, true, true, true); emit OperatorSocketSet( defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultSocket ); } + cheats.expectEmit(true, true, true, true); + emit OperatorRegistered(defaultOperator, operatorSetIds); + cheats.prank(address(allocationManagerMock)); avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); From e28691d59dd5bb802521e9476e3e74b1d6bb8c9a Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 20:46:26 -0400 Subject: [PATCH 09/18] chore: format --- test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol | 4 +++- test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol index f7a31e6d..997a26f3 100644 --- a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol @@ -268,7 +268,9 @@ contract AVSRegistrarAllowListUnitTests_deregisterOperator is AVSRegistrarAllowl cheats.prank(notAllocationManager); cheats.expectRevert(NotAllocationManager.selector); - avsRegistrarWithAllowlist.deregisterOperator(defaultOperator, AVS, defaultOperatorSetId.toArrayU32()); + avsRegistrarWithAllowlist.deregisterOperator( + defaultOperator, AVS, defaultOperatorSetId.toArrayU32() + ); } function testFuzz_correctness( diff --git a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol index 1a55f0c6..2d8a31e6 100644 --- a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol @@ -115,7 +115,7 @@ contract AVSRegistrarSocketUnitTests_registerOperator is AVSRegistrarSocketUnitT } cheats.expectEmit(true, true, true, true); emit OperatorRegistered(defaultOperator, operatorSetIds); - + cheats.prank(address(allocationManagerMock)); avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); From d6884896c460fdce224d7dd36da1d418cc973cce Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Thu, 29 May 2025 22:25:19 -0400 Subject: [PATCH 10/18] fix: socket per operator --- src/interfaces/ISocketRegistryV2.sol | 12 +-- .../registrar/modules/SocketRegistry.sol | 25 ++---- .../modules/SocketRegistryStorage.sol | 5 +- .../presets/AVSRegistrarWithSocket.sol | 24 ++---- .../middlewareV2/AVSRegistrarSocketUnit.t.sol | 83 +++---------------- 5 files changed, 28 insertions(+), 121 deletions(-) diff --git a/src/interfaces/ISocketRegistryV2.sol b/src/interfaces/ISocketRegistryV2.sol index 6e2ffca1..8b611676 100644 --- a/src/interfaces/ISocketRegistryV2.sol +++ b/src/interfaces/ISocketRegistryV2.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; - interface ISocketRegistryErrors { /// @notice Thrown when the caller is not the operator error CallerNotOperator(); @@ -13,10 +11,7 @@ interface ISocketRegistryErrors { interface ISocketRegistryEvents { /// @notice Emitted when an operator socket is set - event OperatorSocketSet(address indexed operator, OperatorSet operatorSet, string socket); - - /// @notice Emitted when an operator socket is removed - event OperatorSocketRemoved(address indexed operator, OperatorSet operatorSet); + event OperatorSocketSet(address indexed operator, string socket); } interface ISocketRegistry is ISocketRegistryErrors, ISocketRegistryEvents { @@ -26,20 +21,17 @@ interface ISocketRegistry is ISocketRegistryErrors, ISocketRegistryEvents { * @return The socket for the operator. */ function getOperatorSocket( - address operator, - OperatorSet memory operatorSet + address operator ) external view returns (string memory); /** * @notice Updates the socket for an operator. * @param operator The operator to set the socket for. - * @param operatorSet The operator set to set the socket for. * @param socket The socket to set for the operator. * @dev This function can only be called by the operator themselves. */ function updateSocket( address operator, - OperatorSet memory operatorSet, string memory socket ) external; } diff --git a/src/middlewareV2/registrar/modules/SocketRegistry.sol b/src/middlewareV2/registrar/modules/SocketRegistry.sol index d6d187e7..04243f66 100644 --- a/src/middlewareV2/registrar/modules/SocketRegistry.sol +++ b/src/middlewareV2/registrar/modules/SocketRegistry.sol @@ -14,44 +14,31 @@ abstract contract SocketRegistry is SocketRegistryStorage { /// @inheritdoc ISocketRegistry function getOperatorSocket( - address operator, - OperatorSet memory operatorSet + address operator ) external view returns (string memory) { - return _operatorToSocket[operator][operatorSet.key()]; + return _operatorToSocket[operator]; } /// @inheritdoc ISocketRegistry function updateSocket( address operator, - OperatorSet memory operatorSet, string memory socket ) external { require(msg.sender == operator, CallerNotOperator()); - _setOperatorSocket(operator, operatorSet, socket); + _setOperatorSocket(operator, socket); } /** * @notice Sets the socket for an operator. * @param operator The address of the operator to set the socket for. - * @param operatorSet The operator set to set the socket for. * @param socket The socket (any arbitrary string as deemed useful by an AVS) to set. - * @dev This function assumes a single socket per operator, for all operatorSets. + * @dev This function sets a single socket per operator, regardless of operatorSet. */ function _setOperatorSocket( address operator, - OperatorSet memory operatorSet, string memory socket ) internal { - _operatorToSocket[operator][operatorSet.key()] = socket; - emit OperatorSocketSet(operator, operatorSet, socket); - } - - /** - * @notice Deletes the socket for an operator. - * @param operator The address of the operator to delete the socket for. - */ - function _removeOperatorSocket(address operator, OperatorSet memory operatorSet) internal { - delete _operatorToSocket[operator][operatorSet.key()]; - emit OperatorSocketRemoved(operator, operatorSet); + _operatorToSocket[operator] = socket; + emit OperatorSocketSet(operator, socket); } } diff --git a/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol b/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol index 1f495767..5363fb6a 100644 --- a/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol +++ b/src/middlewareV2/registrar/modules/SocketRegistryStorage.sol @@ -14,9 +14,8 @@ abstract contract SocketRegistryStorage is ISocketRegistry { * */ - /// @notice A mapping from operator addresses to operatorSet to socker - mapping(address operator => mapping(bytes32 operatorSetKey => string operatorSocket)) internal - _operatorToSocket; + /// @notice A mapping from operator address to socket + mapping(address operator => string operatorSocket) internal _operatorToSocket; /** * @dev This empty reserved space is put in place to allow future versions to add new diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol index 5f722f6e..a44b5079 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol @@ -20,6 +20,8 @@ contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { ) AVSRegistrar(_avs, _allocationManager, _keyRegistrar) {} /// @notice Set the socket for the operator + /// @dev This function sets the socket even if the operator is already registered + /// @dev Operator's should make sure to always provide the socket when registering function _afterRegisterOperator( address operator, uint32[] calldata operatorSetIds, @@ -27,24 +29,8 @@ contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { ) internal override { super._afterRegisterOperator(operator, operatorSetIds, data); - // Decode data and validate length - string[] memory sockets = abi.decode(data, (string[])); - require(sockets.length == operatorSetIds.length, DataLengthMismatch()); - - for (uint32 i; i < operatorSetIds.length; ++i) { - _setOperatorSocket(operator, OperatorSet({avs: avs, id: operatorSetIds[i]}), sockets[i]); - } - } - - /// @notice Remove the socket for the operator - function _afterDeregisterOperator( - address operator, - uint32[] calldata operatorSetIds - ) internal override { - super._afterDeregisterOperator(operator, operatorSetIds); - - for (uint32 i; i < operatorSetIds.length; ++i) { - _removeOperatorSocket(operator, OperatorSet({avs: avs, id: operatorSetIds[i]})); - } + // Set operator socket + string memory socket = abi.decode(data, (string)); + _setOperatorSocket(operator, socket); } } diff --git a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol index 2d8a31e6..7cf8251f 100644 --- a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol @@ -33,21 +33,12 @@ contract AVSRegistrarSocketUnitTests is ); // Encode defaultSocker into data - string[] memory sockets = new string[](1); - sockets[0] = defaultSocket; - socketData = abi.encode(sockets); + socketData = abi.encode(defaultSocket); } function _registerOperator( uint32[] memory operatorSetIds ) internal { - // Generate an array of sockets - string[] memory sockets = new string[](operatorSetIds.length); - for (uint32 i; i < operatorSetIds.length; ++i) { - sockets[i] = defaultSocket; - } - socketData = abi.encode(sockets); - // Register operator _registerKey(defaultOperator, operatorSetIds); cheats.prank(address(allocationManagerMock)); @@ -78,19 +69,6 @@ contract AVSRegistrarSocketUnitTests_registerOperator is AVSRegistrarSocketUnitT ); } - function testFuzz_revert_dataLengthMismatch() public { - // Generate random operator set ids & register keys - uint32[] memory operatorSetIds = new uint32[](2); - operatorSetIds[0] = defaultOperatorSetId; - operatorSetIds[1] = defaultOperatorSetId + 1; - _registerKey(defaultOperator, operatorSetIds); - - // Register operator - cheats.expectRevert(DataLengthMismatch.selector); - cheats.prank(address(allocationManagerMock)); - avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); - } - function testFuzz_correctness( Randomness r ) public rand(r) { @@ -99,33 +77,18 @@ contract AVSRegistrarSocketUnitTests_registerOperator is AVSRegistrarSocketUnitT uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); _registerKey(defaultOperator, operatorSetIds); - // Generate an array of sockets - string[] memory sockets = new string[](numOperatorSetIds); - for (uint32 i; i < numOperatorSetIds; ++i) { - sockets[i] = defaultSocket; - } - socketData = abi.encode(sockets); - // Register operator - for (uint32 i; i < numOperatorSetIds; ++i) { - cheats.expectEmit(true, true, true, true); - emit OperatorSocketSet( - defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}), defaultSocket - ); - } + cheats.expectEmit(true, true, true, true); + emit OperatorSocketSet(defaultOperator, defaultSocket); cheats.expectEmit(true, true, true, true); emit OperatorRegistered(defaultOperator, operatorSetIds); cheats.prank(address(allocationManagerMock)); avsRegistrarWithSocket.registerOperator(defaultOperator, AVS, operatorSetIds, socketData); - // Check that the sockets are set - for (uint32 i; i < numOperatorSetIds; ++i) { - string memory socket = avsRegistrarWithSocket.getOperatorSocket( - defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}) - ); - assertEq(socket, sockets[i], "Socket mismatch"); - } + // Check that the socket is set + string memory socket = avsRegistrarWithSocket.getOperatorSocket(defaultOperator); + assertEq(socket, defaultSocket, "Socket mismatch"); } } @@ -151,28 +114,16 @@ contract AVSRegistrarSocketUnitTests_DeregisterOperator is AVSRegistrarSocketUni uint32 numOperatorSetIds = r.Uint32(1, 50); uint32[] memory operatorSetIds = r.Uint32Array(numOperatorSetIds, 0, type(uint32).max); - // Set sockets _registerOperator(operatorSetIds); - // Deregister operator - for (uint32 i; i < numOperatorSetIds; ++i) { - cheats.expectEmit(true, true, true, true); - emit OperatorSocketRemoved( - defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}) - ); - } cheats.expectEmit(true, true, true, true); emit OperatorDeregistered(defaultOperator, operatorSetIds); cheats.prank(address(allocationManagerMock)); avsRegistrarWithSocket.deregisterOperator(defaultOperator, AVS, operatorSetIds); - // Check that the sockets are removed - for (uint32 i; i < numOperatorSetIds; ++i) { - string memory socket = avsRegistrarWithSocket.getOperatorSocket( - defaultOperator, OperatorSet({avs: AVS, id: operatorSetIds[i]}) - ); - assertEq(socket, "", "Socket mismatch"); - } + // Check that the socket still exists + string memory socket = avsRegistrarWithSocket.getOperatorSocket(defaultOperator); + assertEq(socket, defaultSocket, "Socket mismatch"); } } @@ -187,9 +138,7 @@ contract AVSRegistrarSocketUnitTests_updateSocket is AVSRegistrarSocketUnitTests cheats.prank(notOperator); cheats.expectRevert(CallerNotOperator.selector); - avsRegistrarWithSocket.updateSocket( - defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}), defaultSocket - ); + avsRegistrarWithSocket.updateSocket(defaultOperator, defaultSocket); } function test_updateSocket() public { @@ -198,18 +147,12 @@ contract AVSRegistrarSocketUnitTests_updateSocket is AVSRegistrarSocketUnitTests string memory newSocket = "NewSocket"; cheats.expectEmit(true, true, true, true); - emit OperatorSocketSet( - defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}), newSocket - ); + emit OperatorSocketSet(defaultOperator, newSocket); cheats.prank(defaultOperator); - avsRegistrarWithSocket.updateSocket( - defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}), newSocket - ); + avsRegistrarWithSocket.updateSocket(defaultOperator, newSocket); // Check that the socket is updated - string memory socket = avsRegistrarWithSocket.getOperatorSocket( - defaultOperator, OperatorSet({avs: AVS, id: defaultOperatorSetId}) - ); + string memory socket = avsRegistrarWithSocket.getOperatorSocket(defaultOperator); assertEq(socket, newSocket, "Socket mismatch"); } } From 5bb905efb58af9ff2727224a60779bcebc51dbb3 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Fri, 30 May 2025 09:37:56 -0400 Subject: [PATCH 11/18] chore: format --- src/interfaces/ISocketRegistryV2.sol | 5 +---- src/middlewareV2/registrar/modules/SocketRegistry.sol | 10 ++-------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/interfaces/ISocketRegistryV2.sol b/src/interfaces/ISocketRegistryV2.sol index 8b611676..73b9256d 100644 --- a/src/interfaces/ISocketRegistryV2.sol +++ b/src/interfaces/ISocketRegistryV2.sol @@ -30,8 +30,5 @@ interface ISocketRegistry is ISocketRegistryErrors, ISocketRegistryEvents { * @param socket The socket to set for the operator. * @dev This function can only be called by the operator themselves. */ - function updateSocket( - address operator, - string memory socket - ) external; + function updateSocket(address operator, string memory socket) external; } diff --git a/src/middlewareV2/registrar/modules/SocketRegistry.sol b/src/middlewareV2/registrar/modules/SocketRegistry.sol index 04243f66..221d8707 100644 --- a/src/middlewareV2/registrar/modules/SocketRegistry.sol +++ b/src/middlewareV2/registrar/modules/SocketRegistry.sol @@ -20,10 +20,7 @@ abstract contract SocketRegistry is SocketRegistryStorage { } /// @inheritdoc ISocketRegistry - function updateSocket( - address operator, - string memory socket - ) external { + function updateSocket(address operator, string memory socket) external { require(msg.sender == operator, CallerNotOperator()); _setOperatorSocket(operator, socket); } @@ -34,10 +31,7 @@ abstract contract SocketRegistry is SocketRegistryStorage { * @param socket The socket (any arbitrary string as deemed useful by an AVS) to set. * @dev This function sets a single socket per operator, regardless of operatorSet. */ - function _setOperatorSocket( - address operator, - string memory socket - ) internal { + function _setOperatorSocket(address operator, string memory socket) internal { _operatorToSocket[operator] = socket; emit OperatorSocketSet(operator, socket); } From cbec7049508eb88e70d213f0381ce6aec5f38837 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Fri, 30 May 2025 12:39:21 -0400 Subject: [PATCH 12/18] chore: remove keytype --- src/middlewareV2/registrar/AVSRegistrar.sol | 2 +- src/middlewareV2/registrar/AVSRegistrarStorage.sol | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index 2cf6e9f4..148cb056 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -27,7 +27,7 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { address _avs, IAllocationManager _allocationManager, IKeyRegistrar _keyRegistrar - ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar, IKeyRegistrar.CurveType.BN254) { + ) AVSRegistrarStorage(_avs, _allocationManager, _keyRegistrar) { _disableInitializers(); } diff --git a/src/middlewareV2/registrar/AVSRegistrarStorage.sol b/src/middlewareV2/registrar/AVSRegistrarStorage.sol index 0e0f097f..ed76fe2c 100644 --- a/src/middlewareV2/registrar/AVSRegistrarStorage.sol +++ b/src/middlewareV2/registrar/AVSRegistrarStorage.sol @@ -22,22 +22,13 @@ abstract contract AVSRegistrarStorage is IAVSRegistrar, IAVSRegistrarInternal { /// @notice The allocation manager in EigenLayer core IAllocationManager public immutable allocationManager; - /// @notice The curve type that the Key Registrar is using - IKeyRegistrar.CurveType public immutable curveType; - /// @notice Pointer to the EigenLayer core Key Registrar IKeyRegistrar public immutable keyRegistrar; - constructor( - address _avs, - IAllocationManager _allocationManager, - IKeyRegistrar _keyRegistrar, - IKeyRegistrar.CurveType _curveType - ) { + constructor(address _avs, IAllocationManager _allocationManager, IKeyRegistrar _keyRegistrar) { avs = _avs; allocationManager = _allocationManager; keyRegistrar = _keyRegistrar; - curveType = _curveType; } /** From ace6fe18a488a2e86f3c6205acb2801241efebe1 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:24:36 -0400 Subject: [PATCH 13/18] chore: address nits --- .../registrar/AVSRegistrarStorage.sol | 2 +- ...list.sol => AVSRegistrarWithAllowlist.sol} | 0 .../AVSRegistrarAllowlistUnit.t.sol | 30 +++++++++++++------ 3 files changed, 22 insertions(+), 10 deletions(-) rename src/middlewareV2/registrar/presets/{AVSRegistrarAllowlist.sol => AVSRegistrarWithAllowlist.sol} (100%) diff --git a/src/middlewareV2/registrar/AVSRegistrarStorage.sol b/src/middlewareV2/registrar/AVSRegistrarStorage.sol index ed76fe2c..babe3dff 100644 --- a/src/middlewareV2/registrar/AVSRegistrarStorage.sol +++ b/src/middlewareV2/registrar/AVSRegistrarStorage.sol @@ -15,7 +15,7 @@ abstract contract AVSRegistrarStorage is IAVSRegistrar, IAVSRegistrarInternal { * */ - /// @notice The AVS that this registar is for + /// @notice The AVS that this registrar is for /// @dev In practice, the AVS address in EigenLayer core is address that initialized the Metadata URI. address public immutable avs; diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol similarity index 100% rename from src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol rename to src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol diff --git a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol index 997a26f3..7b9db438 100644 --- a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol @@ -3,10 +3,14 @@ pragma solidity ^0.8.27; import "./AVSRegistrarBase.t.sol"; import {AVSRegistrarWithAllowlist} from - "src/middlewareV2/registrar/presets/AVSRegistrarAllowlist.sol"; + "src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol"; import {IAllowlistErrors, IAllowlistEvents} from "src/interfaces/IAllowlist.sol"; -contract AVSRegistrarAllowlistUnitTests is AVSRegistrarBase, IAllowlistErrors, IAllowlistEvents { +contract AVSRegistrarWithAllowlistUnitTests is + AVSRegistrarBase, + IAllowlistErrors, + IAllowlistEvents +{ AVSRegistrarWithAllowlist public avsRegistrarWithAllowlist; address public allowlistAdmin = address(this); @@ -42,7 +46,7 @@ contract AVSRegistrarAllowlistUnitTests is AVSRegistrarBase, IAllowlistErrors, I } } -contract AVSRegistrarAllowlistUnitTests_initialize is AVSRegistrarAllowlistUnitTests { +contract AVSRegistrarWithAllowlistUnitTests_initialize is AVSRegistrarWithAllowlistUnitTests { function test_initialization() public view { // Check the admin is set assertEq( @@ -56,7 +60,9 @@ contract AVSRegistrarAllowlistUnitTests_initialize is AVSRegistrarAllowlistUnitT } } -contract AVSRegistrarAllowlistUnitTests_addOperatorToAllowlist is AVSRegistrarAllowlistUnitTests { +contract AVSRegistrarWithAllowlistUnitTests_addOperatorToAllowlist is + AVSRegistrarWithAllowlistUnitTests +{ using ArrayLib for *; function testFuzz_revert_notOwner( @@ -112,8 +118,8 @@ contract AVSRegistrarAllowlistUnitTests_addOperatorToAllowlist is AVSRegistrarAl } } -contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is - AVSRegistrarAllowlistUnitTests +contract AVSRegistrarWithAllowlistUnitTests_removeOperatorFromAllowlist is + AVSRegistrarWithAllowlistUnitTests { using ArrayLib for *; @@ -165,7 +171,9 @@ contract AVSRegistrarAllowlistUnitTests_removeOperatorFromAllowlist is } } -contract AVSRegistrarAllowistUnitTest_getRegisteredOperators is AVSRegistrarAllowlistUnitTests { +contract AVSRegistrarAllowistUnitTest_getRegisteredOperators is + AVSRegistrarWithAllowlistUnitTests +{ using ArrayLib for *; function testFuzz_correctness( @@ -203,7 +211,9 @@ contract AVSRegistrarAllowistUnitTest_getRegisteredOperators is AVSRegistrarAllo } } -contract AVSRegistrarAllowListUnitTests_registerOperator is AVSRegistrarAllowlistUnitTests { +contract AVSRegistrarWithAllowlistUnitTests_registerOperator is + AVSRegistrarWithAllowlistUnitTests +{ using ArrayLib for *; function testFuzz_revert_notAllocationManager( @@ -258,7 +268,9 @@ contract AVSRegistrarAllowListUnitTests_registerOperator is AVSRegistrarAllowlis } } -contract AVSRegistrarAllowListUnitTests_deregisterOperator is AVSRegistrarAllowlistUnitTests { +contract AVSRegistrarWithAllowlistUnitTests_deregisterOperator is + AVSRegistrarWithAllowlistUnitTests +{ using ArrayLib for *; function testFuzz_revert_notAllocationManager( From f2956ed0dc9e4f155a7457cf2707988abd799c23 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:28:15 -0400 Subject: [PATCH 14/18] chore: update core module --- lib/eigenlayer-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 97d61830..07cd152a 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 97d61830d34b55ae39a650cf15b72896dd75a675 +Subproject commit 07cd152a20518c8845190c76807a68075523b03c From 8e6ebeef57aeeff520bbca27af86d1b18628eea2 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 2 Jun 2025 12:55:06 -0400 Subject: [PATCH 15/18] chore: push --- src/middlewareV2/registrar/AVSRegistrar.sol | 21 ++------ test/mocks/KeyRegistrarMock.sol | 60 ++++++++++----------- 2 files changed, 33 insertions(+), 48 deletions(-) diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index 148cb056..c81438a0 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -8,9 +8,11 @@ import { OperatorSetLib, OperatorSet } from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import { + IKeyRegistrarTypes, IKeyRegistrar +} from "eigenlayer-contracts/src/contracts/interfaces//IKeyRegistrar.sol"; import {AVSRegistrarStorage} from "./AVSRegistrarStorage.sol"; -import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -56,9 +58,6 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { ) external virtual onlyAllocationManager { _beforeDeregisterOperator(operator, operatorSetIds); - // Remove operator keys from the key registrar - _removeOperatorKeys(operator, operatorSetIds); - _afterDeregisterOperator(operator, operatorSetIds); emit OperatorDeregistered(operator, operatorSetIds); @@ -86,19 +85,7 @@ contract AVSRegistrar is Initializable, AVSRegistrarStorage { function _validateOperatorKeys(address operator, uint32[] calldata operatorSetIds) internal { for (uint32 i = 0; i < operatorSetIds.length; i++) { OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); - require(keyRegistrar.checkAndUpdateKey(operatorSet, operator), KeyNotRegistered()); - } - } - - /** - * @notice Removes the operator keys from the key registrar - * @param operator The operator to remove - * @param operatorSetIds The operator sets to remove - */ - function _removeOperatorKeys(address operator, uint32[] calldata operatorSetIds) internal { - for (uint32 i = 0; i < operatorSetIds.length; i++) { - OperatorSet memory operatorSet = OperatorSet({avs: avs, id: operatorSetIds[i]}); - keyRegistrar.removeKey(operatorSet, operator); + require(keyRegistrar.checkKey(operatorSet, operator), KeyNotRegistered()); } } diff --git a/test/mocks/KeyRegistrarMock.sol b/test/mocks/KeyRegistrarMock.sol index af33930c..57a22895 100644 --- a/test/mocks/KeyRegistrarMock.sol +++ b/test/mocks/KeyRegistrarMock.sol @@ -1,37 +1,35 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.27; +// // SPDX-License-Identifier: BUSL-1.1 +// pragma solidity ^0.8.27; -import "src/interfaces/IKeyRegistrar.sol"; +// import { +// IKeyRegistrarTypes, IKeyRegistrar +// } from "eigenlayer-contracts/src/contracts/interfaces//IKeyRegistrar.sol"; -contract KeyRegistrarMock is IKeyRegistrar { - using OperatorSetLib for OperatorSet; +// contract KeyRegistrarMock is IKeyRegistrar { +// using OperatorSetLib for OperatorSet; - mapping(bytes32 operatorSetKey => mapping(address => bool)) internal _operatorRegistered; +// mapping(bytes32 operatorSetKey => mapping(address => bool)) internal _operatorRegistered; - function setIsRegistered( - address operator, - OperatorSet calldata operatorSet, - bool _isRegistered - ) external { - bytes32 operatorSetKey = operatorSet.key(); - _operatorRegistered[operatorSetKey][operator] = _isRegistered; - } +// function setIsRegistered( +// address operator, +// OperatorSet calldata operatorSet, +// bool _isRegistered +// ) external { +// bytes32 operatorSetKey = operatorSet.key(); +// _operatorRegistered[operatorSetKey][operator] = _isRegistered; +// } - function isRegistered( - OperatorSet calldata operatorSet, - address operator - ) external view returns (bool) { - return _operatorRegistered[operatorSet.key()][operator]; - } +// function isRegistered( +// OperatorSet calldata operatorSet, +// address operator +// ) external view returns (bool) { +// return _operatorRegistered[operatorSet.key()][operator]; +// } - function checkAndUpdateKey( - OperatorSet calldata operatorSet, - address operator - ) external view returns (bool) { - return _operatorRegistered[operatorSet.key()][operator]; - } - - function removeKey(OperatorSet calldata operatorSet, address operator) external { - _operatorRegistered[operatorSet.key()][operator] = false; - } -} +// function checkKey( +// OperatorSet calldata operatorSet, +// address operator +// ) external view returns (bool) { +// return _operatorRegistered[operatorSet.key()][operator]; +// } +// } From 3c6ade50c5326d6341bf07f6c14be5f9c65d8d5a Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:15:22 -0400 Subject: [PATCH 16/18] chore: use core key registrar --- src/middlewareV2/registrar/AVSRegistrar.sol | 2 +- .../registrar/AVSRegistrarStorage.sol | 2 +- .../presets/AVSRegistrarAsIdentifier.sol | 2 +- .../presets/AVSRegistrarWithAllowlist.sol | 2 +- .../presets/AVSRegistrarWithSocket.sol | 2 +- test/mocks/KeyRegistrarMock.sol | 132 +++++++++++++----- .../AVSRegistrarAllowlistUnit.t.sol | 1 + test/unit/middlewareV2/AVSRegistrarBase.t.sol | 2 +- .../middlewareV2/AVSRegistrarSocketUnit.t.sol | 1 + test/unit/middlewareV2/AVSRegistrarUnit.t.sol | 1 + 10 files changed, 106 insertions(+), 41 deletions(-) diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index c81438a0..0ecbf007 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -10,7 +10,7 @@ import { } from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; import { IKeyRegistrarTypes, IKeyRegistrar -} from "eigenlayer-contracts/src/contracts/interfaces//IKeyRegistrar.sol"; +} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import {AVSRegistrarStorage} from "./AVSRegistrarStorage.sol"; diff --git a/src/middlewareV2/registrar/AVSRegistrarStorage.sol b/src/middlewareV2/registrar/AVSRegistrarStorage.sol index babe3dff..0f25b9ca 100644 --- a/src/middlewareV2/registrar/AVSRegistrarStorage.sol +++ b/src/middlewareV2/registrar/AVSRegistrarStorage.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.27; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; -import {IKeyRegistrar} from "../../interfaces/IKeyRegistrar.sol"; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import {IAVSRegistrarInternal} from "../../interfaces/IAVSRegistrarInternal.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol index 2f8c7691..9abacc0d 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarAsIdentifier.sol @@ -5,8 +5,8 @@ import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; -import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; import {AVSRegistrar} from "../AVSRegistrar.sol"; import {SocketRegistry} from "../modules/SocketRegistry.sol"; diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol index dede005f..3965b26a 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.27; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; -import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; import {AVSRegistrar} from "../AVSRegistrar.sol"; import {Allowlist} from "../modules/Allowlist.sol"; import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol index a44b5079..f8928e87 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.27; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; -import {IKeyRegistrar} from "../../../interfaces/IKeyRegistrar.sol"; import {AVSRegistrar} from "../AVSRegistrar.sol"; import {SocketRegistry} from "../modules/SocketRegistry.sol"; import { diff --git a/test/mocks/KeyRegistrarMock.sol b/test/mocks/KeyRegistrarMock.sol index 57a22895..22a9ca2f 100644 --- a/test/mocks/KeyRegistrarMock.sol +++ b/test/mocks/KeyRegistrarMock.sol @@ -1,35 +1,97 @@ -// // SPDX-License-Identifier: BUSL-1.1 -// pragma solidity ^0.8.27; - -// import { -// IKeyRegistrarTypes, IKeyRegistrar -// } from "eigenlayer-contracts/src/contracts/interfaces//IKeyRegistrar.sol"; - -// contract KeyRegistrarMock is IKeyRegistrar { -// using OperatorSetLib for OperatorSet; - -// mapping(bytes32 operatorSetKey => mapping(address => bool)) internal _operatorRegistered; - -// function setIsRegistered( -// address operator, -// OperatorSet calldata operatorSet, -// bool _isRegistered -// ) external { -// bytes32 operatorSetKey = operatorSet.key(); -// _operatorRegistered[operatorSetKey][operator] = _isRegistered; -// } - -// function isRegistered( -// OperatorSet calldata operatorSet, -// address operator -// ) external view returns (bool) { -// return _operatorRegistered[operatorSet.key()][operator]; -// } - -// function checkKey( -// OperatorSet calldata operatorSet, -// address operator -// ) external view returns (bool) { -// return _operatorRegistered[operatorSet.key()][operator]; -// } -// } +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import { + IKeyRegistrarTypes, IKeyRegistrar, BN254 +} from "eigenlayer-contracts/src/contracts/interfaces//IKeyRegistrar.sol"; +import { + OperatorSetLib, OperatorSet +} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import {ISemVerMixin} from "eigenlayer-contracts/src/contracts/interfaces/ISemVerMixin.sol"; + +contract KeyRegistrarMock is IKeyRegistrar { + using OperatorSetLib for OperatorSet; + + mapping(bytes32 operatorSetKey => mapping(address => bool)) internal _operatorRegistered; + + function setIsRegistered( + address operator, + OperatorSet calldata operatorSet, + bool _isRegistered + ) external { + bytes32 operatorSetKey = operatorSet.key(); + _operatorRegistered[operatorSetKey][operator] = _isRegistered; + } + + function checkKey( + OperatorSet calldata operatorSet, + address operator + ) external view returns (bool) { + return _operatorRegistered[operatorSet.key()][operator]; + } + + function initialize(address initialOwner) external {} + + function configureOperatorSet( + OperatorSet memory operatorSet, + CurveType curveType + ) external {} + + function registerKey( + address operator, + OperatorSet memory operatorSet, + bytes calldata pubkey, + bytes calldata signature + ) external {} + + function deregisterKey( + address operator, + OperatorSet memory operatorSet + ) external {} + + function isRegistered( + OperatorSet memory operatorSet, + address operator + ) external pure returns (bool) {} + + function getOperatorSetConfig( + OperatorSet memory operatorSet + ) external pure returns (OperatorSetConfig memory) {} + + function getBN254Key( + OperatorSet memory operatorSet, + address operator + ) external view returns (BN254.G1Point memory g1Point, BN254.G2Point memory g2Point) {} + + /** + * @notice Gets the ECDSA public key for an operator with a specific operator set + * @param operatorSet The operator set to get the key for + * @param operator Address of the operator + * @return pubkey The ECDSA public key + */ + function getECDSAKey( + OperatorSet memory operatorSet, + address operator + ) external pure returns (bytes memory) {} + + function isKeyGloballyRegistered(bytes32 keyHash) external view returns (bool) {} + + function getKeyHash( + OperatorSet memory operatorSet, + address operator + ) external pure returns (bytes32) {} + + function verifyBN254Signature( + bytes32 messageHash, + bytes memory signature, + BN254.G1Point memory pubkeyG1, + BN254.G2Point memory pubkeyG2 + ) external pure {} + + function version() external pure returns (string memory) { + return "v0.0.1"; + } + + receive() external payable {} + fallback() external payable {} +} diff --git a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol index 7b9db438..3391bf08 100644 --- a/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarAllowlistUnit.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.27; import "./AVSRegistrarBase.t.sol"; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import {AVSRegistrarWithAllowlist} from "src/middlewareV2/registrar/presets/AVSRegistrarWithAllowlist.sol"; import {IAllowlistErrors, IAllowlistEvents} from "src/interfaces/IAllowlist.sol"; diff --git a/test/unit/middlewareV2/AVSRegistrarBase.t.sol b/test/unit/middlewareV2/AVSRegistrarBase.t.sol index 075b912d..36c5a8e0 100644 --- a/test/unit/middlewareV2/AVSRegistrarBase.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarBase.t.sol @@ -8,7 +8,7 @@ import {IAVSRegistrarErrors, IAVSRegistrarEvents} from "src/interfaces/IAVSRegis import {AVSRegistrar} from "src/middlewareV2/registrar/AVSRegistrar.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IKeyRegistrar} from "src/interfaces/IKeyRegistrar.sol"; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import { OperatorSet, OperatorSetLib diff --git a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol index 7cf8251f..16c9b28b 100644 --- a/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarSocketUnit.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import "./AVSRegistrarBase.t.sol"; import {AVSRegistrarWithSocket} from "src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol"; import {ISocketRegistryEvents, ISocketRegistryErrors} from "src/interfaces/ISocketRegistryV2.sol"; diff --git a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol index 74c9e110..f545ad61 100644 --- a/test/unit/middlewareV2/AVSRegistrarUnit.t.sol +++ b/test/unit/middlewareV2/AVSRegistrarUnit.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; +import {IKeyRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import "./AVSRegistrarBase.t.sol"; contract AVSRegistrarUnitTests is AVSRegistrarBase { From c9daea1e6f0d2f2e3d3bfbc0336a8f4da51a7dff Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:21:20 -0400 Subject: [PATCH 17/18] chore: format --- src/middlewareV2/registrar/AVSRegistrar.sol | 3 +- test/mocks/KeyRegistrarMock.sol | 33 +++++++++++---------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/middlewareV2/registrar/AVSRegistrar.sol b/src/middlewareV2/registrar/AVSRegistrar.sol index 0ecbf007..24a75a67 100644 --- a/src/middlewareV2/registrar/AVSRegistrar.sol +++ b/src/middlewareV2/registrar/AVSRegistrar.sol @@ -9,7 +9,8 @@ import { OperatorSet } from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; import { - IKeyRegistrarTypes, IKeyRegistrar + IKeyRegistrarTypes, + IKeyRegistrar } from "eigenlayer-contracts/src/contracts/interfaces/IKeyRegistrar.sol"; import {AVSRegistrarStorage} from "./AVSRegistrarStorage.sol"; diff --git a/test/mocks/KeyRegistrarMock.sol b/test/mocks/KeyRegistrarMock.sol index 22a9ca2f..2dfbf62f 100644 --- a/test/mocks/KeyRegistrarMock.sol +++ b/test/mocks/KeyRegistrarMock.sol @@ -2,10 +2,13 @@ pragma solidity ^0.8.27; import { - IKeyRegistrarTypes, IKeyRegistrar, BN254 + IKeyRegistrarTypes, + IKeyRegistrar, + BN254 } from "eigenlayer-contracts/src/contracts/interfaces//IKeyRegistrar.sol"; import { - OperatorSetLib, OperatorSet + OperatorSetLib, + OperatorSet } from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; import {ISemVerMixin} from "eigenlayer-contracts/src/contracts/interfaces/ISemVerMixin.sol"; @@ -30,13 +33,12 @@ contract KeyRegistrarMock is IKeyRegistrar { return _operatorRegistered[operatorSet.key()][operator]; } - function initialize(address initialOwner) external {} - - function configureOperatorSet( - OperatorSet memory operatorSet, - CurveType curveType + function initialize( + address initialOwner ) external {} + function configureOperatorSet(OperatorSet memory operatorSet, CurveType curveType) external {} + function registerKey( address operator, OperatorSet memory operatorSet, @@ -44,11 +46,8 @@ contract KeyRegistrarMock is IKeyRegistrar { bytes calldata signature ) external {} - function deregisterKey( - address operator, - OperatorSet memory operatorSet - ) external {} - + function deregisterKey(address operator, OperatorSet memory operatorSet) external {} + function isRegistered( OperatorSet memory operatorSet, address operator @@ -59,7 +58,7 @@ contract KeyRegistrarMock is IKeyRegistrar { ) external pure returns (OperatorSetConfig memory) {} function getBN254Key( - OperatorSet memory operatorSet, + OperatorSet memory operatorSet, address operator ) external view returns (BN254.G1Point memory g1Point, BN254.G2Point memory g2Point) {} @@ -70,11 +69,13 @@ contract KeyRegistrarMock is IKeyRegistrar { * @return pubkey The ECDSA public key */ function getECDSAKey( - OperatorSet memory operatorSet, + OperatorSet memory operatorSet, address operator ) external pure returns (bytes memory) {} - function isKeyGloballyRegistered(bytes32 keyHash) external view returns (bool) {} + function isKeyGloballyRegistered( + bytes32 keyHash + ) external view returns (bool) {} function getKeyHash( OperatorSet memory operatorSet, @@ -93,5 +94,5 @@ contract KeyRegistrarMock is IKeyRegistrar { } receive() external payable {} - fallback() external payable {} + fallback() external payable {} } From 320c6cbc37a99fc881409b86ce5bf6ee44f7d1f8 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:34:17 -0400 Subject: [PATCH 18/18] chore: add clarification comment --- src/middlewareV2/registrar/modules/SocketRegistry.sol | 1 + src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/middlewareV2/registrar/modules/SocketRegistry.sol b/src/middlewareV2/registrar/modules/SocketRegistry.sol index 221d8707..f255dbe9 100644 --- a/src/middlewareV2/registrar/modules/SocketRegistry.sol +++ b/src/middlewareV2/registrar/modules/SocketRegistry.sol @@ -9,6 +9,7 @@ import { } from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; /// @notice A module that allows for the setting and removal of operator sockets +/// @dev This contract assumes a single socket per operator abstract contract SocketRegistry is SocketRegistryStorage { using OperatorSetLib for OperatorSet; diff --git a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol index f8928e87..f8273c8c 100644 --- a/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol +++ b/src/middlewareV2/registrar/presets/AVSRegistrarWithSocket.sol @@ -21,7 +21,7 @@ contract AVSRegistrarWithSocket is AVSRegistrar, SocketRegistry { /// @notice Set the socket for the operator /// @dev This function sets the socket even if the operator is already registered - /// @dev Operator's should make sure to always provide the socket when registering + /// @dev Operators should make sure to always provide the socket when registering function _afterRegisterOperator( address operator, uint32[] calldata operatorSetIds,