Skip to content

Commit d7c6f3f

Browse files
Feat/operator sets (#579)
* feat: operator set scaffold * fix: impl/storage compile errors; pending updating of tests * chore: `forge fmt` * fix: `OperatorSet` struct misuse * fix: comment * chore: verbose use of `OperatorSet` * test: `registerOperatorToOperatorSet` * feat: `registerOperatorToOperatorSets` Enables registering multiple operator sets in a single call. * chore: `forge fmt` * feat: interface changes * fix: operator set digest * fix: `OPERATOR_SET_REGISTRATION_TYPEHASH` * chore: `forge fmt` * test: wrong avs using signature * fix: optimize for SSTOREs * test: `deregisterOperatorFromOperatorSets` * chore: rename `operatorSetStrategies` * test: `addStrategiesToOperatorSet` * test: `removeStrategiesFromOperatorSet` * test: more coverage * chore: improve natspec * WIP: simp mode * WIP: simp mode * WIP: simp mode - includes interface change, specifically the `StandbyParams` structure. `id` isn't needed for storage. * test: simp mode * test: simp mode * test: simp mode * refactor: simp mode storage * Revert "refactor: simp mode storage" This reverts commit 3b0450e. * Reapply "refactor: simp mode storage" This reverts commit 5f90d78. * feat: simp mode * fix(optimize): salt cancellation - remove check * test: improvements * test: improvements * fix: move `isOperatorSetAVS` update out of loop ooops * fix: standby update typehash * test: cleanup * fix: move mutation out of loop * nit: cleanup * fix: remove unused events --------- Co-authored-by: clandestine.eth <[email protected]>
1 parent 3b47ccf commit d7c6f3f

File tree

8 files changed

+902
-102
lines changed

8 files changed

+902
-102
lines changed

script/deploy/devnet/M2_Deploy_From_Scratch.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ contract Deployer_M2 is Script, Test {
232232
// Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs
233233
delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager);
234234
strategyManagerImplementation = new StrategyManager(delegation, eigenPodManager, slasher);
235-
avsDirectoryImplementation = new AVSDirectory(delegation);
235+
avsDirectoryImplementation = new AVSDirectory(delegation, strategyManager);
236236
slasherImplementation = new Slasher(strategyManager, delegation);
237237
eigenPodManagerImplementation = new EigenPodManager(
238238
ethPOSDeposit,

script/deploy/holesky/M2_Deploy_From_Scratch.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ contract M2_Deploy_Holesky_From_Scratch is ExistingDeploymentParser {
8181
);
8282

8383
eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation));
84-
avsDirectoryImplementation = new AVSDirectory(delegationManager);
84+
avsDirectoryImplementation = new AVSDirectory(delegationManager, strategyManager);
8585
delegationManagerImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager);
8686
strategyManagerImplementation = new StrategyManager(delegationManager, eigenPodManager, slasher);
8787
slasherImplementation = new Slasher(strategyManager, delegationManager);

script/deploy/mainnet/M2_Mainnet_Upgrade.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ contract M2_Mainnet_Upgrade is ExistingDeploymentParser {
4545
*/
4646
function _deployImplementationContracts() internal {
4747
// 1. Deploy New TUPS
48-
avsDirectoryImplementation = new AVSDirectory(delegationManager);
48+
avsDirectoryImplementation = new AVSDirectory(delegationManager, strategyManager);
4949
avsDirectory = AVSDirectory(
5050
address(
5151
new TransparentUpgradeableProxy(

src/contracts/core/AVSDirectory.sol

Lines changed: 257 additions & 30 deletions
Large diffs are not rendered by default.

src/contracts/core/AVSDirectoryStorage.sol

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,20 @@ abstract contract AVSDirectoryStorage is IAVSDirectory {
1313
bytes32 public constant OPERATOR_AVS_REGISTRATION_TYPEHASH =
1414
keccak256("OperatorAVSRegistration(address operator,address avs,bytes32 salt,uint256 expiry)");
1515

16+
/// @notice The EIP-712 typehash for the `OperatorSetRegistration` struct used by the contract
17+
bytes32 public constant OPERATOR_SET_REGISTRATION_TYPEHASH =
18+
keccak256("OperatorSetRegistration(address avs,uint32[] operatorSetIds,bytes32 salt,uint256 expiry)");
19+
20+
/// @notice The EIP-712 typehash for the `StandbyParams` struct used by the contract
21+
bytes32 public constant OPERATOR_STANDBY_UPDATE =
22+
keccak256("OperatorStandbyUpdate(StandbyParam[] standbyParams,bytes32 salt,uint256 expiry)");
23+
1624
/// @notice The DelegationManager contract for EigenLayer
1725
IDelegationManager public immutable delegation;
1826

27+
/// @notice The StrategyManager contract for EigenLayer
28+
IStrategyManager public immutable strategyManager;
29+
1930
/**
2031
* @notice Original EIP-712 Domain separator for this contract.
2132
* @dev The domain separator may change in the event of a fork that modifies the ChainID.
@@ -27,17 +38,30 @@ abstract contract AVSDirectoryStorage is IAVSDirectory {
2738
mapping(address => mapping(address => OperatorAVSRegistrationStatus)) public avsOperatorStatus;
2839

2940
/// @notice Mapping: operator => 32-byte salt => whether or not the salt has already been used by the operator.
30-
/// @dev Salt is used in the `registerOperatorToAVS` function.
41+
/// @dev Salt is used in the `registerOperatorToAVS` and `registerOperatorToOperatorSet` function.
3142
mapping(address => mapping(bytes32 => bool)) public operatorSaltIsSpent;
3243

33-
constructor(IDelegationManager _delegation) {
44+
/// @notice Mapping: AVS => whether or not the AVS uses operator set
45+
mapping(address => bool) public isOperatorSetAVS;
46+
47+
/// @notice Mapping: avs => operator => operatorSetID => whether the operator is registered for the operator set
48+
mapping(address => mapping(address => mapping(uint32 => bool))) public isOperatorInOperatorSet;
49+
50+
/// @notice Mapping: avs => operator => number of operator sets the operator is registered for the AVS
51+
mapping(address => mapping(address => uint256)) public operatorAVSOperatorSetCount;
52+
53+
/// @notice Mapping: avs = operator => operatorSetId => Whether the given operator set in standby mode or not
54+
mapping(address => mapping(address => mapping(uint32 => bool))) public onStandby;
55+
56+
constructor(IDelegationManager _delegation, IStrategyManager _strategyManager) {
3457
delegation = _delegation;
58+
strategyManager = _strategyManager;
3559
}
3660

3761
/**
3862
* @dev This empty reserved space is put in place to allow future versions to add new
3963
* variables without shifting down storage in the inheritance chain.
4064
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
4165
*/
42-
uint256[47] private __gap;
66+
uint256[43] private __gap;
4367
}

src/contracts/interfaces/IAVSDirectory.sol

Lines changed: 124 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,143 @@
22
pragma solidity >=0.5.0;
33

44
import "./ISignatureUtils.sol";
5+
import "./IStrategy.sol";
56

67
interface IAVSDirectory is ISignatureUtils {
7-
/// @notice Enum representing the status of an operator's registration with an AVS
8+
/// @notice Enum representing the registration status of an operator with an AVS.
89
enum OperatorAVSRegistrationStatus {
9-
UNREGISTERED, // Operator not registered to AVS
10-
REGISTERED // Operator registered to AVS
10+
UNREGISTERED, // Operator is not registered with the AVS.
11+
REGISTERED // Operator is registered with the AVS.
1112

1213
}
1314

15+
struct OperatorSet {
16+
address avs;
17+
uint32 id;
18+
}
19+
20+
struct StandbyParam {
21+
OperatorSet operatorSet;
22+
bool onStandby;
23+
}
24+
1425
/**
15-
* @notice Emitted when @param avs indicates that they are updating their MetadataURI string
16-
* @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
26+
* @notice Emitted when an operator's registration status with an AVS is updated.
27+
* Specifically, when an operator enters its first operator set for an AVS, or
28+
* when it is removed from the last operator set.
1729
*/
18-
event AVSMetadataURIUpdated(address indexed avs, string metadataURI);
19-
20-
/// @notice Emitted when an operator's registration status for an AVS is updated
2130
event OperatorAVSRegistrationStatusUpdated(
2231
address indexed operator, address indexed avs, OperatorAVSRegistrationStatus status
2332
);
2433

34+
/// @notice Emitted when an operator is added to an operator set.
35+
event OperatorAddedToOperatorSet(address operator, OperatorSet operatorSet);
36+
37+
/// @notice Emitted when an operator is removed from an operator set.
38+
event OperatorRemovedFromOperatorSet(address operator, OperatorSet operatorSet);
39+
40+
/// @notice Emitted when an AVS updates their metadata URI (Uniform Resource Identifier).
41+
/// @dev The URI is never stored; it is simply emitted through an event for off-chain indexing.
42+
event AVSMetadataURIUpdated(address indexed avs, string metadataURI);
43+
44+
/// @notice Emitted when an operator updates their standby parameters.
45+
event StandbyParamUpdated(address operator, OperatorSet operatorSet, bool onStandby);
46+
2547
/**
26-
* @notice Called by an avs to register an operator with the avs.
27-
* @param operator The address of the operator to register.
28-
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
48+
* @notice Updates the standby parameters for an operator across multiple operator sets.
49+
* Allows the AVS to add the operator to a given operator set if they are not already registered.
50+
*
51+
* @param operator The address of the operator for which the standby parameters are being updated.
52+
* @param standbyParams The new standby parameters for the operator.
53+
* @param signature If non-empty, the signature of the operator authorizing the modification.
54+
* If empty, the `msg.sender` must be the operator.
55+
*/
56+
function updateStandbyParams(
57+
address operator,
58+
StandbyParam[] calldata standbyParams,
59+
SignatureWithSaltAndExpiry calldata signature
60+
) external;
61+
62+
/**
63+
* @notice Called by the AVS's service manager contract to register an operator with the AVS.
64+
*
65+
* @param operator The address of the operator to register.
66+
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
67+
*
68+
* @dev msg.sender must be the AVS.
69+
* @dev Only used by legacy M2 AVSs that have not integrated with operator sets.
2970
*/
3071
function registerOperatorToAVS(
3172
address operator,
3273
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
3374
) external;
3475

3576
/**
36-
* @notice Called by an avs to deregister an operator with the avs.
37-
* @param operator The address of the operator to deregister.
77+
* @notice Called by an AVS to deregister an operator from the AVS.
78+
*
79+
* @param operator The address of the operator to deregister.
80+
*
81+
* @dev Only used by legacy M2 AVSs that have not integrated with operator sets.
3882
*/
3983
function deregisterOperatorFromAVS(address operator) external;
4084

4185
/**
42-
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
43-
* @param metadataURI The URI for metadata associated with an AVS
44-
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `AVSMetadataURIUpdated` event
86+
* @notice Called by AVSs to add an operator to an operator set.
87+
*
88+
* @param operator The address of the operator to be added to the operator set.
89+
* @param operatorSetIds The IDs of the operator sets.
90+
* @param signature The signature of the operator on their intent to register.
91+
*
92+
* @dev msg.sender is used as the AVS.
93+
* @dev The operator must not have a pending deregistration from the operator set.
94+
* @dev If this is the first operator set in the AVS that the operator is
95+
* registering for, a OperatorAVSRegistrationStatusUpdated event is emitted with
96+
* a REGISTERED status.
97+
*/
98+
function registerOperatorToOperatorSets(
99+
address operator,
100+
uint32[] calldata operatorSetIds,
101+
ISignatureUtils.SignatureWithSaltAndExpiry memory signature
102+
) external;
103+
104+
/**
105+
* @notice Called by AVSs or operators to remove an operator from an operator set.
106+
*
107+
* @param operator The address of the operator to be removed from the operator set.
108+
* @param operatorSetIds The IDs of the operator sets.
109+
*
110+
* @dev msg.sender is used as the AVS.
111+
* @dev The operator must be registered for the msg.sender AVS and the given operator set.
112+
* @dev If this removes the operator from all operator sets for the msg.sender AVS,
113+
* then an OperatorAVSRegistrationStatusUpdated event is emitted with a DEREGISTERED status.
114+
*/
115+
function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external;
116+
117+
// VIEW
118+
119+
/**
120+
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
121+
*
122+
* @param metadataURI The URI for metadata associated with an AVS.
123+
*
124+
* @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
45125
*/
46126
function updateAVSMetadataURI(string calldata metadataURI) external;
47127

48128
/**
49-
* @notice Returns whether or not the salt has already been used by the operator.
50-
* @dev Salts is used in the `registerOperatorToAVS` function.
129+
* @notice Returns whether the salt has already been used by the operator or not.
130+
*
131+
* @dev The salt is used in the `registerOperatorToAVS` function.
51132
*/
52133
function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool);
53134

54135
/**
55-
* @notice Calculates the digest hash to be signed by an operator to register with an AVS
56-
* @param operator The account registering as an operator
57-
* @param avs The AVS the operator is registering to
58-
* @param salt A unique and single use value associated with the approver signature.
59-
* @param expiry Time after which the approver's signature becomes invalid
136+
* @notice Calculates the digest hash to be signed by an operator to register with an AVS.
137+
*
138+
* @param operator The account registering as an operator.
139+
* @param avs The AVS the operator is registering with.
140+
* @param salt A unique and single-use value associated with the approver's signature.
141+
* @param expiry The time after which the approver's signature becomes invalid.
60142
*/
61143
function calculateOperatorAVSRegistrationDigestHash(
62144
address operator,
@@ -65,6 +147,24 @@ interface IAVSDirectory is ISignatureUtils {
65147
uint256 expiry
66148
) external view returns (bytes32);
67149

68-
/// @notice The EIP-712 typehash for the Registration struct used by the contract
150+
/**
151+
* @notice Calculates the digest hash to be signed by an operator to register with an operator set.
152+
*
153+
* @param avs The AVS that operator is registering to operator sets for.
154+
* @param operatorSetIds An array of operator set IDs the operator is registering to.
155+
* @param salt A unique and single use value associated with the approver signature.
156+
* @param expiry Time after which the approver's signature becomes invalid.
157+
*/
158+
function calculateOperatorSetRegistrationDigestHash(
159+
address avs,
160+
uint32[] memory operatorSetIds,
161+
bytes32 salt,
162+
uint256 expiry
163+
) external view returns (bytes32);
164+
165+
/// @notice The EIP-712 typehash for the Registration struct used by the contract.
69166
function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32);
167+
168+
/// @notice The EIP-712 typehash for the OperatorSetRegistration struct used by the contract.
169+
function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32);
70170
}

src/test/integration/IntegrationDeployer.t.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser {
437437
delegationManager
438438
);
439439
delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager);
440-
avsDirectoryImplementation = new AVSDirectory(delegationManager);
440+
avsDirectoryImplementation = new AVSDirectory(delegationManager, strategyManager);
441441

442442
// Second, upgrade the proxy contracts to point to the implementations
443443
// DelegationManager
@@ -547,7 +547,7 @@ abstract contract IntegrationDeployer is ExistingDeploymentParser {
547547
delegationManager
548548
);
549549
delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager);
550-
avsDirectoryImplementation = new AVSDirectory(delegationManager);
550+
avsDirectoryImplementation = new AVSDirectory(delegationManager, strategyManager);
551551

552552
// Second, upgrade the proxy contracts to point to the implementations
553553
// DelegationManager

0 commit comments

Comments
 (0)