Skip to content

Commit

Permalink
Merge pull request #7 from Layr-Labs/dev
Browse files Browse the repository at this point in the history
latest upate
  • Loading branch information
Dododododoit authored Jun 11, 2024
2 parents c5b419a + b45dced commit 88c320f
Show file tree
Hide file tree
Showing 28 changed files with 1,516 additions and 959 deletions.
32 changes: 29 additions & 3 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,35 @@ Licensor: Layr Labs, Inc.
Licensed Work: EigenLayer Middleware Contracts
The Licensed Work is (c) 2023 Layr Labs, Inc.

Additional Use Grant: None.

Change Date: 2025-10-16 (October 16th, 2025)
Additional Use Grant:

You may additionally use any of the software included in the following repositories
[here](https://docs.google.com/spreadsheets/d/1PlJRow5C0GMqXZlIxRm5CEnkhH-gMV1wIdq1pCfbZco/edit?usp=sharing)
(“Additional Use Grant Software”) for production commercial uses, but only if such
uses are (i) built on or using the EigenLayer Protocol or EigenDA, and (ii) not
Competing Uses.

“Competing Use” means any use of the Additional Use Grant Software in any product,
protocol, application or service that is made available to third parties and that
(i) substitutes for use of EigenLayer Protocol or EigenDA, (ii) offers the same or
substantially similar functionality as the EigenLayer Protocol or EigenDA or
(iii) is built on or using a protocol with substantially similar functionality as
the EigenLayer Protocol.

EigenLayer Protocol means the restaking protocol as further described in the
documentation [here](https://docs.eigenlayer.xyz/), as updated from time to time.

EigenDA means the data availability protocol built on top of the EigenLayer
Protocol as further described in the documentation
[here](https://docs.eigenlayer.xyz/eigenda/overview), as updated from time to time.

Change Dates:

- All commits at or prior to commit a23de118e7d16081d350c7f83c24261d1421b0ba
(i.e. committed to this repository on or before May 19, 2024, the date of) have a
change date of 2025-10-16 (October 16th, 2025)
- All commits after a23de118e7d16081d350c7f83c24261d1421b0ba (i.e. committed to this
repository after May 19, 2024) have a change date of 2027-05-01 (May 1st, 2027)

Change License: MIT

Expand Down
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
130 changes: 81 additions & 49 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@
pragma solidity ^0.8.12;

import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
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";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";

/**
* @title Minimal implementation of a ServiceManager-type contract.
* This contract can be inherited from or simply used as a point-of-reference.
* @author Layr Labs, Inc.
*/
abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseStorage {
using BitmapUtils for *;

IAVSDirectory internal immutable _avsDirectory;
IPaymentCoordinator internal immutable _paymentCoordinator;
IRegistryCoordinator internal immutable _registryCoordinator;
IStakeRegistry internal immutable _stakeRegistry;

/// @notice when applied to a function, only allows the RegistryCoordinator to call it
modifier onlyRegistryCoordinator() {
require(
Expand All @@ -33,22 +30,38 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
_;
}

/// @notice only rewardsInitiator can call createAVSRewardsSubmission
modifier onlyRewardsInitiator() {
require(
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
) {
_avsDirectory = __avsDirectory;
_paymentCoordinator = ___paymentCoordinator;
_registryCoordinator = __registryCoordinator;
_stakeRegistry = __stakeRegistry;
)
ServiceManagerBaseStorage(
__avsDirectory,
__rewardsCoordinator,
__registryCoordinator,
__stakeRegistry
)
{
_disableInitializers();
}

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

/**
Expand All @@ -61,30 +74,33 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
}

/**
* @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
) public virtual onlyOwner {
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);
uint256 allowance = rangePayments[i].token.allowance(address(this), address(_paymentCoordinator));
rangePayments[i].token.approve(address(_paymentCoordinator), rangePayments[i].amount + allowance);
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.approve(
address(_rewardsCoordinator), rewardsSubmissions[i].amount + allowance
);
}

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

/**
Expand All @@ -107,10 +123,24 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
_avsDirectory.deregisterOperatorFromAVS(operator);
}

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

function _setRewardsInitiator(address newRewardsInitiator) internal {
emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator);
rewardsInitiator = newRewardsInitiator;
}

/**
* @notice Returns the list of strategies that the AVS supports for restaking
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on uniqueness of each element in the returned array.
* @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 view returns (address[] memory) {
Expand All @@ -119,18 +149,19 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
if (quorumCount == 0) {
return new address[](0);
}

uint256 strategyCount;
for(uint256 i = 0; i < quorumCount; i++) {
for (uint256 i = 0; i < quorumCount; i++) {
strategyCount += _stakeRegistry.strategyParamsLength(uint8(i));
}

address[] memory restakedStrategies = new address[](strategyCount);
uint256 index = 0;
for(uint256 i = 0; i < _registryCoordinator.quorumCount(); i++) {
for (uint256 i = 0; i < _registryCoordinator.quorumCount(); 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 @@ -141,10 +172,14 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
* @notice Returns the list of strategies that the operator has potentially restaked on the AVS
* @param operator The address of the operator to get restaked strategies for
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness
* @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 view returns (address[] memory) {
function getOperatorRestakedStrategies(address operator)
external
view
returns (address[] memory)
{
bytes32 operatorId = _registryCoordinator.getOperatorId(operator);
uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap(operatorId);

Expand All @@ -155,30 +190,27 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
// Get number of strategies for each quorum in operator bitmap
bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray(operatorBitmap);
uint256 strategyCount;
for(uint256 i = 0; i < operatorRestakedQuorums.length; i++) {
for (uint256 i = 0; i < operatorRestakedQuorums.length; 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++) {
for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) {
uint8 quorum = uint8(operatorRestakedQuorums[i]);
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++;
}
}
return restakedStrategies;
return restakedStrategies;
}

/// @notice Returns the EigenLayer AVSDirectory contract.
function avsDirectory() external view override returns (address) {
return address(_avsDirectory);
}

// storage gap for upgradeability
// slither-disable-next-line shadowing-state
uint256[50] private __GAP;
}
51 changes: 51 additions & 0 deletions src/ServiceManagerBaseStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";

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

/**
* @title Storage variables for the `ServiceManagerBase` contract.
* @author Layr Labs, Inc.
* @notice This storage contract is separate from the logic to simplify the upgrade process.
*/
abstract contract ServiceManagerBaseStorage is IServiceManager {
/**
*
* CONSTANTS AND IMMUTABLES
*
*/
IAVSDirectory internal immutable _avsDirectory;
IRewardsCoordinator internal immutable _rewardsCoordinator;
IRegistryCoordinator internal immutable _registryCoordinator;
IStakeRegistry internal immutable _stakeRegistry;

/**
*
* STATE VARIABLES
*
*/

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

/// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses
constructor(
IAVSDirectory __avsDirectory,
IRewardsCoordinator __rewardsCoordinator,
IRegistryCoordinator __registryCoordinator,
IStakeRegistry __stakeRegistry
) {
_avsDirectory = __avsDirectory;
_rewardsCoordinator = __rewardsCoordinator;
_registryCoordinator = __registryCoordinator;
_stakeRegistry = __stakeRegistry;
}

// storage gap for upgradeability
uint256[49] private __GAP;
}
Loading

0 comments on commit 88c320f

Please sign in to comment.