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 #315

Merged
merged 14 commits into from
Nov 15, 2024
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[submodule "lib/eigenlayer-contracts"]
path = lib/eigenlayer-contracts
url = https://github.com/Layr-labs/eigenlayer-contracts
branch = dev
branch = feat/rewards-v2
0xrajath marked this conversation as resolved.
Show resolved Hide resolved
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/Openzeppelin/openzeppelin-contracts
Expand Down
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
Submodule eigenlayer-contracts updated 28 files
+1 −1 .github/workflows/coverage.yml
+1 −1 lib/forge-std
+1 −1 script/admin/mainnet/Mainnet_Unpause_Deposits.s.sol
+1 −1 script/deploy/holesky/Preprod_Upgrade_bEIGEN_and_EIGEN.s.sol
+1 −1 script/deploy/holesky/bEIGEN_and_EIGEN_upgrade.s.sol
+1 −1 script/deploy/holesky/upgrade_preprod_rewardsCoordinator.s.sol
+1 −1 script/deploy/local/Deploy_From_Scratch.s.sol
+1 −1 script/deploy/mainnet/EIGEN_timelock_reduction.s.sol
+1 −1 script/deploy/mainnet/EIGEN_upgrade.s.sol
+1 −1 script/deploy/mainnet/EigenPod_Minor_Upgrade_Deploy.s.sol
+1 −1 script/deploy/mainnet/M2_Mainnet_Upgrade.s.sol
+1 −1 script/deploy/mainnet/bEIGEN_timelock_reduction.s.sol
+1 −1 script/deploy/mainnet/bEIGEN_upgrade.s.sol
+1 −1 script/whitelist/delegationFaucet/DeployDelegationFaucet.sol
+280 −48 src/contracts/core/RewardsCoordinator.sol
+11 −2 src/contracts/core/RewardsCoordinatorStorage.sol
+153 −4 src/contracts/interfaces/IRewardsCoordinator.sol
+1 −1 src/test/EigenLayerDeployer.t.sol
+1 −1 src/test/integration/IntegrationDeployer.t.sol
+1 −1 src/test/integration/TimeMachine.t.sol
+1 −1 src/test/integration/mocks/BeaconChainMock.t.sol
+1 −1 src/test/integration/users/User.t.sol
+1 −1 src/test/mocks/Reenterer.sol
+1 −1 src/test/unit/PausableUnit.t.sol
+1 −1 src/test/unit/PauserRegistryUnit.t.sol
+1 −1 src/test/unit/StrategyBaseUnit.t.sol
+1 −1 src/test/utils/EigenLayerUnitTestBase.sol
+1 −1 src/test/utils/EigenPodUser.t.sol
143 changes: 115 additions & 28 deletions src/ServiceManagerBase.sol
0xrajath marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
* @param _metadataURI is the metadata URI for the AVS
* @dev only callable by the owner
*/
function updateAVSMetadataURI(string memory _metadataURI) public virtual onlyOwner {
function updateAVSMetadataURI(
string memory _metadataURI
) public virtual onlyOwner {
_avsDirectory.updateAVSMetadataURI(_metadataURI);
}

Expand All @@ -87,25 +89,92 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
* @dev This function will revert if the `rewardsSubmission` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
*/
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions)
public
virtual
onlyRewardsInitiator
{
function createAVSRewardsSubmission(
IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions
) public virtual onlyRewardsInitiator {
for (uint256 i = 0; i < rewardsSubmissions.length; ++i) {
// transfer token to ServiceManager and approve RewardsCoordinator to transfer again
// in createAVSRewardsSubmission() call
rewardsSubmissions[i].token.transferFrom(msg.sender, address(this), rewardsSubmissions[i].amount);
uint256 allowance =
rewardsSubmissions[i].token.allowance(address(this), address(_rewardsCoordinator));
rewardsSubmissions[i].token.transferFrom(
msg.sender,
address(this),
rewardsSubmissions[i].amount
);
uint256 allowance = rewardsSubmissions[i].token.allowance(
address(this),
address(_rewardsCoordinator)
);
rewardsSubmissions[i].token.approve(
address(_rewardsCoordinator), rewardsSubmissions[i].amount + allowance
address(_rewardsCoordinator),
rewardsSubmissions[i].amount + allowance
);
}

_rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions);
}

/**
* @notice Creates a new performance-based rewards submission, to be split amongst the operators and
* set of stakers delegated to operators who are registered to this `avs`.
* @param performanceRewardsSubmissions The performance rewards submissions being created.
* @dev Only callabe by the permissioned rewardsInitiator address
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
* @dev The tokens are sent to the `RewardsCoordinator` contract
* @dev This contract needs a token approval of sum of all `operatorRewards` in the `performanceRewardsSubmissions`, before calling this function.
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev Operators must be in ascending order of addresses to check for duplicates.
* @dev This function will revert if the `performanceRewardsSubmissions` is malformed.
*/
function createAVSPerformanceRewardsSubmission(
IRewardsCoordinator.PerformanceRewardsSubmission[]
calldata performanceRewardsSubmissions
) public virtual onlyRewardsInitiator {
for (uint256 i = 0; i < performanceRewardsSubmissions.length; ++i) {
// Calculate total amount of token to transfer
uint256 totalAmount = 0;
for (
uint256 j = 0;
j < performanceRewardsSubmissions[i].operatorRewards.length;
++j
) {
totalAmount += performanceRewardsSubmissions[i]
.operatorRewards[j]
.amount;
}

// Transfer token to ServiceManager and approve RewardsCoordinator to transfer again
// in createAVSPerformanceRewardsSubmission() call
performanceRewardsSubmissions[i].token.transferFrom(
msg.sender,
address(this),
totalAmount
);
uint256 allowance = performanceRewardsSubmissions[i]
.token
.allowance(address(this), address(_rewardsCoordinator));
performanceRewardsSubmissions[i].token.approve(
address(_rewardsCoordinator),
totalAmount + allowance
);
}

_rewardsCoordinator.createAVSPerformanceRewardsSubmission(
address(this),
performanceRewardsSubmissions
);
}

/**
* @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract.
* @param claimer The address of the entity that can call `processClaim` on behalf of the earner
* @dev Only callabe by the permissioned rewardsInitiator address
*/
function setClaimerFor(
address claimer
) public virtual onlyRewardsInitiator {
stevennevins marked this conversation as resolved.
Show resolved Hide resolved
_rewardsCoordinator.setClaimerFor(claimer);
}

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS
* @param operator The address of the operator to register.
Expand All @@ -122,7 +191,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
* @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(address operator) public virtual onlyRegistryCoordinator {
function deregisterOperatorFromAVS(
address operator
) public virtual onlyRegistryCoordinator {
_avsDirectory.deregisterOperatorFromAVS(operator);
}

Expand All @@ -131,7 +202,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
* @param newRewardsInitiator The new rewards initiator address
* @dev only callable by the owner
*/
function setRewardsInitiator(address newRewardsInitiator) external onlyOwner {
function setRewardsInitiator(
address newRewardsInitiator
) external onlyOwner {
_setRewardsInitiator(newRewardsInitiator);
}

Expand All @@ -146,7 +219,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
* @dev No guarantee is made on uniqueness of each element in the returned array.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies() external virtual view returns (address[] memory) {
function getRestakeableStrategies()
external
view
virtual
returns (address[] memory)
{
uint256 quorumCount = _registryCoordinator.quorumCount();

if (quorumCount == 0) {
Expand All @@ -161,10 +239,13 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
address[] memory restakedStrategies = new address[](strategyCount);
uint256 index = 0;
for (uint256 i = 0; i < _registryCoordinator.quorumCount(); i++) {
uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(uint8(i));
uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(
uint8(i)
);
for (uint256 j = 0; j < strategyParamsLength; j++) {
restakedStrategies[index] =
address(_stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy);
restakedStrategies[index] = address(
_stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy
);
index++;
}
}
Expand All @@ -178,35 +259,41 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
* @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness
* of each element in the returned array. The off-chain service should do that validation separately
*/
function getOperatorRestakedStrategies(address operator)
external
virtual
view
returns (address[] memory)
{
function getOperatorRestakedStrategies(
address operator
) external view virtual returns (address[] memory) {
bytes32 operatorId = _registryCoordinator.getOperatorId(operator);
uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap(operatorId);
uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap(
operatorId
);

if (operatorBitmap == 0 || _registryCoordinator.quorumCount() == 0) {
return new address[](0);
}

// Get number of strategies for each quorum in operator bitmap
bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray(operatorBitmap);
bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray(
operatorBitmap
);
uint256 strategyCount;
for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) {
strategyCount += _stakeRegistry.strategyParamsLength(uint8(operatorRestakedQuorums[i]));
strategyCount += _stakeRegistry.strategyParamsLength(
uint8(operatorRestakedQuorums[i])
);
}

// Get strategies for each quorum in operator bitmap
address[] memory restakedStrategies = new address[](strategyCount);
uint256 index = 0;
for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) {
uint8 quorum = uint8(operatorRestakedQuorums[i]);
uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(quorum);
uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(
quorum
);
for (uint256 j = 0; j < strategyParamsLength; j++) {
restakedStrategies[index] =
address(_stakeRegistry.strategyParamsByIndex(quorum, j).strategy);
restakedStrategies[index] = address(
_stakeRegistry.strategyParamsByIndex(quorum, j).strategy
);
index++;
}
}
Expand Down
33 changes: 31 additions & 2 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,37 @@ interface IServiceManager is IServiceManagerUI {
* @dev This function will revert if the `rewardsSubmission` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
*/
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external;
function createAVSRewardsSubmission(
IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions
) external;

/**
* @notice Creates a new performance-based rewards submission on behalf of an AVS, to be split amongst the operators and
* set of stakers delegated to operators who are registered to the `avs`.
* @param performanceRewardsSubmissions The performance rewards submissions being created
* @dev Only callabe by the permissioned rewardsInitiator address
* @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`
* @dev The tokens are sent to the `RewardsCoordinator` contract
* @dev This contract needs a token approval of sum of all `operatorRewards` in the `performanceRewardsSubmissions`, before calling this function.
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev Operators must be in ascending order of addresses to check for duplicates.
* @dev This function will revert if the `performanceRewardsSubmissions` is malformed.
*/
function createAVSPerformanceRewardsSubmission(
IRewardsCoordinator.PerformanceRewardsSubmission[]
calldata performanceRewardsSubmissions
) external;

/**
* @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract.
* @param claimer The address of the entity that can call `processClaim` on behalf of the earner
* @dev Only callabe by the permissioned rewardsInitiator address
*/
function setClaimerFor(address claimer) external;

// EVENTS
event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator);
event RewardsInitiatorUpdated(
address prevRewardsInitiator,
address newRewardsInitiator
);
}
60 changes: 60 additions & 0 deletions src/unaudited/ECDSAServiceManagerBase.sol
0xrajath marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ abstract contract ECDSAServiceManagerBase is
_createAVSRewardsSubmission(rewardsSubmissions);
}

/// @inheritdoc IServiceManager
function createAVSPerformanceRewardsSubmission(
IRewardsCoordinator.PerformanceRewardsSubmission[]
calldata performanceRewardsSubmissions
) external virtual onlyRewardsInitiator {
_createAVSPerformanceRewardsSubmission(performanceRewardsSubmissions);
}

/// @inheritdoc IServiceManager
function setClaimerFor(
address claimer
) public virtual onlyRewardsInitiator {
0xrajath marked this conversation as resolved.
Show resolved Hide resolved
IRewardsCoordinator(rewardsCoordinator).setClaimerFor(claimer);
}

/// @inheritdoc IServiceManagerUI
function registerOperatorToAVS(
address operator,
Expand Down Expand Up @@ -203,6 +218,51 @@ abstract contract ECDSAServiceManagerBase is
);
}

/**
* @notice Creates a new performance-based rewards submission, to be split amongst the operators and
* set of stakers delegated to operators who are registered to this `avs`.
* @param performanceRewardsSubmissions The performance rewards submissions being created.
*/
function _createAVSPerformanceRewardsSubmission(
IRewardsCoordinator.PerformanceRewardsSubmission[]
calldata performanceRewardsSubmissions
) internal virtual {
for (uint256 i = 0; i < performanceRewardsSubmissions.length; ++i) {
// Calculate total amount of token to transfer
uint256 totalAmount = 0;
for (
uint256 j = 0;
j < performanceRewardsSubmissions[i].operatorRewards.length;
++j
) {
totalAmount += performanceRewardsSubmissions[i]
.operatorRewards[j]
.amount;
}

// Transfer token to ServiceManager and approve RewardsCoordinator to transfer again
// in createAVSPerformanceRewardsSubmission() call
performanceRewardsSubmissions[i].token.transferFrom(
msg.sender,
address(this),
totalAmount
);
uint256 allowance = performanceRewardsSubmissions[i]
.token
.allowance(address(this), rewardsCoordinator);
performanceRewardsSubmissions[i].token.approve(
rewardsCoordinator,
totalAmount + allowance
);
}

IRewardsCoordinator(rewardsCoordinator)
.createAVSPerformanceRewardsSubmission(
address(this),
performanceRewardsSubmissions
);
}

/**
* @notice Retrieves the addresses of all strategies that are part of the current quorum.
* @dev Fetches the quorum configuration from the ECDSAStakeRegistry and extracts the strategy addresses.
Expand Down
Loading
Loading