Skip to content

Commit 9b2b470

Browse files
authored
feat: rewards v2.2 (#1659)
**Motivation:** We need to support Rewards v2.2 : - Rewards that are linear to allocated unique stake (Needs to be both retroactive and future-looking). - Rewards that are linear to total stake (Needs to be both retroactive and future-looking). TDD: https://www.notion.so/eigen-labs/PRD-Rewards-v2-2-Operator-Set-Rewards-with-Unique-Total-Stake-28513c11c3e080cbb856e41ddc6362f5 **Modifications:** Updates in `RewardsCoordinator`: - New `createUniqueStakeRewardsSubmission` function with `UniqueStakeRewardsSubmissionCreated` event emission - New `PAUSED_UNIQUE_STAKE_REWARDS_SUBMISSION` constant - New `isUniqueStakeRewardsSubmissionHash` mapping - New `createTotalStakeRewardsSubmission` function with `TotalStakeRewardsSubmissionCreated` event emission - New `PAUSED_TOTAL_STAKE_REWARDS_SUBMISSION` constant - New `isTotalStakeRewardsSubmissionHash` mapping - Updated storage gap to 33 slots (from 35 slots) Updated Bindings **Result:** Support for Rewards v2.2
1 parent 8d87b3e commit 9b2b470

File tree

7 files changed

+2581
-6
lines changed

7 files changed

+2581
-6
lines changed

pkg/bindings/IRewardsCoordinator/binding.go

Lines changed: 355 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bindings/RewardsCoordinator/binding.go

Lines changed: 418 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bindings/RewardsCoordinatorStorage/binding.go

Lines changed: 417 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/contracts/core/RewardsCoordinator.sol

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,52 @@ contract RewardsCoordinator is
203203
}
204204
}
205205

206+
/// @inheritdoc IRewardsCoordinator
207+
function createUniqueStakeRewardsSubmission(
208+
OperatorSet calldata operatorSet,
209+
RewardsSubmission[] calldata rewardsSubmissions
210+
) external onlyWhenNotPaused(PAUSED_UNIQUE_STAKE_REWARDS_SUBMISSION) checkCanCall(operatorSet.avs) nonReentrant {
211+
require(allocationManager.isOperatorSet(operatorSet), InvalidOperatorSet());
212+
for (uint256 i = 0; i < rewardsSubmissions.length; ++i) {
213+
RewardsSubmission calldata rewardsSubmission = rewardsSubmissions[i];
214+
uint256 nonce = submissionNonce[operatorSet.avs];
215+
bytes32 rewardsSubmissionHash = keccak256(abi.encode(operatorSet.avs, nonce, rewardsSubmission));
216+
217+
_validateRewardsSubmission(rewardsSubmission);
218+
219+
isUniqueStakeRewardsSubmissionHash[operatorSet.avs][rewardsSubmissionHash] = true;
220+
submissionNonce[operatorSet.avs] = nonce + 1;
221+
222+
emit UniqueStakeRewardsSubmissionCreated(
223+
msg.sender, rewardsSubmissionHash, operatorSet, nonce, rewardsSubmission
224+
);
225+
rewardsSubmission.token.safeTransferFrom(msg.sender, address(this), rewardsSubmission.amount);
226+
}
227+
}
228+
229+
/// @inheritdoc IRewardsCoordinator
230+
function createTotalStakeRewardsSubmission(
231+
OperatorSet calldata operatorSet,
232+
RewardsSubmission[] calldata rewardsSubmissions
233+
) external onlyWhenNotPaused(PAUSED_TOTAL_STAKE_REWARDS_SUBMISSION) checkCanCall(operatorSet.avs) nonReentrant {
234+
require(allocationManager.isOperatorSet(operatorSet), InvalidOperatorSet());
235+
for (uint256 i = 0; i < rewardsSubmissions.length; ++i) {
236+
RewardsSubmission calldata rewardsSubmission = rewardsSubmissions[i];
237+
uint256 nonce = submissionNonce[operatorSet.avs];
238+
bytes32 rewardsSubmissionHash = keccak256(abi.encode(operatorSet.avs, nonce, rewardsSubmission));
239+
240+
_validateRewardsSubmission(rewardsSubmission);
241+
242+
isTotalStakeRewardsSubmissionHash[operatorSet.avs][rewardsSubmissionHash] = true;
243+
submissionNonce[operatorSet.avs] = nonce + 1;
244+
245+
emit TotalStakeRewardsSubmissionCreated(
246+
msg.sender, rewardsSubmissionHash, operatorSet, nonce, rewardsSubmission
247+
);
248+
rewardsSubmission.token.safeTransferFrom(msg.sender, address(this), rewardsSubmission.amount);
249+
}
250+
}
251+
206252
/// @inheritdoc IRewardsCoordinator
207253
function processClaim(
208254
RewardsMerkleClaim calldata claim,

src/contracts/core/RewardsCoordinatorStorage.sol

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
3232
uint8 internal constant PAUSED_OPERATOR_SET_SPLIT = 8;
3333
/// @dev Index for flag that pauses calling setOperatorSetPerformanceRewardsSubmission
3434
uint8 internal constant PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION = 9;
35+
/// @dev Index for flag that pauses calling createOperatorSetRewardsSubmission
36+
uint8 internal constant PAUSED_UNIQUE_STAKE_REWARDS_SUBMISSION = 10;
37+
/// @dev Index for flag that pauses calling createTotalStakeRewardsSubmission
38+
uint8 internal constant PAUSED_TOTAL_STAKE_REWARDS_SUBMISSION = 11;
3539

3640
/// @dev Salt for the earner leaf, meant to distinguish from tokenLeaf since they have the same sized data
3741
uint8 internal constant EARNER_LEAF_SALT = 0;
@@ -130,8 +134,13 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
130134
mapping(address avs => mapping(bytes32 hash => bool valid)) public
131135
isOperatorDirectedOperatorSetRewardsSubmissionHash;
132136

133-
// Construction
137+
/// @notice Returns whether a `hash` is a `valid` unique stake rewards submission hash for a given `avs`.
138+
mapping(address avs => mapping(bytes32 hash => bool valid)) public isUniqueStakeRewardsSubmissionHash;
139+
140+
/// @notice Returns whether a `hash` is a `valid` total stake rewards submission hash for a given `avs`.
141+
mapping(address avs => mapping(bytes32 hash => bool valid)) public isTotalStakeRewardsSubmissionHash;
134142

143+
// Construction
135144
constructor(
136145
IDelegationManager _delegationManager,
137146
IStrategyManager _strategyManager,
@@ -161,5 +170,5 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
161170
* variables without shifting down storage in the inheritance chain.
162171
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
163172
*/
164-
uint256[35] private __gap;
173+
uint256[33] private __gap;
165174
}

src/contracts/interfaces/IRewardsCoordinator.sol

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,38 @@ interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes {
333333
OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
334334
);
335335

336+
/**
337+
* @notice Emitted when an AVS creates a valid `UniqueStakeRewardsSubmission` for an operator set.
338+
* @param caller The address calling `createUniqueStakeRewardsSubmission`.
339+
* @param rewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `rewardsSubmission`).
340+
* @param operatorSet The operatorSet on behalf of which the rewards are being submitted.
341+
* @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
342+
* @param rewardsSubmission The Rewards Submission. Contains the token, start timestamp, duration, strategies and multipliers.
343+
*/
344+
event UniqueStakeRewardsSubmissionCreated(
345+
address indexed caller,
346+
bytes32 indexed rewardsSubmissionHash,
347+
OperatorSet operatorSet,
348+
uint256 submissionNonce,
349+
RewardsSubmission rewardsSubmission
350+
);
351+
352+
/**
353+
* @notice Emitted when an AVS creates a valid `TotalStakeRewardsSubmission` for an operator set.
354+
* @param caller The address calling `createTotalStakeRewardsSubmission`.
355+
* @param rewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `rewardsSubmission`).
356+
* @param operatorSet The operatorSet on behalf of which the rewards are being submitted.
357+
* @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
358+
* @param rewardsSubmission The Rewards Submission. Contains the token, start timestamp, duration, strategies and multipliers.
359+
*/
360+
event TotalStakeRewardsSubmissionCreated(
361+
address indexed caller,
362+
bytes32 indexed rewardsSubmissionHash,
363+
OperatorSet operatorSet,
364+
uint256 submissionNonce,
365+
RewardsSubmission rewardsSubmission
366+
);
367+
336368
/// @notice rewardsUpdater is responsible for submitting DistributionRoots, only owner can set rewardsUpdater
337369
event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater);
338370

@@ -515,6 +547,40 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE
515547
OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
516548
) external;
517549

550+
/**
551+
* @notice Creates a new unique stake rewards submission for an operator set, to be split amongst the operators and
552+
* set of stakers delegated to operators. The operators have to both be registered and allocate unique slashable stake to the operator set to be rewarded.
553+
* @param operatorSet The operator set for which the rewards are being submitted
554+
* @param rewardsSubmissions The rewards submissions being created
555+
* @dev Expected to be called by the AVS that created the operator set
556+
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
557+
* @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
558+
* @dev The tokens are sent to the `RewardsCoordinator` contract
559+
* @dev Strategies must be in ascending order of addresses to check for duplicates
560+
* @dev This function will revert if the `rewardsSubmissions` is malformed.
561+
*/
562+
function createUniqueStakeRewardsSubmission(
563+
OperatorSet calldata operatorSet,
564+
RewardsSubmission[] calldata rewardsSubmissions
565+
) external;
566+
567+
/**
568+
* @notice Creates a new total stake rewards submission for an operator set, to be split amongst the operators and
569+
* set of stakers delegated to operators. The operators have to just be registered to the operator set to be rewarded.
570+
* @param operatorSet The operator set for which the rewards are being submitted
571+
* @param rewardsSubmissions The rewards submissions being created
572+
* @dev Expected to be called by the AVS that created the operator set
573+
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
574+
* @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
575+
* @dev The tokens are sent to the `RewardsCoordinator` contract
576+
* @dev Strategies must be in ascending order of addresses to check for duplicates
577+
* @dev This function will revert if the `rewardsSubmissions` is malformed.
578+
*/
579+
function createTotalStakeRewardsSubmission(
580+
OperatorSet calldata operatorSet,
581+
RewardsSubmission[] calldata rewardsSubmissions
582+
) external;
583+
518584
/**
519585
* @notice Claim rewards against a given root (read from _distributionRoots[claim.rootIndex]).
520586
* Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for,

0 commit comments

Comments
 (0)