Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/contracts/core/RewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,29 @@ contract RewardsCoordinator is
}
}

/// @inheritdoc IRewardsCoordinator
function createOperatorSetRewardsSubmission(
OperatorSet calldata operatorSet,
RewardsSubmission[] calldata rewardsSubmissions
) external onlyWhenNotPaused(PAUSED_OPERATOR_SET_REWARDS_SUBMISSION) checkCanCall(operatorSet.avs) nonReentrant {
require(allocationManager.isOperatorSet(operatorSet), InvalidOperatorSet());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving a note that we should check if it has allocated stake to that operator set.

for (uint256 i = 0; i < rewardsSubmissions.length; i++) {
RewardsSubmission calldata rewardsSubmission = rewardsSubmissions[i];
uint256 nonce = submissionNonce[operatorSet.avs];
bytes32 rewardsSubmissionHash = keccak256(abi.encode(operatorSet.avs, nonce, rewardsSubmission));

_validateRewardsSubmission(rewardsSubmission);

isOperatorSetRewardsSubmissionHash[operatorSet.avs][rewardsSubmissionHash] = true;
submissionNonce[operatorSet.avs] = nonce + 1;

emit OperatorSetRewardsSubmissionCreated(
msg.sender, rewardsSubmissionHash, operatorSet, nonce, rewardsSubmission
);
rewardsSubmission.token.safeTransferFrom(msg.sender, address(this), rewardsSubmission.amount);
}
}

/// @inheritdoc IRewardsCoordinator
function processClaim(
RewardsMerkleClaim calldata claim,
Expand Down
8 changes: 6 additions & 2 deletions src/contracts/core/RewardsCoordinatorStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
uint8 internal constant PAUSED_OPERATOR_SET_SPLIT = 8;
/// @dev Index for flag that pauses calling setOperatorSetPerformanceRewardsSubmission
uint8 internal constant PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION = 9;
/// @dev Index for flag that pauses calling createOperatorSetRewardsSubmission
uint8 internal constant PAUSED_OPERATOR_SET_REWARDS_SUBMISSION = 10;

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

// Construction
/// @notice Returns whether a `hash` is a `valid` operator set rewards submission hash for a given `avs`.
mapping(address avs => mapping(bytes32 hash => bool valid)) public isOperatorSetRewardsSubmissionHash;

// Construction
constructor(
IDelegationManager _delegationManager,
IStrategyManager _strategyManager,
Expand Down Expand Up @@ -161,5 +165,5 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[35] private __gap;
uint256[34] private __gap;
}
34 changes: 34 additions & 0 deletions src/contracts/interfaces/IRewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,22 @@ interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes {
OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
);

/**
* @notice Emitted when an AVS creates a valid `OperatorSetRewardsSubmission` for an operator set.
* @param caller The address calling `createOperatorSetRewardsSubmission`.
* @param rewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `rewardsSubmission`).
* @param operatorSet The operatorSet on behalf of which the rewards are being submitted.
* @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
* @param rewardsSubmission The Rewards Submission. Contains the token, start timestamp, duration, strategies and multipliers.
*/
event OperatorSetRewardsSubmissionCreated(
address indexed caller,
bytes32 indexed rewardsSubmissionHash,
OperatorSet operatorSet,
uint256 submissionNonce,
RewardsSubmission rewardsSubmission
);

/// @notice rewardsUpdater is responsible for submitting DistributionRoots, only owner can set rewardsUpdater
event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater);

Expand Down Expand Up @@ -515,6 +531,24 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE
OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
) external;

/**
* @notice Creates a new rewards submission for an operator set, to be split amongst the operators and
* set of stakers delegated to operators. The operators have to allocate slashable stake to the operator set to be rewarded.
* @param operatorSet The operator set for which the rewards are being submitted
* @param rewardsSubmissions The rewards submissions being created
* @dev Expected to be called by the AVS that created the operator set
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
* @dev The duration of the `rewardsSubmission` cannot be 0 and must be a multiple of `CALCULATION_INTERVAL_SECONDS`
* @dev The tokens are sent to the `RewardsCoordinator` contract
* @dev The `RewardsCoordinator` contract needs a token approval of sum of all `strategies` in the `rewardsSubmissions`, before calling this function
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev This function will revert if the `rewardsSubmissions` is malformed.
*/
function createOperatorSetRewardsSubmission(
OperatorSet calldata operatorSet,
RewardsSubmission[] calldata rewardsSubmissions
) external;

/**
* @notice Claim rewards against a given root (read from _distributionRoots[claim.rootIndex]).
* Earnings are cumulative so earners don't have to claim against all distribution roots they have earnings for,
Expand Down
Loading