diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 58bf3a38..1133cd27 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -114,7 +114,8 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { } /** - * @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`. + * @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` diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index ad953ec0..a557c4f3 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -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 + ); } diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 57326bea..dd423eba 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -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 { + IRewardsCoordinator(rewardsCoordinator).setClaimerFor(claimer); + } + /// @inheritdoc IServiceManagerUI function registerOperatorToAVS( address operator, @@ -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.