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: service manager payments #242

Merged
merged 4 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
Submodule eigenlayer-contracts updated 29 files
+38 −0 .github/workflows/run-deploy-scripts.yml
+92 −45 README.md
+0 −2 docs/README.md
+2 −2 docs/core/DelegationManager.md
+2 −2 docs/core/StrategyManager.md
+71 −0 script/admin/mainnet/Mainnet_Unpause_Deposits.s.sol
+0 −1 script/configs/devnet/M2_deploy_from_scratch.anvil.config.json
+57 −0 script/configs/holesky/Deploy_PaymentCoordinator.holesky.config.json
+17 −20 script/deploy/devnet/M2_Deploy_From_Scratch.s.sol
+44 −0 script/deploy/holesky/Deploy_Preprod_PaymentCoordinator.s.sol
+74 −0 script/deploy/holesky/Deploy_Test_PaymentCoordinator.s.sol
+37 −0 script/output/holesky/Deploy_PaymentCoordinator.holesky.config.json
+37 −0 script/output/holesky/Deploy_PaymentCoordinator_Preprod.holesky.config.json
+61 −0 script/output/holesky/M2_deploy_from_scratch.output.json
+39 −0 script/output/holesky/M2_deploy_preprod.output.json
+2 −1 script/output/mainnet/M1_deployment_mainnet_2023_6_9.json
+156 −29 script/utils/ExistingDeploymentParser.sol
+474 −0 src/contracts/core/PaymentCoordinator.sol
+108 −0 src/contracts/core/PaymentCoordinatorStorage.sol
+3 −0 src/contracts/interfaces/IDelegationManager.sol
+288 −0 src/contracts/interfaces/IPaymentCoordinator.sol
+3 −0 src/contracts/interfaces/IStrategyManager.sol
+66 −0 src/test/events/IPaymentCoordinatorEvents.sol
+3 −0 src/test/mocks/DelegationManagerMock.sol
+7 −0 src/test/mocks/StrategyManagerMock.sol
+54 −0 src/test/test-data/paymentCoordinator/processClaimProofs_Root1.json
+54 −0 src/test/test-data/paymentCoordinator/processClaimProofs_Root2.json
+54 −0 src/test/test-data/paymentCoordinator/processClaimProofs_Root3.json
+1,637 −0 src/test/unit/PaymentCoordinatorUnit.t.sol
33 changes: 32 additions & 1 deletion src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/Ownabl
import {BitmapUtils} from "./libraries/BitmapUtils.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 {IServiceManager} from "./interfaces/IServiceManager.sol";
// import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol";
8sunyuan marked this conversation as resolved.
Show resolved Hide resolved
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";

Expand All @@ -22,6 +24,7 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
IRegistryCoordinator internal immutable _registryCoordinator;
IStakeRegistry internal immutable _stakeRegistry;
IAVSDirectory internal immutable _avsDirectory;
IPaymentCoordinator internal immutable _paymentCoordinator;

/// @notice when applied to a function, only allows the RegistryCoordinator to call it
modifier onlyRegistryCoordinator() {
Expand All @@ -36,11 +39,13 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
constructor(
IAVSDirectory __avsDirectory,
IRegistryCoordinator __registryCoordinator,
IStakeRegistry __stakeRegistry
IStakeRegistry __stakeRegistry,
IPaymentCoordinator ___paymentCoordinator
samlaf marked this conversation as resolved.
Show resolved Hide resolved
) {
_avsDirectory = __avsDirectory;
_registryCoordinator = __registryCoordinator;
_stakeRegistry = __stakeRegistry;
_paymentCoordinator = ___paymentCoordinator;
_disableInitializers();
}

Expand All @@ -57,6 +62,32 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
_avsDirectory.updateAVSMetadataURI(_metadataURI);
}

/**
* @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
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev This function will revert if the `rangePayment` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
*/
function submitRangePayments(
samlaf marked this conversation as resolved.
Show resolved Hide resolved
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);
rangePayments[i].token.approve(address(_paymentCoordinator), rangePayments[i].amount);
}

_paymentCoordinator.payForRange(rangePayments);
}

/**
* @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 Down
6 changes: 3 additions & 3 deletions src/ServiceManagerRouter.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol";

/**
* @title Contract that proxies calls to a ServiceManager contract.
Expand All @@ -20,7 +20,7 @@ contract ServiceManagerRouter {
*/
function getRestakeableStrategies(address serviceManager) external view returns (address[] memory) {
bytes memory data = abi.encodeWithSelector(
IServiceManager.getRestakeableStrategies.selector
IServiceManagerUI.getRestakeableStrategies.selector
);
return _makeCall(serviceManager, data);
}
Expand All @@ -32,7 +32,7 @@ contract ServiceManagerRouter {
*/
function getOperatorRestakedStrategies(address serviceManager, address operator) external view returns (address[] memory) {
bytes memory data = abi.encodeWithSelector(
IServiceManager.getOperatorRestakedStrategies.selector,
IServiceManagerUI.getOperatorRestakedStrategies.selector,
operator
);
return _makeCall(serviceManager, data);
Expand Down
57 changes: 15 additions & 42 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
@@ -1,53 +1,26 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

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

/**
* @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer
* @author Layr Labs, Inc.
*/
interface IServiceManager {
interface IServiceManager is IServiceManagerUI {
/**
* @notice Updates the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
* @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
* @dev Strategies must be in ascending order of addresses to check for duplicates
* @dev This function will revert if the `rangePayment` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
*/
function updateAVSMetadataURI(string memory _metadataURI) external;

/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS
* @param operator The address of the operator to register.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operator,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external;

/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(address operator) external;

/**
* @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
* 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);

/**
* @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.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies() external view returns (address[] memory);

/// @notice Returns the EigenLayer AVSDirectory contract.
function avsDirectory() external view returns (address);
function submitRangePayments(IPaymentCoordinator.RangePayment[] calldata rangePayments) external;
}
62 changes: 62 additions & 0 deletions src/interfaces/IServiceManagerUI.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";

/**
* @title Minimal interface for a ServiceManager-type contract that AVS ServiceManager contracts must implement
* to index their data for the V1 AVS Marketplace
samlaf marked this conversation as resolved.
Show resolved Hide resolved
* @author Layr Labs, Inc.
*/
interface IServiceManagerUI {
/**
* Metadata should follow the format outlined by this example.
{
"name": "EigenLabs AVS 1",
"website": "https://www.eigenlayer.xyz/",
"description": "This is my 1st AVS",
"logo": "https://holesky-operator-metadata.s3.amazonaws.com/eigenlayer.png",
"twitter": "https://twitter.com/eigenlayer"
}
* @notice Updates the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
*/
function updateAVSMetadataURI(string memory _metadataURI) external;

/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS
* @param operator The address of the operator to register.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operator,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external;

/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(address operator) external;

/**
* @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
* 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);

/**
* @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.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies() external view returns (address[] memory);

/// @notice Returns the EigenLayer AVSDirectory contract.
function avsDirectory() external view returns (address);
}
66 changes: 66 additions & 0 deletions test/events/IServiceManagerBaseEvents.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "eigenlayer-contracts/src/contracts/interfaces/IPaymentCoordinator.sol";

interface IServiceManagerBaseEvents {
/// PaymentCoordinator EVENTS ///

/// @notice emitted when an AVS creates a valid RangePayment
event RangePaymentCreated(
address indexed avs,
uint256 indexed paymentNonce,
bytes32 indexed rangePaymentHash,
IPaymentCoordinator.RangePayment rangePayment
);
/// @notice emitted when a valid RangePayment is created for all stakers by a valid submitter
event RangePaymentForAllCreated(
address indexed submitter,
uint256 indexed paymentNonce,
bytes32 indexed rangePaymentHash,
IPaymentCoordinator.RangePayment rangePayment
);
/// @notice paymentUpdater is responsible for submiting DistributionRoots, only owner can set paymentUpdater
event PaymentUpdaterSet(address indexed oldPaymentUpdater, address indexed newPaymentUpdater);
event PayAllForRangeSubmitterSet(
address indexed payAllForRangeSubmitter,
bool indexed oldValue,
bool indexed newValue
);
event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay);
event CalculationIntervalSecondsSet(uint32 oldCalculationIntervalSeconds, uint32 newCalculationIntervalSeconds);
event GlobalCommissionBipsSet(uint16 oldGlobalCommissionBips, uint16 newGlobalCommissionBips);
event ClaimerForSet(address indexed earner, address indexed oldClaimer, address indexed claimer);
/// @notice rootIndex is the specific array index of the newly created root in the storage array
event DistributionRootSubmitted(
uint32 indexed rootIndex,
bytes32 indexed root,
uint32 paymentCalculationEndTimestamp,
uint32 activatedAt
);
/// @notice root is one of the submitted distribution roots that was claimed against
event PaymentClaimed(
bytes32 root,
address indexed earner,
address indexed claimer,
IERC20 indexed token,
uint256 claimedAmount
);



/// TOKEN EVENTS FOR TESTING ///
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);

/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
7 changes: 6 additions & 1 deletion test/integration/CoreRegistration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirecto
import { IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol";
import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import { PaymentCoordinator } from "eigenlayer-contracts/src/contracts/core/PaymentCoordinator.sol";
import { IPaymentCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IPaymentCoordinator.sol";

contract Test_CoreRegistration is MockAVSDeployer {
// Contracts
Expand Down Expand Up @@ -62,12 +64,15 @@ contract Test_CoreRegistration is MockAVSDeployer {
)
);

// Deploy Mock PaymentCoordinator
paymentCoordinatorMock = new PaymentCoordinatorMock();

// Deploy New ServiceManager & RegistryCoordinator implementations
serviceManagerImplementation = new ServiceManagerMock(
avsDirectory,
registryCoordinator,
stakeRegistry
stakeRegistry,
paymentCoordinatorMock
);

registryCoordinatorImplementation = new RegistryCoordinatorHarness(
Expand Down
Loading
Loading