Skip to content

Commit 6bbadd2

Browse files
authored
docs: describe Rewards v2.1 diff (#1055)
1 parent 499fd45 commit 6bbadd2

File tree

1 file changed

+82
-20
lines changed

1 file changed

+82
-20
lines changed

docs/core/RewardsCoordinator.md

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<!-- The primary functions of the `RewardsCoordinator` contract are to (i) accept ERC20 rewards from AVSs (Actively Validated Services) to their Operators and delegated Stakers for a given time range; (ii) enable the protocol to provide ERC20 tokens to all stakers over a specified time range; and (iii) allow stakers and operators to claim their accumulated earnings. -->
99

10-
The `RewardsCoordinator` accepts ERC20s from AVSs alongside rewards submission requests made out to Operators who, during a specified time range, were registered to the AVS in the core [`AVSDirectory`](./AVSDirectory.md) contract.
10+
The `RewardsCoordinator` accepts ERC20s from AVSs alongside rewards submission requests made out to Operators who, during a specified time range, were registered to the AVS in the core [`AllocationManager`](./AllocationManager.md) contract.
1111

1212
There are two forms of rewards:
1313
* Rewards v1, also known as rewards submissions.
@@ -46,21 +46,25 @@ This document is organized according to the following themes (click each to be t
4646
* Mapping for earners(Stakers/Operators) to track their total claimed earnings per reward token. This mapping is used to calculate the difference between the cumulativeEarnings stored in the merkle tree and the previous total claimed amount. This difference is then transfered to the specified destination address.
4747
* `uint16 public defaultOperatorSplitBips`: *Used off-chain* by the rewards updater to calculate an Operator's split for a specific reward.
4848
* This is expected to be a flat 10% rate for the initial rewards release. Expressed in basis points, this is `1000`.
49-
* `mapping(address => mapping(address => OperatorSplit)) internal operatorAVSSplitBips`: operator => AVS => `OperatorSplit`
49+
* `mapping(address => mapping(address => OperatorSplit)) internal _operatorAVSSplitBips`: operator => AVS => `OperatorSplit`
5050
* Operators specify their custom split for a given AVS for each `OperatorDirectedRewardsSubmission`, where Stakers receive a relative proportion (by stake weight) of the remaining amount.
51-
* `mapping(address => OperatorSplit) internal operatorPISplitBips`: operator => `OperatorSplit`
51+
* `mapping(address => OperatorSplit) internal _operatorPISplitBips`: operator => `OperatorSplit`
5252
* Operators may also specify their custom split for [programmatic incentives](https://www.blog.eigenlayer.xyz/introducing-programmatic-incentives-v1/), where Stakers similarly receive a relative proportion (by stake weight) of the remaining amount.
53+
* `mapping(address operator => mapping(bytes32 operatorSetKey => OperatorSplit split)) internal _operatorSetSplitBips`: operator => Operator Set Key => `OperatorSplit`
54+
* Operators may specify their custom split for a given Operator Set, which is more granular than an overarching AVS split
5355

5456
#### Helpful definitions
5557

56-
* `_checkClaim(RewardsMerkleClaim calldata claim, DistributionRoot memory root)`
57-
* Checks the merkle inclusion of a claim against a `DistributionRoot`
58-
* Reverts if any of the following are true:
58+
* **AVS** (Autonomous Verifiable Service) refers to the contract entity that is submitting rewards to the `RewardsCoordinator`.
59+
* This is assumed to be a customized `ServiceManager` contract of some kind that is interfacing with the EigenLayer protocol. See the `ServiceManagerBase` docs here: [`eigenlayer-middleware/docs/ServiceManagerBase.md`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/dev/docs/ServiceManagerBase.md).
60+
* An **Operator Set** refers to a collection of registered operators and strategies. See [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md#operator-sets) for more details.
61+
* An **Operator Set Key** descibes the tuple of an AVS address and an ID that uniquely identifies an Operator Set. See the [AllocationManager](./AllocationManager.md#operator-sets) for details.
62+
* A **rewards submission** includes, unless specified otherwise, both the v1 `RewardsSubmission` and the v2 `OperatorDirectedRewardsSubmission` types.
63+
* The internal function `_checkClaim(RewardsMerkleClaim calldata claim, DistributionRoot memory root)` checks the merkle inclusion of a claim against a `DistributionRoot`
64+
* It reverts if any of the following are true:
5965
* mismatch input param lengths: tokenIndices, tokenTreeProofs, tokenLeaves
6066
* earner proof reverting from calling `_verifyEarnerClaimProof`
6167
* any of the token proofs reverting from calling `_verifyTokenClaimProof`
62-
* Wherever AVS (Actively Validated Service) is mentioned, it refers to the contract entity that is submitting rewards to the `RewardsCoordinator`. This is assumed to be a customized `ServiceManager` contract of some kind that is interfacing with the EigenLayer protocol. See the `ServiceManagerBase` docs here: [`eigenlayer-middleware/docs/ServiceManagerBase.md`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/dev/docs/ServiceManagerBase.md).
63-
* A rewards submission includes, unless specified otherwise, both the v1 `RewardsSubmission` and the v2 `OperatorDirectedRewardsSubmission` types.
6468

6569
---
6670

@@ -71,7 +75,8 @@ Rewards are initially submitted to the contract to be distributed to Operators a
7175
* [`RewardsCoordinator.createAVSRewardsSubmission`](#createavsrewardssubmission)
7276
* [`RewardsCoordinator.createRewardsForAllSubmission`](#createrewardsforallsubmission)
7377
* [`RewardsCoordinator.createRewardsForAllEarners`](#createrewardsforallearners)
74-
* [`RewardsCoordinator.createOperatorDirectedAVSRewardsSubmission`](#createOperatorDirectedAVSRewardsSubmission)
78+
* [`RewardsCoordinator.createOperatorDirectedAVSRewardsSubmission`](#createoperatordirectedavsrewardssubmission)
79+
* [`RewardsCoordinator.createOperatorDirectedOperatorSetRewardsSubmission`](#createoperatordirectedoperatorsetrewardssubmission)
7580

7681
#### `createAVSRewardsSubmission`
7782

@@ -169,7 +174,7 @@ This method is identical in function to [`createAVSRewardsSubmission`](#createav
169174
*Effects*:
170175
* See [`createAVSRewardsSubmission`](#createavsrewardssubmission) above. The only differences are that:
171176
* Each rewards submission hash is stored in the `isRewardsSubmissionForAllHash` mapping
172-
* Emits a `RewardsSubmissionForAllCreated` event
177+
* A `RewardsSubmissionForAllCreated` event is emitted
173178

174179
*Requirements*:
175180
* See [`createAVSRewardsSubmission`](#createavsrewardssubmission) above. The only difference is that each calculated rewards submission hash MUST NOT already exist in the `isRewardsSubmissionForAllHash` mapping.
@@ -207,21 +212,22 @@ function createOperatorDirectedAVSRewardsSubmission(
207212
)
208213
external
209214
onlyWhenNotPaused(PAUSED_OPERATOR_DIRECTED_AVS_REWARDS_SUBMISSION)
215+
checkCanCall(avs)
210216
nonReentrant
211217
```
212218

213219
AVS may make Rewards v2 submissions by calling `createOperatorDirectedAVSRewardsSubmission()` with any custom on-chain or off-chain logic to determine their rewards distribution strategy. This can be custom to the work performed by Operators during a certain period of time, can be a flat reward rate, or some other structure based on the AVS’s economic model. This would enable AVSs' flexibility in rewarding different operators for performance and other variables while maintaining the same easily calculable reward rate for stakers delegating to the same operator and strategy. The AVS can submit multiple performance-based rewards denominated in different tokens for even more flexibility.
214220

215221
*Effects*:
216222
* For each `OperatorDirectedRewardsSubmission` element
217-
* Transfers `amount` of `token` from the `msg.sender` (`AVS`) to the `RewardsCoordinator`
218-
* Hashes `msg.sender` (`AVS`), `nonce`, and `OperatorDirectedRewardsSubmission` struct to create a unique rewards hash and sets this value to `true` in the `isOperatorDirectedAVSRewardsSubmissionHash` mapping
219-
* Increments `submissionNonce[msg.sender]`
223+
* Transfers `amount` of `token` from `msg.sender` to the `RewardsCoordinator`
224+
* Hashes `AVS`, `nonce`, and `OperatorDirectedRewardsSubmission` struct to create a unique rewards hash and sets this value to `true` in the `isOperatorDirectedAVSRewardsSubmissionHash` mapping
225+
* Increments `submissionNonce[avs]`
220226
* Emits an `OperatorDirectedAVSRewardsSubmissionCreated` event
221227

222228
*Requirements*:
223229
* Pause status MUST NOT be set: `PAUSED_OPERATOR_DIRECTED_AVS_REWARDS_SUBMISSION`
224-
* Caller MUST BE the AVS
230+
* Caller MUST be authorized, either as the AVS itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
225231
* Function call is not reentered
226232
* For each `OperatorDirectedRewardsSubmission` element:
227233
* Requirements from calling internal function `_validateOperatorDirectedRewardsSubmission()`
@@ -243,6 +249,33 @@ AVS may make Rewards v2 submissions by calling `createOperatorDirectedAVSRewards
243249
* `operatorDirectedRewardsSubmission.startTimestamp + operatorDirectedRewardsSubmission.duration < block.timestamp`, enforcing strictly retoractive rewards submissions
244250
* `transferFrom` MUST succeed in transferring `amount` of `token` from `msg.sender` to the `RewardsCoordinator`
245251

252+
#### `createOperatorDirectedOperatorSetRewardsSubmission`
253+
254+
```solidity
255+
function createOperatorDirectedOperatorSetRewardsSubmission(
256+
OperatorSet calldata operatorSet,
257+
OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
258+
)
259+
external
260+
onlyWhenNotPaused(PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION)
261+
checkCanCall(operatorSet.avs)
262+
nonReentrant
263+
```
264+
265+
This function allows AVSs to make rewards submissions to specific operator sets, allowing for more granularly targeted rewards based on tasks assigned to a specific operator set, or any other custom AVS logic. Its functionality is almost identical to [`createOperatorDirectedAVSRewardsSubmission`](#createoperatordirectedavsrewardssubmission), save for some operator-set specific requirements, state variables, and events.
266+
267+
Note that an AVS must specify an operator set registered to the AVS; in other words, an operator set belonging to a different AVS, or an unregistered operator set, will cause this function to revert.
268+
269+
*Effects*:
270+
* See [`createOperatorDirectedAVSRewardsSubmission`](#createoperatordirectedavsrewardssubmission) above. The only differences are that:
271+
* Each rewards submission is stored in the `isOperatorDirectedOperatorSetRewardsSubmissionHash` mapping
272+
* An `OperatorDirectedOperatorSetRewardsSubmissionCreated` event is emitted
273+
274+
*Requirements*:
275+
* See [`createOperatorDirectedAVSRewardsSubmission`](#createoperatordirectedavsrewardssubmission) above. The only differences are that:
276+
* `operatorSet` MUST be a [registered operator set](./AllocationManager.md#createoperatorsets) for the given AVS as according to `allocationManager.isOperatorSet()`
277+
* Pause status is instead: `PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION`
278+
246279
---
247280

248281
### Distributing and Claiming Rewards
@@ -409,11 +442,12 @@ function processClaims(
409442
### System Configuration
410443

411444
* [`RewardsCoordinator.setActivationDelay`](#setactivationdelay)
412-
* [`RewardsCoordinator.setDefaultOperatorSplit`](#setDefaultOperatorSplit)
445+
* [`RewardsCoordinator.setDefaultOperatorSplit`](#setdefaultoperatorsplit)
413446
* [`RewardsCoordinator.setRewardsUpdater`](#setrewardsupdater)
414447
* [`RewardsCoordinator.setRewardsForAllSubmitter`](#setrewardsforallsubmitter)
415-
* [`RewardsCoordinator.setOperatorAVSsplit`](#setOperatorAVSsplit)
416-
* [`RewardsCoordinator.setOperatorPIsplit`](#setOperatorPIsplit)
448+
* [`RewardsCoordinator.setOperatorAVSSplit`](#setoperatoravssplit)
449+
* [`RewardsCoordinator.setOperatorPISplit`](#setoperatorpisplit)
450+
* [`RewardsCoordinator.setOperatorSetSplit`](#setoperatorsetsplit)
417451

418452
#### `setActivationDelay`
419453

@@ -487,6 +521,7 @@ function setOperatorAVSSplit(
487521
)
488522
external
489523
onlyWhenNotPaused(PAUSED_OPERATOR_AVS_SPLIT)
524+
checkCanCall(operator)
490525
```
491526

492527
An Operator may, for a given AVS, set a split which will determine what percent of their attributed rewards are allocated to themselves. The remaining percentage will go to Stakers.
@@ -500,7 +535,7 @@ The split will take effect after an `activationDelay` set by the contract owner.
500535
* Emits an `OperatorAVSSplitBipsSet` event
501536

502537
*Requirements*:
503-
* Caller MUST BE the operator
538+
* Caller MUST be authorized, either as the operator itself or an admin/appointee (see [`PermissionController.md`](../permissions/PermissionController.md))
504539
* Split MUST BE <= 10,000 bips (100%)
505540
* Current `block.timestamp` MUST BE greater than current `operatorSplit.activatedAt`.
506541
* Any pending split must have already completed prior to setting a new split.
@@ -514,15 +549,42 @@ function setOperatorPISplit(
514549
)
515550
external
516551
onlyWhenNotPaused(PAUSED_OPERATOR_PI_SPLIT)
552+
checkCanCall(operator)
517553
```
518554

519555
Similar to [`setOperatorAVSSplit`](#setoperatoravssplit), Operators may set their split for [programmatic incentives](https://www.blog.eigenlayer.xyz/introducing-programmatic-incentives-v1/), allowing them to specify what percent of these rewards they will maintain and what percent will go to their Stakers. The `allocationDelay` also applies here, as well as the inability to reinitiate a split update before the delay passes.
520556

521557
*Effects*:
522-
* Same as [`setOperatorAVSSplit`](#setoperatoravssplit), but with `operatorPISplitBips` instead of `operatorAVSSplitBips`.
558+
* See [`setOperatorAVSSplit`](#setoperatoravssplit) above. The only differences are that:
559+
* The split is stored within `_operatorPISplitBips` instead of `_operatorAVSSplitBips`.
560+
* An `OperatorPISplitBipsSet` event is emitted
561+
562+
*Requirements*:
563+
* See [`setOperatorAVSSplit`](#setoperatoravssplit) above. The only difference is that:
564+
* Pause status is instead: `PAUSED_OPERATOR_PI_SPLIT`
565+
566+
#### `setOperatorSetSplit`
567+
568+
```solidity
569+
function setOperatorSetSplit(
570+
address operator,
571+
OperatorSet calldata operatorSet,
572+
uint16 split
573+
)
574+
external
575+
onlyWhenNotPaused(PAUSED_OPERATOR_SET_SPLIT)
576+
checkCanCall(operator)
577+
```
578+
579+
*Effects*:
580+
* See [`setOperatorAVSSplit`](#setoperatoravssplit) above. The only difference is that:
581+
* The split is stored within `_operatorSetSplitBips` instead of `_operatorAVSSplitBips`
582+
* An `OperatorSetSplitBipsSet` event is emitted
523583

524584
*Requirements*:
525-
* See [`setOperatorAVSSplit`](#setoperatoravssplit).
585+
* See [`setOperatorAVSSplit`](#setoperatoravssplit) above. The only differences are that:
586+
* `operatorSet` MUST be a [registered operator set](./AllocationManager.md#createoperatorsets) for the given AVS as according to `allocationManager.isOperatorSet()`
587+
* Pause status is instead: `PAUSED_OPERATOR_SET_SPLIT`
526588

527589
---
528590

0 commit comments

Comments
 (0)