Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ These histories are used by offchain code to query state at particular blocks, a
##### Hooking Into EigenLayer Core

The main thing that links an AVS to the EigenLayer core contracts is that when EigenLayer Operators register/deregister with an AVS, the AVS calls these functions in EigenLayer core:
* [`DelegationManager.registerOperatorToAVS`][core-registerToAVS]
* [`DelegationManager.deregisterOperatorFromAVS`][core-deregisterFromAVS]
* [`AVSRegistry.registerOperatorToAVS`][core-registerToAVS]
* [`AVSRegistry.deregisterOperatorFromAVS`][core-deregisterFromAVS]
Comment on lines +100 to +101
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AVSDirectory

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also need to update the link at the top of the file -- but currently we don't have docs for this contract/those methods. Lemme work on this today so I can get you a link...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


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`.

Expand Down
23 changes: 14 additions & 9 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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 {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";

import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
Expand All @@ -20,8 +20,8 @@ contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
using BitmapUtils for *;

IRegistryCoordinator internal immutable _registryCoordinator;
IDelegationManager internal immutable _delegationManager;
IStakeRegistry internal immutable _stakeRegistry;
IAVSDirectory internal immutable _avsDirectory;

/// @notice when applied to a function, only allows the RegistryCoordinator to call it
modifier onlyRegistryCoordinator() {
Expand All @@ -34,11 +34,11 @@ contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {

/// @notice Sets the (immutable) `_registryCoordinator` address
constructor(
IDelegationManager __delegationManager,
IAVSDirectory __avsDirectory,
IRegistryCoordinator __registryCoordinator,
IStakeRegistry __stakeRegistry
) {
_delegationManager = __delegationManager;
_avsDirectory = __avsDirectory;
_registryCoordinator = __registryCoordinator;
_stakeRegistry = __stakeRegistry;
_disableInitializers();
Expand All @@ -54,27 +54,27 @@ contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
* @dev only callable by the owner
*/
function setMetadataURI(string memory _metadataURI) public virtual onlyOwner {
_delegationManager.updateAVSMetadataURI(_metadataURI);
_avsDirectory.updateAVSMetadataURI(_metadataURI);
}

/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS
* @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.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operator,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) public virtual onlyRegistryCoordinator {
_delegationManager.registerOperatorToAVS(operator, operatorSignature);
_avsDirectory.registerOperatorToAVS(operator, operatorSignature);
}

/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS
* @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 {
_delegationManager.deregisterOperatorFromAVS(operator);
_avsDirectory.deregisterOperatorFromAVS(operator);
}

/**
Expand Down Expand Up @@ -142,4 +142,9 @@ contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
}
return restakedStrategies;
}

/// @notice Returns the EigenLayer AVSDirectory contract.
function avsDirectory() external view override returns (address) {
return address(_avsDirectory);
}
}
3 changes: 3 additions & 0 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,7 @@ interface IServiceManager {
* 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);
}
50 changes: 40 additions & 10 deletions test/integration/CoreRegistration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
pragma solidity =0.8.12;

import "../utils/MockAVSDeployer.sol";
import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
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 { IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";

contract Test_CoreRegistration is MockAVSDeployer {
// Contracts
Expand Down Expand Up @@ -43,9 +46,27 @@ contract Test_CoreRegistration is MockAVSDeployer {
)
);

// Deploy New AVS Directory
AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager);
avsDirectory = AVSDirectory(
address(
new TransparentUpgradeableProxy(
address(avsDirectoryImplementation),
address(proxyAdmin),
abi.encodeWithSelector(
AVSDirectory.initialize.selector,
address(this), // owner
pauserRegistry,
0 // 0 is initialPausedStatus
)
)
)
);


// Deploy New ServiceManager & RegistryCoordinator implementations
serviceManagerImplementation = new ServiceManagerBase(
delegationManager,
avsDirectory,
registryCoordinator,
stakeRegistry
);
Expand Down Expand Up @@ -88,7 +109,7 @@ contract Test_CoreRegistration is MockAVSDeployer {
bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP);
for (uint i = 0; i < quorumNumbers.length; i++) {
_setOperatorWeight(operator, uint8(quorumNumbers[i]), defaultStake);
}
}
}

function test_registerOperator_coreStateChanges() public {
Expand All @@ -103,13 +124,16 @@ contract Test_CoreRegistration is MockAVSDeployer {
maxExpiry
);

// set operator as registered in Eigenlayer
delegationMock.setIsOperator(operator, true);

// Register operator
cheats.prank(operator);
registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature);

// Check operator is registered
IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(serviceManager), operator);
assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED));
IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator);
assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED));
}

function test_deregisterOperator_coreStateChanges() public {
Expand All @@ -122,8 +146,8 @@ contract Test_CoreRegistration is MockAVSDeployer {
registryCoordinator.deregisterOperator(quorumNumbers);

// Check operator is deregistered
IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(serviceManager), operator);
assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.UNREGISTERED));
IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator);
assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED));
}

function test_deregisterOperator_notGloballyDeregistered() public {
Expand All @@ -138,8 +162,8 @@ contract Test_CoreRegistration is MockAVSDeployer {
registryCoordinator.deregisterOperator(quorumNumbers);

// Check operator is still registered
IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(serviceManager), operator);
assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED));
IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator);
assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED));
}

function test_setMetadataURI_fail_notServiceManagerOwner() public {
Expand All @@ -149,11 +173,14 @@ contract Test_CoreRegistration is MockAVSDeployer {
serviceManager.setMetadataURI("Test MetadataURI");
}

event AVSMetadataURIUpdated(address indexed avs, string metadataURI);

function test_setMetadataURI() public {
address toPrankFrom = serviceManager.owner();
cheats.prank(toPrankFrom);
cheats.expectEmit(true, true, true, true);
emit AVSMetadataURIUpdated(address(serviceManager), "Test MetadataURI");
serviceManager.setMetadataURI("Test MetadataURI");
// TODO: check effects here
}

// Utils
Expand All @@ -167,6 +194,9 @@ contract Test_CoreRegistration is MockAVSDeployer {
maxExpiry
);

// set operator as registered in Eigenlayer
delegationMock.setIsOperator(operator, true);

// Register operator
cheats.prank(operator);
registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature);
Expand All @@ -182,7 +212,7 @@ contract Test_CoreRegistration is MockAVSDeployer {
operatorSignature.salt = salt;
operatorSignature.expiry = expiry;
{
bytes32 digestHash = delegationManager.calculateOperatorAVSRegistrationDigestHash(operatorToSign, avs, salt, expiry);
bytes32 digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash(operatorToSign, avs, salt, expiry);
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(_operatorPrivateKey, digestHash);
operatorSignature.signature = abi.encodePacked(r, s, v);
}
Expand Down
10 changes: 5 additions & 5 deletions test/integration/IntegrationBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,18 @@ abstract contract IntegrationBase is IntegrationConfig {
}
}

/// DelegationManager:
/// AVSDirectory:

function assert_NotRegisteredToAVS(User operator, string memory err) internal {
IDelegationManager.OperatorAVSRegistrationStatus status = delegationManager.avsOperatorStatus(address(serviceManager), address(operator));
IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator));

assertTrue(status == IDelegationManager.OperatorAVSRegistrationStatus.UNREGISTERED, err);
assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED, err);
}

function assert_IsRegisteredToAVS(User operator, string memory err) internal {
IDelegationManager.OperatorAVSRegistrationStatus status = delegationManager.avsOperatorStatus(address(serviceManager), address(operator));
IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator));

assertTrue(status == IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED, err);
assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED, err);
}

/*******************************************************************************
Expand Down
10 changes: 5 additions & 5 deletions test/integration/IntegrationChecks.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ contract IntegrationChecks is IntegrationBase {
assert_Snap_Added_OperatorListEntry(operator, quorums,
"operator list should have one more entry");

// DelegationManager
// AVSDirectory
assert_IsRegisteredToAVS(operator,
"operator should be registered to AVS");
}
Expand Down Expand Up @@ -128,7 +128,7 @@ contract IntegrationChecks is IntegrationBase {
assert_Snap_Replaced_OperatorListEntries(incomingOperator, churnedOperators, churnedQuorums,
"operator list should contain incoming operator and should not contain churned operators");

// DelegationManager
// AVSDirectory
assert_IsRegisteredToAVS(incomingOperator,
"operator should be registered to AVS");

Expand Down Expand Up @@ -235,7 +235,7 @@ contract IntegrationChecks is IntegrationBase {
assert_Snap_Unchanged_OperatorListEntry(quorums,
"operator list should be unchanged for each quorum");

// DelegationManager
// AVSDirectory
assert_IsRegisteredToAVS(operator,
"operator should be registered to AVS");
}
Expand Down Expand Up @@ -315,7 +315,7 @@ contract IntegrationChecks is IntegrationBase {
assert_Snap_Removed_OperatorListEntry(operator, quorums,
"operator list should have one fewer entry");

// DelegationManager
// AVSDirectory
assert_NotRegisteredToAVS(operator,
"operator should not be registered to the AVS");
}
Expand Down Expand Up @@ -404,7 +404,7 @@ contract IntegrationChecks is IntegrationBase {
assert_HasDeregisteredStatus(operator,
"operatorInfo status should be DEREGISTERED");

// DelegationManager
// AVSDirectory
assert_NotRegisteredToAVS(operator,
"operator should not be registered to the AVS");
}
Expand Down
19 changes: 18 additions & 1 deletion test/integration/IntegrationDeployer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import "@openzeppelin/contracts/utils/Strings.sol";
import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol";
import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol";
import "eigenlayer-contracts/src/contracts/core/Slasher.sol";
import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol";
import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol";
import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol";
Expand Down Expand Up @@ -48,6 +49,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {

// Core contracts to deploy
DelegationManager delegationManager;
AVSDirectory public avsDirectory;
StrategyManager strategyManager;
EigenPodManager eigenPodManager;
PauserRegistry pauserRegistry;
Expand Down Expand Up @@ -126,6 +128,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
delayedWithdrawalRouter = DelayedWithdrawalRouter(
address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), ""))
);
avsDirectory = AVSDirectory(
address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), ""))
);

// Deploy EigenPod Contracts
pod = new EigenPod(
Expand All @@ -150,6 +155,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
delegationManager
);
DelayedWithdrawalRouter delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager);
AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager);

// Third, upgrade the proxy contracts to point to the implementations
uint256 minWithdrawalDelayBlocks = 7 days / 12 seconds;
Expand Down Expand Up @@ -217,6 +223,17 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
minWithdrawalDelayBlocks
)
);
// AVSDirectory
proxyAdmin.upgradeAndCall(
TransparentUpgradeableProxy(payable(address(avsDirectory))),
address(avsDirectoryImplemntation),
abi.encodeWithSelector(
AVSDirectory.initialize.selector,
eigenLayerReputedMultisig, // initialOwner
pauserRegistry,
0 // initialPausedStatus
)
);

// Deploy and whitelist strategies
baseStrategyImplementation = new StrategyBase(strategyManager);
Expand Down Expand Up @@ -283,7 +300,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
StakeRegistry stakeRegistryImplementation = new StakeRegistry(IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager));
BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(IRegistryCoordinator(registryCoordinator));
IndexRegistry indexRegistryImplementation = new IndexRegistry(IRegistryCoordinator(registryCoordinator));
ServiceManagerBase serviceManagerImplementation = new ServiceManagerBase(IDelegationManager(delegationManager), IRegistryCoordinator(registryCoordinator), stakeRegistry);
ServiceManagerBase serviceManagerImplementation = new ServiceManagerBase(IAVSDirectory(avsDirectory), IRegistryCoordinator(registryCoordinator), stakeRegistry);

proxyAdmin.upgrade(
TransparentUpgradeableProxy(payable(address(stakeRegistry))),
Expand Down
Loading