Skip to content

Commit

Permalink
feat: require avs register metadata in allocation manager (#1025)
Browse files Browse the repository at this point in the history
require avs register metadata in allocation manager before they can
create operatorset

---------

Co-authored-by: clandestine.eth <[email protected]>
  • Loading branch information
bowenli86 and 0xClandestine authored Feb 19, 2025
1 parent 998e11d commit ed938f7
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 79 deletions.
180 changes: 102 additions & 78 deletions docs/core/AllocationManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Libraries and Mixins:

## Overview

The `AllocationManager` manages registration and deregistration of operators to operator sets, handles allocation and slashing of operators' slashable stake, and is the entry point an AVS uses to slash an operator. The `AllocationManager's` responsibilities are broken down into the following concepts:
The `AllocationManager` manages AVS metadata registration, registration and deregistration of operators to operator sets, handles allocation and slashing of operators' slashable stake, and is the entry point an AVS uses to slash an operator. The `AllocationManager's` responsibilities are broken down into the following concepts:

* [AVS Metadata](#avs-metadata)
* [Operator Sets](#operator-sets)
* [Allocations and Slashing](#allocations-and-slashing)
* [Config](#config)
Expand All @@ -38,6 +40,104 @@ The `AllocationManager` manages registration and deregistration of operators to

---

## AVS Metadata

AVSs must register their metadata to declare themselves who they are as the first step, before they can create operator sets or register operators into operator sets. `AllocationManager` keeps track of AVSs that have registered metadata.

**Methods:**
* [`updateAVSMetadataURI`](#updateavsmetadatauri)


#### `updateAVSMetadataURI`

```solidity
/**
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
*
* @param metadataURI The URI for metadata associated with an AVS.
*
* @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
*/
function updateAVSMetadataURI(
address avs,
string calldata metadataURI
)
external
checkCanCall(avs)
```

_Note: this method can be called directly by an AVS, or by a caller authorized by the AVS. See [`PermissionController.md`](../permissions/PermissionController.md) for details._

Below is the format AVSs should use when updating their metadata URI initially. This is not validated onchain.

```json
{
"name": "AVS",
"website": "https.avs.xyz/",
"description": "Some description about",
"logo": "http://github.com/logo.png",
"twitter": "https://twitter.com/avs",
}
```


Later on, once AVSs have created operator sets, content in their metadata URI can be updated subsequently.

```json
{
"name": "AVS",
"website": "https.avs.xyz/",
"description": "Some description about",
"logo": "http://github.com/logo.png",
"twitter": "https://twitter.com/avs",
"operatorSets": [
{
"name": "ETH Set",
"id": "1", // Note: we use this param to match the opSet id in the Allocation Manager
"description": "The ETH operatorSet for AVS",
"software": [
{
"name": "NetworkMonitor",
"description": "",
"url": "https://link-to-binary-or-github.com"
},
{
"name": "ValidatorClient",
"description": "",
"url": "https://link-to-binary-or-github.com"
}
],
"slashingConditions": ["Condition A", "Condition B"]
},
{
"name": "EIGEN Set",
"id": "2", // Note: we use this param to match the opSet id in the Allocation Manager
"description": "The EIGEN operatorSet for AVS",
"software": [
{
"name": "NetworkMonitor",
"description": "",
"url": "https://link-to-binary-or-github.com"
},
{
"name": "ValidatorClient",
"description": "",
"url": "https://link-to-binary-or-github.com"
}
],
"slashingConditions": ["Condition A", "Condition B"]
}
]
}
```

*Effects*:
* Emits an `AVSMetadataURIUpdated` event for use in offchain services

*Requirements*:
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))


## Operator Sets

Operator sets, as described in [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md#operator-sets), are useful for AVSs to configure operator groupings which can be assigned different tasks, rewarded based on their strategy allocations, and slashed according to different rules. Operator sets are defined in [`libraries/OperatorSetLib.sol`](../../src/contracts/libraries/OperatorSetLib.sol):
Expand Down Expand Up @@ -145,6 +245,7 @@ Optionally, the `avs` can provide a list of `strategies`, specifying which strat

*Requirements*:
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
* AVS MUST have registered metadata
* For each `CreateSetParams` element:
* Each `params.operatorSetId` MUST NOT already exist in `_operatorSets[avs]`

Expand Down Expand Up @@ -665,7 +766,6 @@ Once slashing is processed for a strategy, [slashed stake is burned via the `Del
**Methods:**
* [`setAllocationDelay`](#setallocationdelay)
* [`setAVSRegistrar`](#setavsregistrar)
* [`updateAVSMetadataURI`](#updateavsmetadatauri)

#### `setAllocationDelay`

Expand Down Expand Up @@ -756,79 +856,3 @@ Note that when an operator registers, registration will FAIL if the call to `IAV

*Requirements*:
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))

#### `updateAVSMetadataURI`

```solidity
/**
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
*
* @param metadataURI The URI for metadata associated with an AVS.
*
* @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
*/
function updateAVSMetadataURI(
address avs,
string calldata metadataURI
)
external
checkCanCall(avs)
```

_Note: this method can be called directly by an AVS, or by a caller authorized by the AVS. See [`PermissionController.md`](../permissions/PermissionController.md) for details._

Below is the format AVSs should use when updating their metadata URI. This is not validated onchain.

```json
{
"name": "AVS",
"website": "https.avs.xyz/",
"description": "Some description about",
"logo": "http://github.com/logo.png",
"twitter": "https://twitter.com/avs",
"operatorSets": [
{
"name": "ETH Set",
"id": "1", // Note: we use this param to match the opSet id in the Allocation Manager
"description": "The ETH operatorSet for AVS",
"software": [
{
"name": "NetworkMonitor",
"description": "",
"url": "https://link-to-binary-or-github.com"
},
{
"name": "ValidatorClient",
"description": "",
"url": "https://link-to-binary-or-github.com"
}
],
"slashingConditions": ["Condition A", "Condition B"]
},
{
"name": "EIGEN Set",
"id": "2", // Note: we use this param to match the opSet id in the Allocation Manager
"description": "The EIGEN operatorSet for AVS",
"software": [
{
"name": "NetworkMonitor",
"description": "",
"url": "https://link-to-binary-or-github.com"
},
{
"name": "ValidatorClient",
"description": "",
"url": "https://link-to-binary-or-github.com"
}
],
"slashingConditions": ["Condition A", "Condition B"]
}
]
}
```

*Effects*:
* Emits an `AVSMetadataURIUpdated` event for use in offchain services

*Requirements*:
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
7 changes: 7 additions & 0 deletions src/contracts/core/AllocationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,18 @@ contract AllocationManager is

/// @inheritdoc IAllocationManager
function updateAVSMetadataURI(address avs, string calldata metadataURI) external checkCanCall(avs) {
if (!_avsRegisteredMetadata[avs]) {
_avsRegisteredMetadata[avs] = true;
}

emit AVSMetadataURIUpdated(avs, metadataURI);
}

/// @inheritdoc IAllocationManager
function createOperatorSets(address avs, CreateSetParams[] calldata params) external checkCanCall(avs) {
// Check that the AVS exists and has registered metadata
require(_avsRegisteredMetadata[avs], NonexistentAVSMetadata());

for (uint256 i = 0; i < params.length; i++) {
OperatorSet memory operatorSet = OperatorSet(avs, params[i].operatorSetId);

Expand Down
6 changes: 5 additions & 1 deletion src/contracts/core/AllocationManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ abstract contract AllocationManagerStorage is IAllocationManager {
/// These must be completed in order to free up magnitude for future allocation
mapping(address operator => mapping(IStrategy strategy => DoubleEndedQueue.Bytes32Deque)) internal deallocationQueue;

/// @dev Lists the AVSs who has registered metadata and claimed itself as an AVS
/// @notice bool is not used and is always true if the avs has registered metadata
mapping(address avs => bool) internal _avsRegisteredMetadata;

// Construction

constructor(IDelegationManager _delegation, uint32 _DEALLOCATION_DELAY, uint32 _ALLOCATION_CONFIGURATION_DELAY) {
Expand All @@ -102,5 +106,5 @@ abstract contract AllocationManagerStorage is IAllocationManager {
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[37] private __gap;
uint256[36] private __gap;
}
2 changes: 2 additions & 0 deletions src/contracts/interfaces/IAllocationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ interface IAllocationManagerErrors {

/// @dev Thrown when an invalid operator is provided.
error InvalidOperator();
/// @dev Thrown when an invalid avs whose metadata is not registered is provided.
error NonexistentAVSMetadata();
/// @dev Thrown when an operator's allocation delay has yet to be set.
error UninitializedAllocationDelay();
/// @dev Thrown when attempting to slash an operator when they are not slashable.
Expand Down
1 change: 1 addition & 0 deletions src/test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ abstract contract IntegrationBase is IntegrationDeployer, TypeImporter {
function _newRandomAVS() internal returns (AVS avs, OperatorSet[] memory operatorSets) {
string memory avsName = string.concat("avs", numAVSs.toString());
avs = _genRandAVS(avsName);
avs.updateAVSMetadataURI("https://example.com");
operatorSets = avs.createOperatorSets(_randomStrategies());
++numAVSs;
}
Expand Down
12 changes: 12 additions & 0 deletions src/test/integration/users/AVS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ contract AVS is Logger, IAllocationManagerTypes, IAVSRegistrar {
/// AllocationManager
/// -----------------------------------------------------------------------

function updateAVSMetadataURI(
string memory uri
) public createSnapshot {
print.method("updateAVSMetadataURI");

console.log("Setting AVS metadata URI to: %s", uri);
_tryPrankAppointee_AllocationManager(IAllocationManager.updateAVSMetadataURI.selector);
allocationManager.updateAVSMetadataURI(address(this), uri);

print.gasUsed();
}

function createOperatorSets(
IStrategy[][] memory strategies
) public createSnapshot returns (OperatorSet[] memory operatorSets) {
Expand Down
14 changes: 14 additions & 0 deletions src/test/unit/AllocationManagerUnit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ contract AllocationManagerUnitTests is EigenLayerUnitTestSetup, IAllocationManag
defaultStrategies = strategyMock.toArray();
defaultOperatorSet = OperatorSet(defaultAVS, 0);

cheats.prank(defaultAVS);
allocationManager.updateAVSMetadataURI(defaultAVS, "https://example.com");


_createOperatorSet(defaultOperatorSet, defaultStrategies);
_registerOperator(defaultOperator);
_setAllocationDelay(defaultOperator, DEFAULT_OPERATOR_ALLOCATION_DELAY);
Expand Down Expand Up @@ -3830,13 +3834,23 @@ contract AllocationManagerUnitTests_createOperatorSets is AllocationManagerUnitT
allocationManager.createOperatorSets(defaultAVS, CreateSetParams(defaultOperatorSet.id, defaultStrategies).toArray());
}

function testRevert_createOperatorSets_NonexistentAVSMetadata(Randomness r) public rand(r) {
address avs = r.Address();
cheats.expectRevert(NonexistentAVSMetadata.selector);
cheats.prank(avs);
allocationManager.createOperatorSets(avs, CreateSetParams(defaultOperatorSet.id, defaultStrategies).toArray());
}

function testFuzz_createOperatorSets_Correctness(
Randomness r
) public rand(r) {
address avs = r.Address();
uint256 numOpSets = r.Uint256(1, FUZZ_MAX_OP_SETS);
uint256 numStrategies = r.Uint256(1, FUZZ_MAX_STRATS);

cheats.prank(avs);
allocationManager.updateAVSMetadataURI(avs, "https://example.com");

CreateSetParams[] memory createSetParams = new CreateSetParams[](numOpSets);

for (uint256 i; i < numOpSets; ++i) {
Expand Down

0 comments on commit ed938f7

Please sign in to comment.