Skip to content

feat: incentives update #1214

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

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7220246
feat: initial sketches of several ideas
MinisculeTarantula Jan 30, 2025
99f94c2
feat: improvements to config, distributor, comments
MinisculeTarantula Jan 31, 2025
fee31c1
feat: access control + normalized streams
MinisculeTarantula Feb 4, 2025
9d5ad6b
chore: delete unused contracts and interfaces
MinisculeTarantula Feb 4, 2025
25c894f
chore: split up files, put them in /incentives/ folder
MinisculeTarantula Feb 6, 2025
485c1f8
feat: modifications for fixed inflation rate
MinisculeTarantula Feb 7, 2025
2e877ad
chore: add more functions + events to interface
MinisculeTarantula Feb 7, 2025
daa57bd
chore: improve IProgrammaticIncentivesConfig documenation
MinisculeTarantula Feb 10, 2025
faa526a
feat: initial sketches of several ideas
MinisculeTarantula Jan 30, 2025
48f7ce0
feat: improvements to config, distributor, comments
MinisculeTarantula Jan 31, 2025
86e54a3
feat: access control + normalized streams
MinisculeTarantula Feb 4, 2025
e3408f7
chore: delete unused contracts and interfaces
MinisculeTarantula Feb 4, 2025
34a9c22
chore: split up files, put them in /incentives/ folder
MinisculeTarantula Feb 6, 2025
4387109
feat: modifications for fixed inflation rate
MinisculeTarantula Feb 7, 2025
8bf33b8
chore: add more functions + events to interface
MinisculeTarantula Feb 7, 2025
ecf95da
chore: improve IProgrammaticIncentivesConfig documenation
MinisculeTarantula Feb 10, 2025
c5faf2c
Merge branch 'feat-incentives-update' of https://github.com/Layr-Labs…
MinisculeTarantula Feb 11, 2025
2518da3
feat: modify struct field for clarity
MinisculeTarantula Feb 11, 2025
4fa9503
feat: support changing stream rates in library
MinisculeTarantula Feb 11, 2025
d7c521f
feat: support for multi-TIMESCALE distributions
MinisculeTarantula Feb 12, 2025
1c28fee
feat: more opinionated interface for incentives submissions
MinisculeTarantula Mar 3, 2025
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
19 changes: 19 additions & 0 deletions src/contracts/core/RewardsCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,25 @@ contract RewardsCoordinator is
}
}

function distributeIncentives(
uint256 incentivesVersion,
RewardsSubmission calldata incentivesSubmission,
bytes calldata additionalData
) external onlyWhenNotPaused(PAUSED_INCENTIVES_DISTRIBUTION) onlyRewardsForAllSubmitter nonReentrant {
uint256 nonce = submissionNonce[msg.sender];
bytes32 incentivesSubmissionHash = keccak256(abi.encode(msg.sender, nonce, incentivesSubmission));

_validateRewardsSubmission(incentivesSubmission);

isRewardsSubmissionForAllEarnersHash[msg.sender][incentivesSubmissionHash] = true;
submissionNonce[msg.sender] = nonce + 1;

emit IncentivesDistributed(
msg.sender, nonce, incentivesSubmissionHash, incentivesSubmission, incentivesVersion, additionalData
);
incentivesSubmission.token.safeTransferFrom(msg.sender, address(this), incentivesSubmission.amount);
}

/// @inheritdoc IRewardsCoordinator
function createOperatorDirectedAVSRewardsSubmission(
address avs,
Expand Down
7 changes: 6 additions & 1 deletion src/contracts/core/RewardsCoordinatorStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 distributeIncentives
uint8 internal constant PAUSED_INCENTIVES_DISTRIBUTION = 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 @@ -133,6 +135,9 @@ abstract contract RewardsCoordinatorStorage is IRewardsCoordinator {
mapping(address avs => mapping(bytes32 hash => bool valid)) public
isOperatorDirectedOperatorSetRewardsSubmissionHash;

/// @notice Returns whether a `hash` is a `valid` rewards submission for all earners hash for a given `avs`.
mapping(address avs => mapping(bytes32 hash => bool valid)) public isIncentivesSubmissionHash;

// Construction

constructor(
Expand Down Expand Up @@ -164,5 +169,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;
}
213 changes: 213 additions & 0 deletions src/contracts/incentives/ProgrammaticIncentivesConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
import "./Vectors.sol";
import "./Streams.sol";

interface IProgrammaticIncentivesConfig {
// events for rewards-boost whitelist
event AVSAddedToWhitelist(address indexed avs);
event AVSRemovedFromWhitelist(address indexed avs);
event TokenAddedToWhitelist(address indexed token);
event TokenRemovedFromWhitelist(address indexed token);

error InputLengthMismatch();

// @notice Returns the weighting vector at `vectorIndex` (currently used to compare Strategy shares)
function vector(uint256 vectorIndex) external view returns (VectorEntry[] memory);

/**
* @notice Getter function for querying whether a `key` is in the `vectorIndex`-th vector
* @dev Will not revert even in the event that the `vectorIndex`-th vector does not exist
*/
function isInVector(uint256 vectorIndex, address key) external view returns (bool);

// @notice Returns the total number of existing vectors
function numberOfWeightingVectors() external view returns (uint256);

/**
* @notice Getter function for fetching the index of a `key` within the `vectorIndex`-th vector
* @dev Reverts if the key is not in the vector
*/
function keyIndex(uint256 vectorIndex, address key) external view returns (uint256);

// TODO: determine if this function should be called by the recipient itself or can be called on behalf of them
// @notice Claims all pending inflationary tokens for the `substreamRecipient`
function claimForSubstream(address substreamRecipient) external;

// contract owner-only functions
// @notice mapping avs => whether or not the avs is considered for incentives types that are filtered by the AVS whitelist
function avsIsWhitelisted(address avs) external view returns (bool);

// @notice Called by the contract owner modify the whitelist status of an AVS
function editAVSWhitelistStatus(address avs, bool newWhitelistStatus) external;

// @notice mapping token => whether or not the token is considered for incentives types that are filtered by the token whitelist
function tokenIsWhitelisted(address avs) external view returns (bool);

// TODO: there may be additional inputs required, especially if e.g. this function is supposed to deploy a token oracle contract
// @notice Called by the contract owner modify the whitelist status of a token
function editTokenWhitelistStatus(address avs, bool newWhitelistStatus) external;

// @notice Called by the contract owner to change the relative amount of inflation going to the `substreamRecipient`
function updateSubstreamWeight(address substreamRecipient, uint256 newWeight) external;

// @notice Called by the contract owner to create a new vector indicating how different tokens are weighted relative to one another
function createNewVector(VectorEntry[] memory initialVectorEntries) external;

// @notice Called by the contract owner to add additional entries to an existing vector
function addEntries(uint256 vectorIndex, VectorEntry[] memory newEntries) external;

// @notice Called by the contract owner to remove entries from an existing vector
function removeKeys(uint256 vectorIndex, address[] memory keysToRemove, uint256[] memory keyIndices) external;


}

/**
* @notice Central contract for managing the configuration of many Programmatic Incentives parameters.
* Uses LinearVectors and the associated LinearVectorOps library to manage lists of strategies & weights used by Distributor contracts.
*/
contract ProgrammaticIncentivesConfig is Initializable, OwnableUpgradeable, IProgrammaticIncentivesConfig {
using LinearVectorOps for *;
using StreamMath for *;

// 4% of initial supply of EIGEN
uint256 constant internal CURRENT_YEARLY_INFLATION = 66945866731386400000000160;

// @inheritdoc IProgrammaticIncentivesConfig
mapping(address => bool) public avsIsWhitelisted;

// @inheritdoc IProgrammaticIncentivesConfig
mapping(address => bool) public tokenIsWhitelisted;

LinearVector[] internal _weightingVectors;

// @notice single inflationary stream of EIGEN tokens
// TODO: figure out what automatic getter(s) for this look like!
NonNormalizedStream public stream;

address public bEIGEN;

constructor() {
_disableInitializers();
}

function initialize(address initialOwner, address _bEIGEN) external initializer {
_transferOwnership(initialOwner);
bEIGEN = _bEIGEN;
stream.rate = CURRENT_YEARLY_INFLATION * TIMESCALE / 365 days;
stream.lastUpdatedTimestamp = block.timestamp;
// TODO: initiate substreams?
}

// @inheritdoc IProgrammaticIncentivesConfig
function editAVSWhitelistStatus(address avs, bool newWhitelistStatus) external onlyOwner {
if (avsIsWhitelisted[avs] && !newWhitelistStatus) {
avsIsWhitelisted[avs] = false;
emit AVSRemovedFromWhitelist(avs);
} else if (!avsIsWhitelisted[avs] && newWhitelistStatus) {
avsIsWhitelisted[avs] = true;
emit AVSAddedToWhitelist(avs);
}
}

// @inheritdoc IProgrammaticIncentivesConfig
function editTokenWhitelistStatus(address token, bool newWhitelistStatus) external onlyOwner {
if (tokenIsWhitelisted[token] && !newWhitelistStatus) {
tokenIsWhitelisted[token] = false;
emit TokenRemovedFromWhitelist(token);
} else if (!tokenIsWhitelisted[token] && newWhitelistStatus) {
tokenIsWhitelisted[token] = true;
emit TokenAddedToWhitelist(token);
}
}

// @inheritdoc IProgrammaticIncentivesConfig
function claimForSubstream(address substreamRecipient) external {
// TODO: access control? could just use msg.sender instead of having `substreamRecipient` input
stream.claimForSubstream(substreamRecipient, bEIGEN);
}

// @inheritdoc IProgrammaticIncentivesConfig
function updateSubstreamWeight(address substreamRecipient, uint256 newWeight) external onlyOwner {
stream.updateSubstreamWeight(substreamRecipient, newWeight, bEIGEN);
}

// @inheritdoc IProgrammaticIncentivesConfig
function createNewVector(VectorEntry[] memory initialVectorEntries) external onlyOwner {
_weightingVectors.push();
_addEntries({
vectorIndex: _weightingVectors.length - 1,
newEntries: initialVectorEntries
});
}

// @inheritdoc IProgrammaticIncentivesConfig
function numberOfWeightingVectors() external view returns (uint256) {
return _weightingVectors.length;
}

// @inheritdoc IProgrammaticIncentivesConfig
function vector(uint256 vectorIndex) external view returns (VectorEntry[] memory) {
return _weightingVectors[vectorIndex].vector;
}

// @inheritdoc IProgrammaticIncentivesConfig
function isInVector(uint256 vectorIndex, address key) external view returns (bool) {
return _weightingVectors[vectorIndex].isInVector[key];
}

// @inheritdoc IProgrammaticIncentivesConfig
function keyIndex(uint256 vectorIndex, address key) external view returns (uint256) {
return _weightingVectors[vectorIndex].findKeyIndex(key);
}

function _addEntries(uint256 vectorIndex, VectorEntry[] memory newEntries) internal {
for (uint256 i = 0; i < newEntries.length; ++i) {
_weightingVectors[vectorIndex].addEntry(newEntries[i]);
}
}

function _removeKeys(uint256 vectorIndex, address[] memory keysToRemove, uint256[] memory keyIndices) internal {
require(keysToRemove.length == keyIndices.length, InputLengthMismatch());
for (uint256 i = 0; i < keysToRemove.length; ++i) {
_weightingVectors[vectorIndex].removeKey(keysToRemove[i], keyIndices[i]);
}
}

// @inheritdoc IProgrammaticIncentivesConfig
function addEntries(uint256 vectorIndex, VectorEntry[] memory newEntries) external onlyOwner {
_addEntries(vectorIndex, newEntries);
}

// @inheritdoc IProgrammaticIncentivesConfig
function removeKeys(uint256 vectorIndex, address[] memory keysToRemove, uint256[] memory keyIndices) external onlyOwner {
_removeKeys(vectorIndex, keysToRemove, keyIndices);
}
}























Loading
Loading