Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rewards v2.1 #1011

Open
wants to merge 1 commit into
base: slashing-magnitudes-fixes
Choose a base branch
from
Open
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
118 changes: 61 additions & 57 deletions docs/storage-report/RewardsCoordinator.md

Large diffs are not rendered by default.

78 changes: 41 additions & 37 deletions docs/storage-report/RewardsCoordinatorStorage.md

Large diffs are not rendered by default.

61 changes: 55 additions & 6 deletions src/contracts/core/RewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ contract RewardsCoordinator is
PermissionControllerMixin
{
using SafeERC20 for IERC20;
using OperatorSetLib for OperatorSet;

modifier onlyRewardsUpdater() {
require(msg.sender == rewardsUpdater, UnauthorizedCaller());
Expand Down Expand Up @@ -177,6 +178,34 @@ contract RewardsCoordinator is
}
}

/// @inheritdoc IRewardsCoordinator
function createOperatorDirectedOperatorSetRewardsSubmission(
OperatorSet calldata operatorSet,
OperatorDirectedRewardsSubmission[] calldata rewardsSubmissions
)
external
onlyWhenNotPaused(PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION)
checkCanCall(operatorSet.avs)
nonReentrant
{
require(allocationManager.isOperatorSet(operatorSet), InvalidOperatorSet());
for (uint256 i = 0; i < rewardsSubmissions.length; i++) {
OperatorDirectedRewardsSubmission calldata rewardsSubmission = rewardsSubmissions[i];
uint256 nonce = submissionNonce[operatorSet.avs];
bytes32 rewardsSubmissionHash = keccak256(abi.encode(operatorSet.avs, nonce, rewardsSubmission));

uint256 totalAmount = _validateOperatorDirectedRewardsSubmission(rewardsSubmission);

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

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

/// @inheritdoc IRewardsCoordinator
function processClaim(
RewardsMerkleClaim calldata claim,
Expand Down Expand Up @@ -268,8 +297,8 @@ contract RewardsCoordinator is
uint16 split
) external onlyWhenNotPaused(PAUSED_OPERATOR_AVS_SPLIT) checkCanCall(operator) {
uint32 activatedAt = uint32(block.timestamp) + activationDelay;
uint16 oldSplit = _getOperatorSplit(operatorAVSSplitBips[operator][avs]);
_setOperatorSplit(operatorAVSSplitBips[operator][avs], split, activatedAt);
uint16 oldSplit = _getOperatorSplit(_operatorAVSSplitBips[operator][avs]);
_setOperatorSplit(_operatorAVSSplitBips[operator][avs], split, activatedAt);

emit OperatorAVSSplitBipsSet(msg.sender, operator, avs, activatedAt, oldSplit, split);
}
Expand All @@ -280,12 +309,27 @@ contract RewardsCoordinator is
uint16 split
) external onlyWhenNotPaused(PAUSED_OPERATOR_PI_SPLIT) checkCanCall(operator) {
uint32 activatedAt = uint32(block.timestamp) + activationDelay;
uint16 oldSplit = _getOperatorSplit(operatorPISplitBips[operator]);
_setOperatorSplit(operatorPISplitBips[operator], split, activatedAt);
uint16 oldSplit = _getOperatorSplit(_operatorPISplitBips[operator]);
_setOperatorSplit(_operatorPISplitBips[operator], split, activatedAt);

emit OperatorPISplitBipsSet(msg.sender, operator, activatedAt, oldSplit, split);
}

/// @inheritdoc IRewardsCoordinator
function setOperatorSetSplit(
address operator,
OperatorSet calldata operatorSet,
uint16 split
) external onlyWhenNotPaused(PAUSED_OPERATOR_SET_OPERATOR_SPLIT) checkCanCall(operator) {
require(allocationManager.isOperatorSet(operatorSet), InvalidOperatorSet());

uint32 activatedAt = uint32(block.timestamp) + activationDelay;
uint16 oldSplit = _getOperatorSplit(_operatorOperatorSetSplitBips[operator][operatorSet.key()]);
_setOperatorSplit(_operatorOperatorSetSplitBips[operator][operatorSet.key()], split, activatedAt);

emit OperatorOperatorSetSplitBipsSet(msg.sender, operator, operatorSet, activatedAt, oldSplit, split);
}

/// @inheritdoc IRewardsCoordinator
function setRewardsUpdater(
address _rewardsUpdater
Expand Down Expand Up @@ -602,14 +646,19 @@ contract RewardsCoordinator is

/// @inheritdoc IRewardsCoordinator
function getOperatorAVSSplit(address operator, address avs) external view returns (uint16) {
return _getOperatorSplit(operatorAVSSplitBips[operator][avs]);
return _getOperatorSplit(_operatorAVSSplitBips[operator][avs]);
}

/// @inheritdoc IRewardsCoordinator
function getOperatorPISplit(
address operator
) external view returns (uint16) {
return _getOperatorSplit(operatorPISplitBips[operator]);
return _getOperatorSplit(_operatorPISplitBips[operator]);
}

/// @inheritdoc IRewardsCoordinator
function getOperatorSetSplit(address operator, OperatorSet calldata operatorSet) external view returns (uint16) {
return _getOperatorSplit(_operatorOperatorSetSplitBips[operator][operatorSet.key()]);
}

/// @inheritdoc IRewardsCoordinator
Expand Down
29 changes: 20 additions & 9 deletions src/contracts/core/RewardsCoordinatorStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
uint8 internal constant PAUSED_REWARD_ALL_STAKERS_AND_OPERATORS = 4;
/// @dev Index for flag that pauses calling createOperatorDirectedAVSRewardsSubmission
uint8 internal constant PAUSED_OPERATOR_DIRECTED_AVS_REWARDS_SUBMISSION = 5;
/// @dev Index for flag that pauses calling setOperatorSetPerformanceRewardsSubmission
uint8 internal constant PAUSED_OPERATOR_DIRECTED_OPERATOR_SET_REWARDS_SUBMISSION = 6;
/// @dev Index for flag that pauses calling setOperatorAVSSplit
uint8 internal constant PAUSED_OPERATOR_AVS_SPLIT = 6;
uint8 internal constant PAUSED_OPERATOR_AVS_SPLIT = 7;
/// @dev Index for flag that pauses calling setOperatorPISplit
uint8 internal constant PAUSED_OPERATOR_PI_SPLIT = 7;
uint8 internal constant PAUSED_OPERATOR_PI_SPLIT = 8;
/// @dev Index for flag that pauses calling setOperatorSetSplit
uint8 internal constant PAUSED_OPERATOR_SET_OPERATOR_SPLIT = 9;

/// @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 @@ -115,14 +119,21 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {

// Construction

/// @notice Mapping: avs => operatorDirectedAVSRewardsSubmissionHash => bool to check if operator-directed rewards submission hash has been submitted
mapping(address => mapping(bytes32 => bool)) public isOperatorDirectedAVSRewardsSubmissionHash;
/// @notice Returns whether a `hash` is a `valid` operator set performance rewards submission hash for a given `avs`.
mapping(address avs => mapping(bytes32 hash => bool valid)) public isOperatorDirectedAVSRewardsSubmissionHash;

/// @notice Mapping: operator => avs => OperatorSplit. The split an operator takes for a specific AVS.
mapping(address => mapping(address => OperatorSplit)) internal operatorAVSSplitBips;
/// @notice Returns whether a `hash` is a `valid` operator set performance rewards submission hash for a given `avs`.
mapping(address avs => mapping(bytes32 hash => bool valid)) public isOperatorSetPerformanceRewardsSubmissionHash;

/// @notice Mapping: operator => OperatorPISplit. The split an operator takes for Programmatic Incentives.
mapping(address => OperatorSplit) internal operatorPISplitBips;
/// @notice Returns the `split` an `operator` takes for an `avs`.
mapping(address operator => mapping(address avs => OperatorSplit split)) internal _operatorAVSSplitBips;

/// @notice Returns the `split` an `operator` takes for Programmatic Incentives.
mapping(address operator => OperatorSplit split) internal _operatorPISplitBips;

/// @notice Returns the `split` an `operator` takes for a given operator set.
mapping(address operator => mapping(bytes32 operatorSetKey => OperatorSplit split)) internal
_operatorOperatorSetSplitBips;

constructor(
IDelegationManager _delegationManager,
Expand Down Expand Up @@ -153,5 +164,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[37] private __gap;
uint256[35] private __gap;
Copy link
Contributor

Choose a reason for hiding this comment

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

The gaps count seems to be wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will run the new make update-gap command once it's merged, nice callout.

}
55 changes: 55 additions & 0 deletions src/contracts/interfaces/IRewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.27;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../libraries/OperatorSetLib.sol";
import "./IPauserRegistry.sol";
import "./IStrategy.sol";

Expand All @@ -27,6 +28,8 @@ interface IRewardsCoordinatorErrors {
error NewRootMustBeForNewCalculatedPeriod();
/// @dev Thrown when rewards end timestamp has not elapsed.
error RewardsEndTimestampNotElapsed();
/// @dev Thrown when an invalid operator set is provided.
error InvalidOperatorSet();

/// Rewards Submissions

Expand Down Expand Up @@ -277,6 +280,22 @@ interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes {
OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission
);

/**
* @notice Emitted when an AVS creates a valid performance based `OperatorDirectedRewardsSubmission`
* @param caller The address calling `createOperatorDirectedOperatorSetRewardsSubmission`.
* @param operatorSet The operatorSet on behalf of which the performance rewards are being submitted.
* @param performanceRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `performanceRewardsSubmission`).
* @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash.
* @param performanceRewardsSubmission The Performance Rewards Submission. Contains the token, start timestamp, duration, description and, strategy and multipliers.
*/
event OperatorDirectedOperatorSetRewardsSubmissionCreated(
address indexed caller,
OperatorSet indexed operatorSet,
bytes32 indexed performanceRewardsSubmissionHash,
uint256 submissionNonce,
OperatorDirectedRewardsSubmission performanceRewardsSubmission
);

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

Expand Down Expand Up @@ -321,6 +340,24 @@ interface IRewardsCoordinatorEvents is IRewardsCoordinatorTypes {
uint16 newOperatorPISplitBips
);

/**
* @notice Emitted when the operator split for a given operatorSet is set.
* @param caller The address calling `setOperatorSetSplit`.
* @param operator The operator on behalf of which the split is being set.
* @param operatorSet The operatorSet for which the split is being set.
* @param activatedAt The timestamp at which the split will be activated.
* @param oldOperatorSetSplitBips The old split for the operator for the operatorSet.
* @param newOperatorSetSplitBips The new split for the operator for the operatorSet.
*/
event OperatorOperatorSetSplitBipsSet(
address indexed caller,
address indexed operator,
OperatorSet indexed operatorSet,
uint32 activatedAt,
uint16 oldOperatorSetSplitBips,
uint16 newOperatorSetSplitBips
);

event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer);

/// @notice rootIndex is the specific array index of the newly created root in the storage array
Expand Down Expand Up @@ -420,6 +457,13 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE
OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions
) external;

/// @notice operatorSet parallel of createAVSPerformanceRewardsSubmission
/// @dev sender must be the avs of the given operatorSet
function createOperatorDirectedOperatorSetRewardsSubmission(
OperatorSet calldata operatorSet,
OperatorDirectedRewardsSubmission[] calldata performanceRewardsSubmissions
) 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 Expand Up @@ -522,6 +566,14 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE
*/
function setOperatorPISplit(address operator, uint16 split) external;

/**
* @notice Sets the split for a specific operator for a specific operatorSet.
* @param operator The operator who is setting the split.
* @param operatorSet The operatorSet for which the split is being set by the operator.
* @param split The split for the operator for the specific operatorSet in bips.
*/
function setOperatorSetSplit(address operator, OperatorSet calldata operatorSet, uint16 split) external;

/**
* @notice Sets the permissioned `rewardsUpdater` address which can post new roots
* @dev Only callable by the contract owner
Expand Down Expand Up @@ -570,6 +622,9 @@ interface IRewardsCoordinator is IRewardsCoordinatorErrors, IRewardsCoordinatorE
address operator
) external view returns (uint16);

/// @notice Returns the split for a specific `operator` for a given `operatorSet`
function getOperatorSetSplit(address operator, OperatorSet calldata operatorSet) external view returns (uint16);

/// @notice return the hash of the earner's leaf
function calculateEarnerLeafHash(
EarnerTreeMerkleLeaf calldata leaf
Expand Down
15 changes: 13 additions & 2 deletions src/test/mocks/AllocationManagerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@
pragma solidity ^0.8.9;

import "forge-std/Test.sol";
import "../../contracts/interfaces/IStrategy.sol";
import "../../contracts/libraries/Snapshots.sol";
import "src/contracts/interfaces/IStrategy.sol";
import "src/contracts/libraries/Snapshots.sol";
import "src/contracts/libraries/OperatorSetLib.sol";

contract AllocationManagerMock is Test {
using Snapshots for Snapshots.DefaultWadHistory;
using OperatorSetLib for OperatorSet;

receive() external payable {}
fallback() external payable {}

mapping(bytes32 operatorSetKey => bool) public _isOperatorSet;
mapping(address avs => uint256) public getOperatorSetCount;
mapping(address => mapping(IStrategy => Snapshots.DefaultWadHistory)) internal _maxMagnitudeHistory;

function setIsOperatorSet(OperatorSet memory operatorSet, bool boolean) external {
_isOperatorSet[operatorSet.key()] = boolean;
}

function isOperatorSet(OperatorSet memory operatorSet) external view returns (bool) {
return _isOperatorSet[operatorSet.key()];
}

function setMaxMagnitudes(
address operator,
IStrategy[] calldata strategies,
Expand Down
Loading
Loading