2
2
pragma solidity ^ 0.8.27 ;
3
3
4
4
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol " ;
5
+ import {IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
6
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
5
7
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol " ;
6
8
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol " ;
7
9
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol " ;
@@ -30,6 +32,7 @@ import {LibMergeSort} from "./libraries/LibMergeSort.sol";
30
32
* @author Layr Labs, Inc.
31
33
*/
32
34
abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
35
+ using SafeERC20 for IERC20 ;
33
36
using BitmapUtils for * ;
34
37
35
38
uint256 public constant SLASHER_PROPOSAL_DELAY = 7 days ;
@@ -141,26 +144,93 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
141
144
* @dev Strategies must be in ascending order of addresses to check for duplicates
142
145
* @dev This function will revert if the `rewardsSubmission` is malformed,
143
146
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
147
+ * @dev This function may fail to execute with a large number of submissions due to gas limits. Use a
148
+ * smaller array of submissions if necessary.
144
149
*/
145
150
function createAVSRewardsSubmission (
146
151
IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions
147
152
) public virtual onlyRewardsInitiator {
148
153
for (uint256 i = 0 ; i < rewardsSubmissions.length ; ++ i) {
149
154
// transfer token to ServiceManager and approve RewardsCoordinator to transfer again
150
155
// in createAVSRewardsSubmission() call
151
- rewardsSubmissions[i].token.transferFrom (
152
- msg .sender , address (this ), rewardsSubmissions[i].amount
156
+ rewardsSubmissions[i].token.safeTransferFrom (
157
+ msg .sender ,
158
+ address (this ),
159
+ rewardsSubmissions[i].amount
153
160
);
154
- uint256 allowance =
155
- rewardsSubmissions[i].token.allowance (address (this ), address (_rewardsCoordinator));
156
- rewardsSubmissions[i].token.approve (
157
- address (_rewardsCoordinator), rewardsSubmissions[i].amount + allowance
161
+ rewardsSubmissions[i].token.safeIncreaseAllowance (
162
+ address (_rewardsCoordinator),
163
+ rewardsSubmissions[i].amount
158
164
);
159
165
}
160
166
161
167
_rewardsCoordinator.createAVSRewardsSubmission (rewardsSubmissions);
162
168
}
163
169
170
+ /**
171
+ * @notice Creates a new operator-directed rewards submission, to be split amongst the operators and
172
+ * set of stakers delegated to operators who are registered to this `avs`.
173
+ * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created.
174
+ * @dev Only callable by the permissioned rewardsInitiator address
175
+ * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
176
+ * @dev The tokens are sent to the `RewardsCoordinator` contract
177
+ * @dev This contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function.
178
+ * @dev Strategies must be in ascending order of addresses to check for duplicates
179
+ * @dev Operators must be in ascending order of addresses to check for duplicates.
180
+ * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed.
181
+ * @dev This function may fail to execute with a large number of submissions due to gas limits. Use a
182
+ * smaller array of submissions if necessary.
183
+ */
184
+ function createOperatorDirectedAVSRewardsSubmission (
185
+ IRewardsCoordinator.OperatorDirectedRewardsSubmission[]
186
+ calldata operatorDirectedRewardsSubmissions
187
+ ) public virtual onlyRewardsInitiator {
188
+ for (
189
+ uint256 i = 0 ;
190
+ i < operatorDirectedRewardsSubmissions.length ;
191
+ ++ i
192
+ ) {
193
+ // Calculate total amount of token to transfer
194
+ uint256 totalAmount = 0 ;
195
+ for (
196
+ uint256 j = 0 ;
197
+ j <
198
+ operatorDirectedRewardsSubmissions[i].operatorRewards.length ;
199
+ ++ j
200
+ ) {
201
+ totalAmount += operatorDirectedRewardsSubmissions[i]
202
+ .operatorRewards[j]
203
+ .amount;
204
+ }
205
+
206
+ // Transfer token to ServiceManager and approve RewardsCoordinator to transfer again
207
+ // in createOperatorDirectedAVSRewardsSubmission() call
208
+ operatorDirectedRewardsSubmissions[i].token.safeTransferFrom (
209
+ msg .sender ,
210
+ address (this ),
211
+ totalAmount
212
+ );
213
+ operatorDirectedRewardsSubmissions[i].token.safeIncreaseAllowance (
214
+ address (_rewardsCoordinator),
215
+ totalAmount
216
+ );
217
+ }
218
+
219
+ _rewardsCoordinator.createOperatorDirectedAVSRewardsSubmission (
220
+ address (this ),
221
+ operatorDirectedRewardsSubmissions
222
+ );
223
+ }
224
+
225
+ /**
226
+ * @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract.
227
+ * @param claimer The address of the entity that can call `processClaim` on behalf of the earner
228
+ * @dev Only callable by the owner.
229
+ */
230
+ function setClaimerFor (address claimer ) public virtual onlyOwner {
231
+ _rewardsCoordinator.setClaimerFor (claimer);
232
+ }
233
+
164
234
/**
165
235
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS
166
236
* @param operator The address of the operator to register.
@@ -219,7 +289,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
219
289
* @dev No guarantee is made on uniqueness of each element in the returned array.
220
290
* The off-chain service should do that validation separately
221
291
*/
222
- function getRestakeableStrategies () external view virtual returns (address [] memory ) {
292
+ function getRestakeableStrategies ()
293
+ external
294
+ view
295
+ virtual
296
+ returns (address [] memory )
297
+ {
223
298
uint256 quorumCount = _registryCoordinator.quorumCount ();
224
299
225
300
if (quorumCount == 0 ) {
@@ -234,10 +309,13 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
234
309
address [] memory restakedStrategies = new address [](strategyCount);
235
310
uint256 index = 0 ;
236
311
for (uint256 i = 0 ; i < _registryCoordinator.quorumCount (); i++ ) {
237
- uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength (uint8 (i));
312
+ uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength (
313
+ uint8 (i)
314
+ );
238
315
for (uint256 j = 0 ; j < strategyParamsLength; j++ ) {
239
- restakedStrategies[index] =
240
- address (_stakeRegistry.strategyParamsByIndex (uint8 (i), j).strategy);
316
+ restakedStrategies[index] = address (
317
+ _stakeRegistry.strategyParamsByIndex (uint8 (i), j).strategy
318
+ );
241
319
index++ ;
242
320
}
243
321
}
@@ -255,28 +333,37 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
255
333
address operator
256
334
) external view virtual returns (address [] memory ) {
257
335
bytes32 operatorId = _registryCoordinator.getOperatorId (operator);
258
- uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap (operatorId);
336
+ uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap (
337
+ operatorId
338
+ );
259
339
260
340
if (operatorBitmap == 0 || _registryCoordinator.quorumCount () == 0 ) {
261
341
return new address [](0 );
262
342
}
263
343
264
344
// Get number of strategies for each quorum in operator bitmap
265
- bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray (operatorBitmap);
345
+ bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray (
346
+ operatorBitmap
347
+ );
266
348
uint256 strategyCount;
267
349
for (uint256 i = 0 ; i < operatorRestakedQuorums.length ; i++ ) {
268
- strategyCount += _stakeRegistry.strategyParamsLength (uint8 (operatorRestakedQuorums[i]));
350
+ strategyCount += _stakeRegistry.strategyParamsLength (
351
+ uint8 (operatorRestakedQuorums[i])
352
+ );
269
353
}
270
354
271
355
// Get strategies for each quorum in operator bitmap
272
356
address [] memory restakedStrategies = new address [](strategyCount);
273
357
uint256 index = 0 ;
274
358
for (uint256 i = 0 ; i < operatorRestakedQuorums.length ; i++ ) {
275
359
uint8 quorum = uint8 (operatorRestakedQuorums[i]);
276
- uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength (quorum);
360
+ uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength (
361
+ quorum
362
+ );
277
363
for (uint256 j = 0 ; j < strategyParamsLength; j++ ) {
278
- restakedStrategies[index] =
279
- address (_stakeRegistry.strategyParamsByIndex (quorum, j).strategy);
364
+ restakedStrategies[index] = address (
365
+ _stakeRegistry.strategyParamsByIndex (quorum, j).strategy
366
+ );
280
367
index++ ;
281
368
}
282
369
}
0 commit comments