Skip to content

Commit

Permalink
chore: refactor with RewardsCoordinator (#262)
Browse files Browse the repository at this point in the history
  • Loading branch information
8sunyuan authored May 30, 2024
1 parent a7b1851 commit 51e36f1
Show file tree
Hide file tree
Showing 17 changed files with 391 additions and 462 deletions.
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ EigenLayer AVSs ("actively validated services") are protocols that make use of E
**Currently, each AVS needs to implement one thing on-chain:** registration/deregistration conditions that define how an Operator registers for/deregisters from the AVS. This repo provides building blocks to support these functions.

*Eventually,* the core contracts and this repo will be extended to cover other conditions, including:
* payment conditions that define how an Operator is paid for the services it provides
* reward conditions that define how an Operator is rewarded for the services it provides
* slashing conditions that define "malicious behavior" in the context of the AVS, and the punishments for this behavior

*... however, the design for these conditions is still in progress.*
Expand Down Expand Up @@ -102,7 +102,7 @@ The main thing that links an AVS to the EigenLayer core contracts is that when E

These methods ensure that the Operator registering with the AVS is also registered as an Operator in EigenLayer core. In this repo, these methods are called by the `ServiceManagerBase`.

Eventually, Operator slashing and payment for services will be part of the middleware/core relationship, but these features aren't implemented yet and their design is a work in progress.
Eventually, Operator slashing and rewards for services will be part of the middleware/core relationship, but these features aren't implemented yet and their design is a work in progress.

### System Components

Expand Down
2 changes: 1 addition & 1 deletion docs/ServiceManagerBase.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
The `ServiceManagerBase` represents the AVS's address relative to EigenLayer core. When registering or deregistering an operator from an AVS, the AVS's `ServiceManagerBase` communicates this change to the core contracts, allowing the core contracts to maintain an up-to-date view on operator registration status with various AVSs.

*As of M2*:
* Currently, this contract is used by the `AVSDirectory` to keep track of operator registration and deregistration. Eventually, this relationship will be expanded to allow operators to opt in to slashing and payments for services.
* Currently, this contract is used by the `AVSDirectory` to keep track of operator registration and deregistration. Eventually, this relationship will be expanded to allow operators to opt in to slashing and rewards for services.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/experimental/AVS-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This document aims to describe and summarize how actively validated services (AV
- enabling operators to continuously update their commitments to middlewares, and
- enabling AVS to freeze operators for the purpose of slashing (the corresponding unfreeze actions are determined by the veto committee).

We are currently in the process of implementing the API for payment flow from AVSs to operators in EigenLayer. Details of this API will be added to this document in the near future.
We are currently in the process of implementing the API for rewards flow from AVSs to operators in EigenLayer. Details of this API will be added to this document in the near future.

The following figure summarizes scope of this document:
![Doc Outline](../images/middleware_outline_doc.png)
Expand Down
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
68 changes: 33 additions & 35 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/Ownabl
import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {IPaymentCoordinator} from
"eigenlayer-contracts/src/contracts/interfaces/IPaymentCoordinator.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";

import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
Expand All @@ -31,24 +30,25 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt
_;
}

modifier onlyPaymentInitiator() {
/// @notice only rewardsInitiator can call createAVSRewardsSubmission
modifier onlyRewardsInitiator() {
require(
msg.sender == paymentInitiator,
"ServiceManagerBase.onlyPaymentInitiator: caller is not the payment initiator"
msg.sender == rewardsInitiator,
"ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator"
);
_;
}

/// @notice Sets the (immutable) `_registryCoordinator` address
constructor(
IAVSDirectory __avsDirectory,
IPaymentCoordinator ___paymentCoordinator,
IRewardsCoordinator __rewardsCoordinator,
IRegistryCoordinator __registryCoordinator,
IStakeRegistry __stakeRegistry
)
ServiceManagerBaseStorage(
__avsDirectory,
___paymentCoordinator,
__rewardsCoordinator,
__registryCoordinator,
__stakeRegistry
)
Expand All @@ -58,10 +58,10 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt

function __ServiceManagerBase_init(
address initialOwner,
address _paymentInitiator
address _rewardsInitiator
) internal virtual onlyInitializing {
_transferOwnership(initialOwner);
_setPaymentInitiator(_paymentInitiator);
_setRewardsInitiator(_rewardsInitiator);
}

/**
Expand All @@ -74,35 +74,33 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt
}

/**
* @notice Creates a new range payment on behalf of an AVS, to be split amongst the
* set of stakers delegated to operators who are registered to the `avs`.
* Note that the owner calling this function must have approved the tokens to be transferred to the ServiceManager
* and of course has the required balances.
* @param rangePayments The range payments being created
* @dev Expected to be called by the ServiceManager of the AVS on behalf of which the payment is being made
* @dev The duration of the `rangePayment` cannot exceed `paymentCoordinator.MAX_PAYMENT_DURATION()`
* @dev The tokens are sent to the `PaymentCoordinator` contract
* @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract, to be split amongst the
* set of stakers delegated to operators who are registered to this `avs`
* @param rewardsSubmissions The 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 Strategies must be in ascending order of addresses to check for duplicates
* @dev This function will revert if the `rangePayment` is malformed,
* @dev This function will revert if the `rewardsSubmission` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
*/
function payForRange(IPaymentCoordinator.RangePayment[] calldata rangePayments)
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions)
public
virtual
onlyPaymentInitiator
onlyRewardsInitiator
{
for (uint256 i = 0; i < rangePayments.length; ++i) {
// transfer token to ServiceManager and approve PaymentCoordinator to transfer again
// in payForRange() call
rangePayments[i].token.transferFrom(msg.sender, address(this), rangePayments[i].amount);
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 =
rangePayments[i].token.allowance(address(this), address(_paymentCoordinator));
rangePayments[i].token.approve(
address(_paymentCoordinator), rangePayments[i].amount + allowance
rewardsSubmissions[i].token.allowance(address(this), address(_rewardsCoordinator));
rewardsSubmissions[i].token.approve(
address(_rewardsCoordinator), rewardsSubmissions[i].amount + allowance
);
}

_paymentCoordinator.payForRange(rangePayments);
_rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions);
}

/**
Expand All @@ -126,17 +124,17 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt
}

/**
* @notice Sets the payment initiator address
* @param newPaymentInitiator The new payment initiator address
* @notice Sets the rewards initiator address
* @param newRewardsInitiator The new rewards initiator address
* @dev only callable by the owner
*/
function setPaymentInitiator(address newPaymentInitiator) external onlyOwner {
_setPaymentInitiator(newPaymentInitiator);
function setRewardsInitiator(address newRewardsInitiator) external onlyOwner {
_setRewardsInitiator(newRewardsInitiator);
}

function _setPaymentInitiator(address newPaymentInitiator) internal {
emit PaymentInitiatorUpdated(paymentInitiator, newPaymentInitiator);
paymentInitiator = newPaymentInitiator;
function _setRewardsInitiator(address newRewardsInitiator) internal {
emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator);
rewardsInitiator = newRewardsInitiator;
}

/**
Expand Down
15 changes: 7 additions & 8 deletions src/ServiceManagerBaseStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";

import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {IPaymentCoordinator} from
"eigenlayer-contracts/src/contracts/interfaces/IPaymentCoordinator.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";

/**
* @title Storage variables for the `ServiceManagerBase` contract.
Expand All @@ -21,7 +20,7 @@ abstract contract ServiceManagerBaseStorage is IServiceManager {
*
*/
IAVSDirectory internal immutable _avsDirectory;
IPaymentCoordinator internal immutable _paymentCoordinator;
IRewardsCoordinator internal immutable _rewardsCoordinator;
IRegistryCoordinator internal immutable _registryCoordinator;
IStakeRegistry internal immutable _stakeRegistry;

Expand All @@ -31,18 +30,18 @@ abstract contract ServiceManagerBaseStorage is IServiceManager {
*
*/

/// @notice The address of the entity that can initiate payments
address public paymentInitiator;
/// @notice The address of the entity that can initiate rewards
address public rewardsInitiator;

/// @notice Sets the (immutable) `_avsDirectory`, `_paymentCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses
/// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses
constructor(
IAVSDirectory __avsDirectory,
IPaymentCoordinator __paymentCoordinator,
IRewardsCoordinator __rewardsCoordinator,
IRegistryCoordinator __registryCoordinator,
IStakeRegistry __stakeRegistry
) {
_avsDirectory = __avsDirectory;
_paymentCoordinator = __paymentCoordinator;
_rewardsCoordinator = __rewardsCoordinator;
_registryCoordinator = __registryCoordinator;
_stakeRegistry = __stakeRegistry;
}
Expand Down
23 changes: 10 additions & 13 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import {IPaymentCoordinator} from
"eigenlayer-contracts/src/contracts/interfaces/IPaymentCoordinator.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IServiceManagerUI} from "./IServiceManagerUI.sol";

/**
Expand All @@ -11,20 +10,18 @@ import {IServiceManagerUI} from "./IServiceManagerUI.sol";
*/
interface IServiceManager is IServiceManagerUI {
/**
* @notice Creates a new range payment on behalf of an AVS, to be split amongst the
* set of stakers delegated to operators who are registered to the `avs`.
* Note that the owner calling this function must have approved the tokens to be transferred to the ServiceManager
* and of course has the required balances.
* @param rangePayments The range payments being created
* @dev Expected to be called by the ServiceManager of the AVS on behalf of which the payment is being made
* @dev The duration of the `rangePayment` cannot exceed `paymentCoordinator.MAX_PAYMENT_DURATION()`
* @dev The tokens are sent to the `PaymentCoordinator` contract
* @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract, to be split amongst the
* set of stakers delegated to operators who are registered to this `avs`
* @param rewardsSubmissions The 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 Strategies must be in ascending order of addresses to check for duplicates
* @dev This function will revert if the `rangePayment` is malformed,
* @dev This function will revert if the `rewardsSubmission` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
*/
function payForRange(IPaymentCoordinator.RangePayment[] calldata rangePayments) external;
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external;

// EVENTS
event PaymentInitiatorUpdated(address prevPaymentInitiator, address newPaymentInitiator);
event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator);
}
42 changes: 21 additions & 21 deletions src/unaudited/ECDSAServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {IServiceManagerUI} from "../interfaces/IServiceManagerUI.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IStakeRegistry} from "../interfaces/IStakeRegistry.sol";
import {IPaymentCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IPaymentCoordinator.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {Quorum} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol";
import {ECDSAStakeRegistry} from "../unaudited/ECDSAStakeRegistry.sol";

Expand All @@ -25,8 +25,8 @@ abstract contract ECDSAServiceManagerBase is
/// @notice Address of the AVS directory contract, which manages AVS-related data for registered operators.
address public immutable avsDirectory;

/// @notice Address of the payment coordinator contract, which handles payment distributions.
address internal immutable paymentCoordinator;
/// @notice Address of the rewards coordinator contract, which handles rewards distributions.
address internal immutable rewardsCoordinator;

/// @notice Address of the delegation manager contract, which manages staker delegations to operators.
address internal immutable delegationManager;
Expand All @@ -46,18 +46,18 @@ abstract contract ECDSAServiceManagerBase is
* @dev Constructor for ECDSAServiceManagerBase, initializing immutable contract addresses and disabling initializers.
* @param _avsDirectory The address of the AVS directory contract, managing AVS-related data for registered operators.
* @param _stakeRegistry The address of the stake registry contract, managing registration and stake recording.
* @param _paymentCoordinator The address of the payment coordinator contract, handling payment distributions.
* @param _rewardsCoordinator The address of the rewards coordinator contract, handling rewards distributions.
* @param _delegationManager The address of the delegation manager contract, managing staker delegations to operators.
*/
constructor(
address _avsDirectory,
address _stakeRegistry,
address _paymentCoordinator,
address _rewardsCoordinator,
address _delegationManager
) {
avsDirectory = _avsDirectory;
stakeRegistry = _stakeRegistry;
paymentCoordinator = _paymentCoordinator;
rewardsCoordinator = _rewardsCoordinator;
delegationManager = _delegationManager;
_disableInitializers();
}
Expand All @@ -80,10 +80,10 @@ abstract contract ECDSAServiceManagerBase is
}

/// @inheritdoc IServiceManager
function payForRange(
IPaymentCoordinator.RangePayment[] calldata rangePayments
function createAVSRewardsSubmission(
IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions
) external virtual onlyOwner {
_payForRange(rangePayments);
_createAVSRewardsSubmission(rewardsSubmissions);
}

/// @inheritdoc IServiceManagerUI
Expand Down Expand Up @@ -155,26 +155,26 @@ abstract contract ECDSAServiceManagerBase is
}

/**
* @notice Processes a batch of range payments by transferring the specified amounts from the sender to this contract and then approving the PaymentCoordinator to use these amounts.
* @dev This function handles the transfer and approval of tokens necessary for range payments. It then delegates the actual payment logic to the PaymentCoordinator contract.
* @param rangePayments An array of `RangePayment` structs, each representing a payment for a specific range.
* @notice Processes a batch of rewards submissions by transferring the specified amounts from the sender to this contract and then approving the RewardsCoordinator to use these amounts.
* @dev This function handles the transfer and approval of tokens necessary for rewards submissions. It then delegates the actual rewards logic to the RewardsCoordinator contract.
* @param rewardsSubmissions An array of `RewardsSubmission` structs, each representing rewards for a specific range.
*/
function _payForRange(
IPaymentCoordinator.RangePayment[] calldata rangePayments
function _createAVSRewardsSubmission(
IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions
) internal virtual {
for (uint256 i = 0; i < rangePayments.length; ++i) {
rangePayments[i].token.transferFrom(
for (uint256 i = 0; i < rewardsSubmissions.length; ++i) {
rewardsSubmissions[i].token.transferFrom(
msg.sender,
address(this),
rangePayments[i].amount
rewardsSubmissions[i].amount
);
rangePayments[i].token.approve(
paymentCoordinator,
rangePayments[i].amount
rewardsSubmissions[i].token.approve(
rewardsCoordinator,
rewardsSubmissions[i].amount
);
}

IPaymentCoordinator(paymentCoordinator).payForRange(rangePayments);
IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(rewardsSubmissions);
}

/**
Expand Down
Loading

0 comments on commit 51e36f1

Please sign in to comment.