1+ // SPDX-License-Identifier: BUSL-1.1
2+ pragma solidity = 0.8.12 ;
3+
4+ import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol " ;
5+ import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol " ;
6+ import "@openzeppelin-upgrades/contracts/security/ReentrancyGuardUpgradeable.sol " ;
7+ import "../permissions/Pausable.sol " ;
8+ import "../libraries/EIP1271SignatureUtils.sol " ;
9+ import "./AVSDirectoryStorage.sol " ;
10+
11+ contract AVSDirectory is
12+ Initializable ,
13+ OwnableUpgradeable ,
14+ Pausable ,
15+ AVSDirectoryStorage ,
16+ ReentrancyGuardUpgradeable
17+ {
18+ // @dev Index for flag that pauses operator register/deregister to avs when set.
19+ uint8 internal constant PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS = 0 ;
20+
21+ // @dev Chain ID at the time of contract deployment
22+ uint256 internal immutable ORIGINAL_CHAIN_ID;
23+
24+ /*******************************************************************************
25+ INITIALIZING FUNCTIONS
26+ *******************************************************************************/
27+
28+ /**
29+ * @dev Initializes the immutable addresses of the strategy mananger, delegationManager, slasher,
30+ * and eigenpodManager contracts
31+ */
32+ constructor (IDelegationManager _delegation ) AVSDirectoryStorage (_delegation) {
33+ _disableInitializers ();
34+ ORIGINAL_CHAIN_ID = block .chainid ;
35+ }
36+
37+ /**
38+ * @dev Initializes the addresses of the initial owner, pauser registry, and paused status.
39+ * minWithdrawalDelayBlocks is set only once here
40+ */
41+ function initialize (
42+ address initialOwner ,
43+ IPauserRegistry _pauserRegistry ,
44+ uint256 initialPausedStatus
45+ ) external initializer {
46+ _initializePauser (_pauserRegistry, initialPausedStatus);
47+ _DOMAIN_SEPARATOR = _calculateDomainSeparator ();
48+ _transferOwnership (initialOwner);
49+ }
50+
51+ /*******************************************************************************
52+ EXTERNAL FUNCTIONS
53+ *******************************************************************************/
54+
55+
56+ /**
57+ * @notice Called by the AVS's service manager contract to register an operator with the avs.
58+ * @param operator The address of the operator to register.
59+ * @param operatorSignature The signature, salt, and expiry of the operator's signature.
60+ */
61+ function registerOperatorToAVS (
62+ address operator ,
63+ ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
64+ ) external onlyWhenNotPaused (PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS) {
65+
66+ require (
67+ operatorSignature.expiry >= block .timestamp ,
68+ "AVSDirectory.registerOperatorToAVS: operator signature expired "
69+ );
70+ require (
71+ avsOperatorStatus[msg .sender ][operator] != OperatorAVSRegistrationStatus.REGISTERED,
72+ "AVSDirectory.registerOperatorToAVS: operator already registered "
73+ );
74+ require (
75+ ! operatorSaltIsSpent[operator][operatorSignature.salt],
76+ "AVSDirectory.registerOperatorToAVS: salt already spent "
77+ );
78+ require (
79+ delegation.isOperator (operator),
80+ "AVSDirectory.registerOperatorToAVS: operator not registered to EigenLayer yet " );
81+
82+ // Calculate the digest hash
83+ bytes32 operatorRegistrationDigestHash = calculateOperatorAVSRegistrationDigestHash ({
84+ operator: operator,
85+ avs: msg .sender ,
86+ salt: operatorSignature.salt,
87+ expiry: operatorSignature.expiry
88+ });
89+
90+ // Check that the signature is valid
91+ EIP1271SignatureUtils.checkSignature_EIP1271 (
92+ operator,
93+ operatorRegistrationDigestHash,
94+ operatorSignature.signature
95+ );
96+
97+ // Set the operator as registered
98+ avsOperatorStatus[msg .sender ][operator] = OperatorAVSRegistrationStatus.REGISTERED;
99+
100+ // Mark the salt as spent
101+ operatorSaltIsSpent[operator][operatorSignature.salt] = true ;
102+
103+ emit OperatorAVSRegistrationStatusUpdated (operator, msg .sender , OperatorAVSRegistrationStatus.REGISTERED);
104+ }
105+
106+ /**
107+ * @notice Called by an avs to deregister an operator with the avs.
108+ * @param operator The address of the operator to deregister.
109+ */
110+ function deregisterOperatorFromAVS (address operator ) external onlyWhenNotPaused (PAUSED_OPERATOR_REGISTER_DEREGISTER_TO_AVS) {
111+ require (
112+ avsOperatorStatus[msg .sender ][operator] == OperatorAVSRegistrationStatus.REGISTERED,
113+ "AVSDirectory.deregisterOperatorFromAVS: operator not registered "
114+ );
115+
116+ // Set the operator as deregistered
117+ avsOperatorStatus[msg .sender ][operator] = OperatorAVSRegistrationStatus.UNREGISTERED;
118+
119+ emit OperatorAVSRegistrationStatusUpdated (operator, msg .sender , OperatorAVSRegistrationStatus.UNREGISTERED);
120+ }
121+
122+ /**
123+ * @notice Called by an avs to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
124+ * @param metadataURI The URI for metadata associated with an avs
125+ */
126+ function updateAVSMetadataURI (string calldata metadataURI ) external {
127+ emit AVSMetadataURIUpdated (msg .sender , metadataURI);
128+ }
129+
130+ /*******************************************************************************
131+ VIEW FUNCTIONS
132+ *******************************************************************************/
133+
134+ /**
135+ * @notice Calculates the digest hash to be signed by an operator to register with an AVS
136+ * @param operator The account registering as an operator
137+ * @param avs The address of the service manager contract for the AVS that the operator is registering to
138+ * @param salt A unique and single use value associated with the approver signature.
139+ * @param expiry Time after which the approver's signature becomes invalid
140+ */
141+ function calculateOperatorAVSRegistrationDigestHash (
142+ address operator ,
143+ address avs ,
144+ bytes32 salt ,
145+ uint256 expiry
146+ ) public view returns (bytes32 ) {
147+ // calculate the struct hash
148+ bytes32 structHash = keccak256 (
149+ abi.encode (OPERATOR_AVS_REGISTRATION_TYPEHASH, operator, avs, salt, expiry)
150+ );
151+ // calculate the digest hash
152+ bytes32 digestHash = keccak256 (
153+ abi.encodePacked ("\x19\x01 " , domainSeparator (), structHash)
154+ );
155+ return digestHash;
156+ }
157+
158+ /**
159+ * @notice Getter function for the current EIP-712 domain separator for this contract.
160+ * @dev The domain separator will change in the event of a fork that changes the ChainID.
161+ */
162+ function domainSeparator () public view returns (bytes32 ) {
163+ if (block .chainid == ORIGINAL_CHAIN_ID) {
164+ return _DOMAIN_SEPARATOR;
165+ } else {
166+ return _calculateDomainSeparator ();
167+ }
168+ }
169+
170+ // @notice Internal function for calculating the current domain separator of this contract
171+ function _calculateDomainSeparator () internal view returns (bytes32 ) {
172+ return keccak256 (abi.encode (DOMAIN_TYPEHASH, keccak256 (bytes ("EigenLayer " )), block .chainid , address (this )));
173+ }
174+ }
0 commit comments