Skip to content

Commit

Permalink
ELIP-001:merge code
Browse files Browse the repository at this point in the history
  • Loading branch information
daring5920 committed Jan 6, 2025
1 parent bec0900 commit e28d90f
Show file tree
Hide file tree
Showing 15 changed files with 444 additions and 144 deletions.
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
61 changes: 35 additions & 26 deletions src/EjectionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,36 +74,45 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{
uint32 ejectedOperators;

bool ratelimitHit;
for(uint8 j = 0; j < _operatorIds[i].length; ++j) {
uint256 operatorStake = stakeRegistry.getCurrentStake(_operatorIds[i][j], quorumNumber);

//if caller is ejector enforce ratelimit
if(
isEjector[msg.sender] &&
quorumEjectionParams[quorumNumber].rateLimitWindow > 0 &&
stakeForEjection + operatorStake > amountEjectable
){
stakeEjectedForQuorum[quorumNumber].push(StakeEjection({
timestamp: block.timestamp,
stakeEjected: stakeForEjection
}));
ratelimitHit = true;
break;
}
if(amountEjectable > 0 || msg.sender == owner()){
for(uint8 j = 0; j < _operatorIds[i].length; ++j) {
uint256 operatorStake = stakeRegistry.getCurrentStake(_operatorIds[i][j], quorumNumber);

//if caller is ejector enforce ratelimit
if(
isEjector[msg.sender] &&
quorumEjectionParams[quorumNumber].rateLimitWindow > 0 &&
stakeForEjection + operatorStake > amountEjectable
){
ratelimitHit = true;

stakeForEjection += operatorStake;
++ejectedOperators;

registryCoordinator.ejectOperator(
registryCoordinator.getOperatorFromId(_operatorIds[i][j]),
abi.encodePacked(quorumNumber)
);

stakeForEjection += operatorStake;
++ejectedOperators;
emit OperatorEjected(_operatorIds[i][j], quorumNumber);

registryCoordinator.ejectOperator(
registryCoordinator.getOperatorFromId(_operatorIds[i][j]),
abi.encodePacked(quorumNumber)
);

emit OperatorEjected(_operatorIds[i][j], quorumNumber);
break;
}

stakeForEjection += operatorStake;
++ejectedOperators;

registryCoordinator.ejectOperator(
registryCoordinator.getOperatorFromId(_operatorIds[i][j]),
abi.encodePacked(quorumNumber)
);

emit OperatorEjected(_operatorIds[i][j], quorumNumber);
}
}

//record the stake ejected if ejector and ratelimit enforced
if(!ratelimitHit && isEjector[msg.sender]){
if(isEjector[msg.sender] && stakeForEjection > 0){
stakeEjectedForQuorum[quorumNumber].push(StakeEjection({
timestamp: block.timestamp,
stakeEjected: stakeForEjection
Expand Down Expand Up @@ -150,7 +159,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{
*/
function amountEjectableForQuorum(uint8 _quorumNumber) public view returns (uint256) {
uint256 cutoffTime = block.timestamp - quorumEjectionParams[_quorumNumber].rateLimitWindow;
uint256 totalEjectable = quorumEjectionParams[_quorumNumber].ejectableStakePercent * stakeRegistry.getCurrentTotalStake(_quorumNumber) / BIPS_DENOMINATOR;
uint256 totalEjectable = uint256(quorumEjectionParams[_quorumNumber].ejectableStakePercent) * uint256(stakeRegistry.getCurrentTotalStake(_quorumNumber)) / uint256(BIPS_DENOMINATOR);
uint256 totalEjected;
uint256 i;
if (stakeEjectedForQuorum[_quorumNumber].length == 0) {
Expand Down
46 changes: 33 additions & 13 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ pragma solidity ^0.8.12;

import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol";

import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
import {BN254} from "./libraries/BN254.sol";
Expand All @@ -33,19 +34,12 @@ contract RegistryCoordinator is
Initializable,
Pausable,
OwnableUpgradeable,
RegistryCoordinatorStorage,
ISocketUpdater,
RegistryCoordinatorStorage,
ISignatureUtils
{
using BitmapUtils for *;
using BN254 for BN254.G1Point;

struct Operator {
address operator;
bytes32 operatorId;
uint96 stake;
}

modifier onlyEjector {
require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector");
_;
Expand All @@ -65,9 +59,10 @@ contract RegistryCoordinator is
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
IIndexRegistry _indexRegistry,
ISocketRegistry _socketRegistry
)
RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry)
RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _socketRegistry)
EIP712("AVSRegistryCoordinator", "v0.0.1")
{
_disableInitializers();
Expand Down Expand Up @@ -355,8 +350,8 @@ contract RegistryCoordinator is
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external {
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered");
emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket);
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegCoord.updateSocket: operator not registered");
_setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket);
}
/*******************************************************************************
EXTERNAL FUNCTIONS - EJECTOR
Expand Down Expand Up @@ -524,6 +519,26 @@ contract RegistryCoordinator is
return results;
}

/**
* @notice Checks if the caller is the ejector
* @dev Reverts if the caller is not the ejector
*/
function _checkEjector() internal view {
require(msg.sender == ejector, "RegCoord.onlyEjector: caller is not the ejector");
}

/**
* @notice Checks if a quorum exists
* @param quorumNumber The quorum number to check
* @dev Reverts if the quorum does not exist
*/
function _checkQuorumExists(uint8 quorumNumber) internal view {
require(
quorumNumber < quorumCount,
"RegCoord.quorumExists: quorum does not exist"
);
}

/**
* @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the
* operator has not registered a pubkey, attempts to register a pubkey using
Expand Down Expand Up @@ -814,6 +829,11 @@ contract RegistryCoordinator is
ejector = newEjector;
}

function _setOperatorSocket(bytes32 operatorId, string memory socket) internal {
socketRegistry.setOperatorSocket(operatorId, socket);
emit OperatorSocketUpdate(operatorId, socket);
}

/*******************************************************************************
VIEW FUNCTIONS
*******************************************************************************/
Expand Down
7 changes: 6 additions & 1 deletion src/RegistryCoordinatorStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol";

abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {

Expand Down Expand Up @@ -39,6 +40,8 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {
IStakeRegistry public immutable stakeRegistry;
/// @notice the Index Registry contract that will keep track of operators' indexes
IIndexRegistry public immutable indexRegistry;
/// @notice the Socket Registry contract that will keep track of operators' sockets
ISocketRegistry public immutable socketRegistry;

/*******************************************************************************
STATE
Expand Down Expand Up @@ -73,12 +76,14 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
IIndexRegistry _indexRegistry,
ISocketRegistry _socketRegistry
) {
serviceManager = _serviceManager;
stakeRegistry = _stakeRegistry;
blsApkRegistry = _blsApkRegistry;
indexRegistry = _indexRegistry;
socketRegistry = _socketRegistry;
}

// storage gap for upgradeability
Expand Down
104 changes: 89 additions & 15 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: BUSL-1.1
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 {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
Expand All @@ -18,7 +19,8 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol";
* This contract can be inherited from or simply used as a point-of-reference.
* @author Layr Labs, Inc.
*/
abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseStorage {
abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
using SafeERC20 for IERC20;
using BitmapUtils for *;

/// @notice when applied to a function, only allows the RegistryCoordinator to call it
Expand All @@ -32,11 +34,15 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt

/// @notice only rewardsInitiator can call createAVSRewardsSubmission
modifier onlyRewardsInitiator() {
_checkRewardsInitiator();
_;
}

function _checkRewardsInitiator() internal view {
require(
msg.sender == rewardsInitiator,
"ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator"
);
_;
}

/// @notice Sets the (immutable) `_registryCoordinator` address
Expand Down Expand Up @@ -79,32 +85,99 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt
* @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 Only callable 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 `rewardsSubmission` is malformed,
* e.g. if the `strategies` and `weights` arrays are of non-equal lengths
* @dev This function may fail to execute with a large number of submissions due to gas limits. Use a
* smaller array of submissions if necessary.
*/
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions)
public
virtual
onlyRewardsInitiator
{
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
rewardsSubmissions[i].token.safeTransferFrom(
msg.sender,
address(this),
rewardsSubmissions[i].amount
);
rewardsSubmissions[i].token.safeIncreaseAllowance(
address(_rewardsCoordinator),
rewardsSubmissions[i].amount
);
}

_rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions);
}

/**
* @notice Creates a new operator-directed rewards submission, to be split amongst the operators and
* set of stakers delegated to operators who are registered to this `avs`.
* @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created.
* @dev Only callable 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 `operatorDirectedRewardsSubmissions`, 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 `operatorDirectedRewardsSubmissions` is malformed.
* @dev This function may fail to execute with a large number of submissions due to gas limits. Use a
* smaller array of submissions if necessary.
*/
function createOperatorDirectedAVSRewardsSubmission(
IRewardsCoordinator.OperatorDirectedRewardsSubmission[]
calldata operatorDirectedRewardsSubmissions
) public virtual onlyRewardsInitiator {
for (
uint256 i = 0;
i < operatorDirectedRewardsSubmissions.length;
++i
) {
// Calculate total amount of token to transfer
uint256 totalAmount = 0;
for (
uint256 j = 0;
j <
operatorDirectedRewardsSubmissions[i].operatorRewards.length;
++j
) {
totalAmount += operatorDirectedRewardsSubmissions[i]
.operatorRewards[j]
.amount;
}

// Transfer token to ServiceManager and approve RewardsCoordinator to transfer again
// in createOperatorDirectedAVSRewardsSubmission() call
operatorDirectedRewardsSubmissions[i].token.safeTransferFrom(
msg.sender,
address(this),
totalAmount
);
operatorDirectedRewardsSubmissions[i].token.safeIncreaseAllowance(
address(_rewardsCoordinator),
totalAmount
);
}

_rewardsCoordinator.createOperatorDirectedAVSRewardsSubmission(
address(this),
operatorDirectedRewardsSubmissions
);
}

/**
* @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 callable by the owner.
*/
function setClaimerFor(address claimer) public virtual onlyOwner {
_rewardsCoordinator.setClaimerFor(claimer);
}

/**
* @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 Expand Up @@ -150,6 +223,7 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt
function getRestakeableStrategies()
external
view
virtual
returns (address[] memory)
{
uint256 quorumCount = _registryCoordinator.quorumCount();
Expand Down Expand Up @@ -188,7 +262,7 @@ abstract contract ServiceManagerBase is OwnableUpgradeable, ServiceManagerBaseSt
*/
function getOperatorRestakedStrategies(
address operator
) external view returns (address[] memory) {
) external view virtual returns (address[] memory) {
bytes32 operatorId = _registryCoordinator.getOperatorId(operator);
uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap(
operatorId
Expand Down
4 changes: 3 additions & 1 deletion src/ServiceManagerBaseStorage.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";

import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
Expand All @@ -13,7 +15,7 @@ import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces
* @author Layr Labs, Inc.
* @notice This storage contract is separate from the logic to simplify the upgrade process.
*/
abstract contract ServiceManagerBaseStorage is IServiceManager {
abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeable {
/**
*
* CONSTANTS AND IMMUTABLES
Expand Down
Loading

0 comments on commit e28d90f

Please sign in to comment.