Skip to content

Commit

Permalink
add Strategy <> OperatorSet mapping in storage, and APIs and events (#…
Browse files Browse the repository at this point in the history
…814)

* feat: add strategy to operator set mapping with corresponding functions and events

* fix: update

* fix: remove pagination of getStrategiesInOperatorSet

* fix: update

* fix: compiles

* fix: add checks

* fix: address -> IStrategy

* fix: storage gap

---------

Co-authored-by: gpsanant <[email protected]>
  • Loading branch information
bowenli86 and gpsanant authored Oct 12, 2024
1 parent 47a4ff5 commit 5b4d415
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
44 changes: 44 additions & 0 deletions src/contracts/core/AVSDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,34 @@ contract AVSDirectory is
_deregisterFromOperatorSets(msg.sender, operator, operatorSetIds);
}

/// @inheritdoc IAVSDirectory
function addStrategiesToOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external override {
OperatorSet memory operatorSet = OperatorSet(msg.sender, operatorSetId);
require(isOperatorSet[msg.sender][operatorSetId], InvalidOperatorSet());
bytes32 encodedOperatorSet = _encodeOperatorSet(operatorSet);
for (uint256 i = 0; i < strategies.length; i++) {
require(_operatorSetStrategies[encodedOperatorSet].add(address(strategies[i])), StrategyAlreadyInOperatorSet());
emit StrategyAddedToOperatorSet(operatorSet, strategies[i]);
}
}

/// @inheritdoc IAVSDirectory
function removeStrategiesFromOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external override {
OperatorSet memory operatorSet = OperatorSet(msg.sender, operatorSetId);
require(isOperatorSet[msg.sender][operatorSetId], InvalidOperatorSet());
bytes32 encodedOperatorSet = _encodeOperatorSet(operatorSet);
for (uint256 i = 0; i < strategies.length; i++) {
require(_operatorSetStrategies[encodedOperatorSet].remove(address(strategies[i])), StrategyNotInOperatorSet());
emit StrategyRemovedFromOperatorSet(operatorSet, strategies[i]);
}
}

/**
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
*
Expand Down Expand Up @@ -451,6 +479,22 @@ contract AVSDirectory is
}
}

/**
* @notice Returns an array of strategies in the operatorSet.
* @param operatorSet The operatorSet to query.
*/
function getStrategiesInOperatorSet(
OperatorSet memory operatorSet
) external view returns (IStrategy[] memory strategies) {
bytes32 encodedOperatorSet = _encodeOperatorSet(operatorSet);
uint256 length = _operatorSetStrategies[encodedOperatorSet].length();

strategies = new IStrategy[](length);
for (uint256 i; i < length; ++i) {
strategies[i] = IStrategy(_operatorSetStrategies[encodedOperatorSet].at(i));
}
}

/**
* @notice Returns the number of operators registered to an operatorSet.
* @param operatorSet The operatorSet to get the member count for
Expand Down
4 changes: 4 additions & 0 deletions src/contracts/core/AVSDirectoryStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ abstract contract AVSDirectoryStorage is IAVSDirectory {
/// @dev Each key is formatted as such: bytes32(abi.encodePacked(avs, uint96(operatorSetId)))
mapping(bytes32 => EnumerableSet.AddressSet) internal _operatorSetMembers;

/// @notice Mapping: operatorSet => List of strategies that the operatorSet contains
/// @dev Each key is formatted as such: bytes32(abi.encodePacked(avs, uint96(operatorSetId)))
mapping(bytes32 => EnumerableSet.AddressSet) internal _operatorSetStrategies;

/// @notice Mapping: operator => avs => operatorSetId => operator registration status
mapping(address => mapping(address => mapping(uint32 => OperatorSetRegistrationStatus))) public operatorSetStatus;

Expand Down
2 changes: 1 addition & 1 deletion src/contracts/core/AllocationManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,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[44] private __gap;
uint256[43] private __gap;
}
47 changes: 46 additions & 1 deletion src/contracts/interfaces/IAVSDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity >=0.5.0;

import "./ISignatureUtils.sol";
import "./IStrategy.sol";

/// @notice Struct representing an operator set
struct OperatorSet {
Expand All @@ -24,7 +25,11 @@ interface IAVSDirectoryErrors {
error InvalidOperator();
/// @dev Thrown when an invalid operator set is provided.
error InvalidOperatorSet();
/// @dev Thrown when `operator` is not a registered operator.
/// @dev Thrown when a strategy is already added to an operator set.
error StrategyAlreadyInOperatorSet();
/// @dev Thrown when a strategy is not in an operator set.
error StrategyNotInOperatorSet();
/// @dev Thrown when an invalid operator set is provided.
error OperatorNotRegistered();

/// @dev Thrown when attempting to spend a spent eip-712 salt.
Expand Down Expand Up @@ -72,6 +77,12 @@ interface IAVSDirectoryEvents is IAVSDirectoryTypes {
/// @notice Emitted when an operator is removed from an operator set.
event OperatorRemovedFromOperatorSet(address indexed operator, OperatorSet operatorSet);

/// @notice Emitted when a strategy is added to an operator set.
event StrategyAddedToOperatorSet(OperatorSet operatorSet, IStrategy strategy);

/// @notice Emitted when a strategy is removed from an operator set.
event StrategyRemovedFromOperatorSet(OperatorSet operatorSet, IStrategy strategy);

/// @notice Emitted when an AVS updates their metadata URI (Uniform Resource Identifier).
/// @dev The URI is never stored; it is simply emitted through an event for off-chain indexing.
event AVSMetadataURIUpdated(address indexed avs, string metadataURI);
Expand Down Expand Up @@ -169,6 +180,32 @@ interface IAVSDirectory is IAVSDirectoryEvents, IAVSDirectoryErrors, ISignatureU
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external;

/**
* @notice Called by AVSs to add a set of strategies to an operator set.
*
* @param operatorSetId The ID of the operator set.
* @param strategies The addresses of the strategies to be added to the operator set.
*
* @dev msg.sender is used as the AVS.
*/
function addStrategiesToOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external;

/**
* @notice Called by AVSs to remove a set of strategies from an operator set.
*
* @param operatorSetId The ID of the operator set.
* @param strategies The addresses of the strategies to be removed from the operator set.
*
* @dev msg.sender is used as the AVS.
*/
function removeStrategiesFromOperatorSet(
uint32 operatorSetId,
IStrategy[] calldata strategies
) external;

/**
* @notice Legacy function called by the AVS's service manager contract
* to register an operator with the AVS. NOTE: this function will be deprecated in a future release
Expand Down Expand Up @@ -285,6 +322,14 @@ interface IAVSDirectory is IAVSDirectoryEvents, IAVSDirectoryErrors, ISignatureU
uint256 length
) external view returns (address[] memory operators);

/**
* @notice Returns an array of strategies in the operatorSet.
* @param operatorSet The operatorSet to query.
*/
function getStrategiesInOperatorSet(
OperatorSet memory operatorSet
) external view returns (IStrategy[] memory strategies);

/**
* @notice Returns the number of operators registered to an operatorSet.
* @param operatorSet The operatorSet to get the member count for
Expand Down

0 comments on commit 5b4d415

Please sign in to comment.