Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/delegation/providers/eigenlayer/EigenServiceManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import { Access } from "../../../access/Access.sol";
import { IEigenServiceManager } from "../../../interfaces/IEigenServiceManager.sol";
import { IOracle } from "../../../interfaces/IOracle.sol";
import { EigenServiceManagerStorageUtils } from "../../../storage/EigenServiceManagerStorageUtils.sol";
import { EigenOperator, IEigenOperator } from "./EigenOperator.sol";
import { IAllocationManager } from "./interfaces/IAllocationManager.sol";
import { IDelegationManager } from "./interfaces/IDelegationManager.sol";
import { IRewardsCoordinator } from "./interfaces/IRewardsCoordinator.sol";
import { IStrategy } from "./interfaces/IStrategy.sol";
import { IStrategyManager } from "./interfaces/IStrategyManager.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @title EigenServiceManager
/// @author weso, Cap Labs
/// @notice This contract acts as the avs in the eigenlayer protocol
contract EigenServiceManager is IEigenServiceManager, UUPSUpgradeable, Access, EigenServiceManagerStorageUtils {
using SafeERC20 for IERC20;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @inheritdoc IEigenServiceManager
function initialize(
address _accessControl,
EigenAddresses memory _eigenAddresses,
address _oracle,
uint32 _epochsBetweenDistributions
) external initializer {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
__Access_init(_accessControl);
__UUPSUpgradeable_init();
$.eigen = _eigenAddresses;
$.oracle = _oracle;
$.epochsBetweenDistributions = _epochsBetweenDistributions;
$.nextOperatorId++;
$.redistributionRecipients.push(address(this));
// Deploy a instance for the upgradeable beacon proxies
UpgradeableBeacon beacon = new UpgradeableBeacon(address(new EigenOperator()), address(this));
$.eigenOperatorInstance = address(beacon);
// Starting metadata for the avs, can be updated later and should be updated when adding new operators
string memory metadata = "https://cap.app/meta/eigen-avs.json";
_updateAVSMetadataURI(metadata);
}
/// @inheritdoc IEigenServiceManager
function slash(address _operator, address _recipient, uint256 _slashShare, uint48)
external
checkAccess(this.slash.selector)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
if (operatorData.strategy == address(0)) revert ZeroAddress();
if (_recipient == address(0)) revert ZeroAddress();
if (_slashShare == 0) revert ZeroSlash();
address _strategy = operatorData.strategy;
IERC20 _slashedCollateral = IStrategy(_strategy).underlyingToken();
/// Slash share is a percentage of total operators collateral, this is calculated in Delegation.sol
uint256 beforeSlash = _slashedCollateral.balanceOf(address(this));
/// We map to the eigen operator address in this _slash function
/// @dev rounding considerations suggested via eigen
/// https://docs.eigencloud.xyz/products/eigenlayer/developers/howto/build/slashing/precision-rounding-considerations
/// Since we control the magnitude and are the only allocation, rounding is less of a concern
_slash(_strategy, _operator, _slashShare);
/// Send slashed collateral to the liquidator
uint256 slashedAmount = _slashedCollateral.balanceOf(address(this)) - beforeSlash;
if (slashedAmount == 0) revert ZeroSlash();
_slashedCollateral.safeTransfer(_recipient, slashedAmount);
emit Slash(_operator, _recipient, slashedAmount, uint48(block.timestamp));
}
/// @inheritdoc IEigenServiceManager
function distributeRewards(address _operator, address _token)
external
checkAccess(this.distributeRewards.selector)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
uint256 calcIntervalSeconds = IRewardsCoordinator($.eigen.rewardsCoordinator).CALCULATION_INTERVAL_SECONDS();
uint32 lastDistroEpoch = operatorData.lastDistributionEpoch[_token];
/// Fetch the strategy for the operator
address _strategy = operatorData.strategy;
/// Check if rewards are ready - calculate available amount correctly
uint256 _amount = IERC20(_token).balanceOf(address(this)) - $.pendingRewardsByToken[_token];
/// If this is the first distribution, use operator creation epoch
if (lastDistroEpoch == 0) lastDistroEpoch = operatorData.createdAtEpoch;
/// Calculate the current epoch and check if enough time has passed
uint32 currentEpoch = uint32(block.timestamp / calcIntervalSeconds);
uint32 nextAllowedEpoch = lastDistroEpoch + $.epochsBetweenDistributions;
/// If not enough time has passed since last distribution, add to pending rewards
if (currentEpoch < nextAllowedEpoch) {
/// Only add to pending if there are new tokens available
if (_amount > 0) {
$.pendingRewardsByToken[_token] += _amount;
operatorData.pendingRewards[_token] += _amount;
}
return;
}
/// Include both new tokens and any existing pending rewards for this operator
uint256 totalAmount = _amount + operatorData.pendingRewards[_token];
/// Only proceed if there are rewards to distribute
if (totalAmount == 0) return;
_checkApproval(_token, $.eigen.rewardsCoordinator);
_createRewardsSubmission(_operator, _strategy, _token, totalAmount, lastDistroEpoch, currentEpoch);
/// Update accounting: subtract the pending rewards that were just distributed
$.pendingRewardsByToken[_token] -= operatorData.pendingRewards[_token];
operatorData.pendingRewards[_token] = 0;
operatorData.lastDistributionEpoch[_token] = currentEpoch;
emit DistributedRewards(_operator, _token, totalAmount);
}
/// @inheritdoc IEigenServiceManager
function registerOperator(address _eigenOperator, address _avs, uint32[] calldata _operatorSetIds, bytes calldata)
external
checkAccess(this.registerOperator.selector)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
if (_avs != address(this)) revert InvalidAVS();
if (_operatorSetIds.length != 1) revert InvalidOperatorSetIds();
if ($.eigenOperatorToOperator[_eigenOperator] == address(0)) revert OperatorDoesntExist();
IAllocationManager allocationManager = IAllocationManager($.eigen.allocationManager);
IAllocationManager.OperatorSet memory operatorSet =
IAllocationManager.OperatorSet({ avs: _avs, id: _operatorSetIds[0] });
address redistributionRecipient = allocationManager.getRedistributionRecipient(operatorSet);
if (redistributionRecipient != address(this)) revert InvalidRedistributionRecipient();
emit OperatorRegistered(IEigenOperator(_eigenOperator).operator(), _eigenOperator, _avs, _operatorSetIds[0]);
}
/// @inheritdoc IEigenServiceManager
function registerStrategy(address _strategy, address _operator, address _restaker, string memory _operatorMetadata)
external
checkAccess(this.registerStrategy.selector)
returns (uint32 _operatorSetId)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
// Deploy the operator clone that will act as the operator in the eigen system
address eigenOperator = _deployEigenOperator(_operator, _operatorMetadata);
operatorData.eigenOperator = eigenOperator;
_operatorSetId = $.nextOperatorId;
$.eigenOperatorToOperator[eigenOperator] = _operator;
// Checks, no duplicate operators or operator set ids, a strategy can have many operators.
// Since restakers can only delegate to one operator, this is not a problem.
// https://docs.eigencloud.xyz/products/eigenlayer/restakers/restaking-guides/restaking-developer-guide#smart-contract-delegation-user-guide
if (operatorData.strategy != address(0)) revert AlreadyRegisteredOperator();
if (operatorData.operatorSetId != 0) revert OperatorSetAlreadyCreated();
if (IERC20Metadata(address(IStrategy(_strategy).underlyingToken())).decimals() < 6) revert InvalidDecimals();
IAllocationManager allocationManager = IAllocationManager($.eigen.allocationManager);
// Create the operator set params
IAllocationManager.CreateSetParams[] memory params = new IAllocationManager.CreateSetParams[](1);
address[] memory strategies = new address[](1);
strategies[0] = _strategy;
params[0] = IAllocationManager.CreateSetParams({ operatorSetId: _operatorSetId, strategies: strategies });
// Create the operator set
allocationManager.createRedistributingOperatorSets(address(this), params, $.redistributionRecipients);
operatorData.strategy = _strategy;
operatorData.operatorSetId = _operatorSetId;
uint256 calcIntervalSeconds = IRewardsCoordinator($.eigen.rewardsCoordinator).CALCULATION_INTERVAL_SECONDS();
uint256 activationDelay = IRewardsCoordinator($.eigen.rewardsCoordinator).activationDelay();
operatorData.createdAtEpoch = uint32((block.timestamp + activationDelay) / calcIntervalSeconds);
// Callback the operator beacon and register to the operator set
EigenOperator(eigenOperator).registerOperatorSetToServiceManager(_operatorSetId, _restaker);
// Increment the next operator id for the next operator set
$.nextOperatorId++;
emit StrategyRegistered(_strategy, _operator);
}
/// @inheritdoc IEigenServiceManager
function updateAVSMetadataURI(string calldata _metadataURI)
external
checkAccess(this.updateAVSMetadataURI.selector)
{
_updateAVSMetadataURI(_metadataURI);
}
/// @inheritdoc IEigenServiceManager
function setEpochsBetweenDistributions(uint32 _epochsBetweenDistributions)
external
checkAccess(this.setEpochsBetweenDistributions.selector)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
$.epochsBetweenDistributions = _epochsBetweenDistributions;
emit EpochsBetweenDistributionsSet(_epochsBetweenDistributions);
}
/// @inheritdoc IEigenServiceManager
function allocate(address _operator) external {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
if (operatorData.eigenOperator == address(0)) revert ZeroAddress();
EigenOperator(operatorData.eigenOperator).allocate(operatorData.operatorSetId, operatorData.strategy);
}
/// @inheritdoc IEigenServiceManager
function upgradeEigenOperatorImplementation(address _newImplementation)
external
checkAccess(this.upgradeEigenOperatorImplementation.selector)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
if (_newImplementation == address(0)) revert ZeroAddress();
UpgradeableBeacon beacon = UpgradeableBeacon($.eigenOperatorInstance);
beacon.upgradeTo(_newImplementation);
}
/// @inheritdoc IEigenServiceManager
function coverage(address _operator) external view returns (uint256 delegation) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
address _strategy = operatorData.strategy;
if (_strategy == address(0)) return 0;
address[] memory strategies = new address[](1);
strategies[0] = _strategy;
/// Reject small shares for coverage because of rounding concerns
uint256[] memory operatorShares =
IDelegationManager($.eigen.delegationManager).getOperatorShares(operatorData.eigenOperator, strategies);
if (operatorShares[0] < 1e9) return 0;
address _oracle = $.oracle;
(delegation,) = _coverageByStrategy(_operator, _strategy, _oracle);
}
/// @inheritdoc IEigenServiceManager
function getEigenOperator(address _operator) external view returns (address) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
return $.operators[_operator].eigenOperator;
}
/// @inheritdoc IEigenServiceManager
function slashableCollateral(address _operator, uint48) external view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
if (operatorData.strategy == address(0)) return 0;
return _slashableCollateralByStrategy(_operator, operatorData.strategy);
}
/// @inheritdoc IEigenServiceManager
function operatorSetId(address _operator) external view returns (uint32) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
return operatorData.operatorSetId;
}
/// @inheritdoc IEigenServiceManager
function operatorToStrategy(address _operator) external view returns (address) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
return operatorData.strategy;
}
/// @inheritdoc IEigenServiceManager
function eigenAddresses() external view returns (EigenAddresses memory) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
return $.eigen;
}
/// @inheritdoc IEigenServiceManager
function epochsBetweenDistributions() external view returns (uint32) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
return $.epochsBetweenDistributions;
}
/// @inheritdoc IEigenServiceManager
function createdAtEpoch(address _operator) external view returns (uint32) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
return $.operators[_operator].createdAtEpoch;
}
/// @inheritdoc IEigenServiceManager
function calculationIntervalSeconds() external view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
return IRewardsCoordinator($.eigen.rewardsCoordinator).CALCULATION_INTERVAL_SECONDS();
}
/// @inheritdoc IEigenServiceManager
function pendingRewards(address _operator, address _token) external view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
return operatorData.pendingRewards[_token];
}
/// @dev Deploys an eigen operator
/// @param _operator The operator/borrower address
/// @param _operatorMetadata The operator metadata
/// @return _eigenOperator The eigen operator contract address
function _deployEigenOperator(address _operator, string memory _operatorMetadata)
private
returns (address _eigenOperator)
{
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
// Best practice initialize on deployment
bytes memory initdata =
abi.encodeWithSelector(EigenOperator.initialize.selector, address(this), _operator, _operatorMetadata);
_eigenOperator = address(new BeaconProxy($.eigenOperatorInstance, initdata));
}
/// @notice Updates the metadata URI for the AVS
/// @param _metadataURI is the metadata URI for the AVS
function _updateAVSMetadataURI(string memory _metadataURI) private {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
IAllocationManager($.eigen.allocationManager).updateAVSMetadataURI(address(this), _metadataURI);
}
/// @notice Slash the operator
/// @param _strategy The strategy address
/// @param _operator The operator address
/// @param _slashShare The slash share
function _slash(address _strategy, address _operator, uint256 _slashShare) private {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
address[] memory strategies = new address[](1);
strategies[0] = _strategy;
_slashShare += 1;
_slashShare = _slashShare > 1e18 ? 1e18 : _slashShare;
// @dev wads are a percentage of collateral in 1e18
uint256[] memory wadsToSlash = new uint256[](1);
wadsToSlash[0] = _slashShare;
IAllocationManager.SlashingParams memory slashingParams = IAllocationManager.SlashingParams({
operator: operatorData.eigenOperator,
operatorSetId: operatorData.operatorSetId,
strategies: strategies,
wadsToSlash: wadsToSlash,
description: "liquidation"
});
// @dev slash the operator
(uint256 slashId,) = IAllocationManager($.eigen.allocationManager).slashOperator(address(this), slashingParams);
IAllocationManager.OperatorSet memory operatorSet =
IAllocationManager.OperatorSet({ avs: address(this), id: operatorData.operatorSetId });
// @dev clear the burn or redistributable shares, this sends them to the service manager
IStrategyManager($.eigen.strategyManager).clearBurnOrRedistributableSharesByStrategy(
operatorSet, slashId, _strategy
);
}
/// @notice Create a rewards submission
/// @param _operator The operator address
/// @param _strategy The strategy address
/// @param _token The token address
/// @param _amount The amount of tokens (already includes pending rewards)
/// @param _lastDistroEpoch The last distribution epoch
/// @param _currentEpoch The current epoch
function _createRewardsSubmission(
address _operator,
address _strategy,
address _token,
uint256 _amount,
uint256 _lastDistroEpoch,
uint256 _currentEpoch
) private {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
/// Get the strategy for the operator and create the rewards submission
IRewardsCoordinator.OperatorDirectedRewardsSubmission[] memory rewardsSubmissions =
new IRewardsCoordinator.OperatorDirectedRewardsSubmission[](1);
IRewardsCoordinator.StrategyAndMultiplier[] memory _strategiesAndMultipliers =
new IRewardsCoordinator.StrategyAndMultiplier[](1);
IRewardsCoordinator.OperatorReward[] memory _operatorRewards = new IRewardsCoordinator.OperatorReward[](1);
_operatorRewards[0] =
IRewardsCoordinator.OperatorReward({ operator: operatorData.eigenOperator, amount: _amount });
IRewardsCoordinator.OperatorSet memory operatorSet =
IRewardsCoordinator.OperatorSet({ avs: address(this), id: operatorData.operatorSetId });
/// Since there is only 1 strategy multiplier is just 1e18 everything goes to the strategy
_strategiesAndMultipliers[0] =
IRewardsCoordinator.StrategyAndMultiplier({ strategy: _strategy, multiplier: 1e18 });
uint256 calcIntervalSeconds = IRewardsCoordinator($.eigen.rewardsCoordinator).CALCULATION_INTERVAL_SECONDS();
// Start at the next epoch to next double reward the current epoch which should have been included in the previous distribution
_lastDistroEpoch += 1;
uint48 maxDuration = IRewardsCoordinator($.eigen.rewardsCoordinator).MAX_REWARDS_DURATION();
uint256 startTimestamp = _lastDistroEpoch * calcIntervalSeconds;
uint256 duration = (_currentEpoch - _lastDistroEpoch) * calcIntervalSeconds;
if (duration > maxDuration) duration = maxDuration;
rewardsSubmissions[0] = IRewardsCoordinator.OperatorDirectedRewardsSubmission({
strategiesAndMultipliers: _strategiesAndMultipliers,
token: _token,
operatorRewards: _operatorRewards,
startTimestamp: uint32(startTimestamp),
duration: uint32(duration),
description: "interest"
});
IRewardsCoordinator($.eigen.rewardsCoordinator).createOperatorDirectedOperatorSetRewardsSubmission(
operatorSet, rewardsSubmissions
);
}
/// @notice Check if the token has enough allowance for the spender
/// @param _token The token to check
/// @param _spender The spender to check
function _checkApproval(address _token, address _spender) private {
if (IERC20(_token).allowance(_spender, address(this)) == 0) {
IERC20(_token).forceApprove(_spender, type(uint256).max);
}
}
/// @notice Get the slashable collateral for a given operator and strategy
/// @param _operator The operator address
/// @param _strategy The strategy address
/// @return The slashable collateral
function _slashableCollateralByStrategy(address _operator, address _strategy) private view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
address collateralAddress = address(IStrategy(_strategy).underlyingToken());
uint8 decimals = IERC20Metadata(collateralAddress).decimals();
(uint256 collateralPrice,) = IOracle($.oracle).getPrice(collateralAddress);
uint256 collateral = _getSlashableStake(_operator);
uint256 collateralValue = collateral * collateralPrice / (10 ** decimals);
return collateralValue;
}
/// @notice Get the coverage for a given operator and strategy
/// @param _operator The operator address
/// @param _strategy The strategy address
/// @param _oracle The oracle address
/// @return collateralValue The collateral value
/// @return collateral The collateral
function _coverageByStrategy(address _operator, address _strategy, address _oracle)
private
view
returns (uint256 collateralValue, uint256 collateral)
{
address collateralAddress = address(IStrategy(_strategy).underlyingToken());
uint8 decimals = IERC20Metadata(collateralAddress).decimals();
(uint256 collateralPrice,) = IOracle(_oracle).getPrice(collateralAddress);
// @dev get the minimum slashable stake
collateral = _minimumSlashableStake(_operator, _strategy);
collateralValue = collateral * collateralPrice / (10 ** decimals);
}
/// @notice Get the slashable stake for a given operator and strategy
/// @param _operator The operator address
/// @return The slashable stake of the operator
function _getSlashableStake(address _operator) private view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
address _strategy = operatorData.strategy;
// Get the slashable stake for the operator/OperatorSet
uint256 slashableStake = _minimumSlashableStake(_operator, _strategy);
// Get the stake in queue
uint256 stakeInQueue = _slashableStakeInQueue(operatorData.eigenOperator, _strategy);
// Sum up the slashable stake and the stake in queue
uint256 totalSlashableStake = slashableStake + stakeInQueue;
return totalSlashableStake;
}
/// @notice Get the slashable stake in queue for withdrawal from a given operator and strategy
/// @param _operator The operator address
/// @param _strategy The strategy address
/// @return The slashable stake in queue for withdrawal
function _slashableStakeInQueue(address _operator, address _strategy) private view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
// @dev get the slashable stake in queue which are waiting to be withdrawn
return IStrategy(_strategy).sharesToUnderlyingView(
IDelegationManager($.eigen.delegationManager).getSlashableSharesInQueue(_operator, _strategy)
);
}
/// @notice Get the minimum slashable stake for a given operator and strategy
/// @param _operator The operator address
/// @param _strategy The strategy address
/// @return The minimum slashable stake
function _minimumSlashableStake(address _operator, address _strategy) private view returns (uint256) {
EigenServiceManagerStorage storage $ = getEigenServiceManagerStorage();
CachedOperatorData storage operatorData = $.operators[_operator];
IAllocationManager.OperatorSet memory operatorSet =
IAllocationManager.OperatorSet({ avs: address(this), id: operatorData.operatorSetId });
address[] memory operators = new address[](1);
operators[0] = operatorData.eigenOperator;
address[] memory strategies = new address[](1);
strategies[0] = _strategy;
// @dev get the minimum slashable stake at the current block
uint256[][] memory slashableShares = IAllocationManager($.eigen.allocationManager).getMinimumSlashableStake(
operatorSet, operators, strategies, uint32(block.number)
);
return IStrategy(_strategy).sharesToUnderlyingView(slashableShares[0][0]);
}
/// @inheritdoc UUPSUpgradeable
function _authorizeUpgrade(address) internal override checkAccess(bytes4(0)) { }
}
"
},
"contracts/access/Access.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import { IAccess } from "../interfaces/IAccess.sol";
import { IAccessControl } from "../interfaces/IAccessControl.sol";
import { AccessStorageUtils } from "../storage/AccessStorageUtils.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/// @title Access
/// @author kexley, Cap Labs
/// @notice Inheritable access
abstract contract Access is IAccess, Initializable, AccessStorageUtils {
/// @dev Check caller has permissions for a function, revert if call is not allowed
/// @param _selector Function selector
modifier checkAccess(bytes4 _selector) {
_checkAccess(_selector);
_;
}
/// @dev Initialize the access control address
/// @param _accessControl Access control address
function __Access_init(address _accessControl) internal onlyInitializing {
__Access_init_unchained(_accessControl);
}
/// @dev Initialize unchained
/// @param _accessControl Access control address
function __Access_init_unchained(address _accessControl) internal onlyInitializing {
getAccessStorage().accessControl = _accessControl;
}
/// @dev Check caller has access to a function, revert overwise
/// @param _selector Function selector
function _checkAccess(bytes4 _selector) internal view {
bool hasAccess =
IAccessControl(getAccessStorage().accessControl).checkAccess(_selector, address(this), msg.sender);
if (!hasAccess) revert AccessDenied();
}
}
"
},
"contracts/interfaces/IEigenServiceManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
interface IEigenServiceManager {
/// @dev Invalid AVS
error InvalidAVS();
/// @dev Invalid operator set ids
error InvalidOperatorSetIds();
/// @dev Invalid operator
error InvalidOperator();
/// @dev Operator already registered
error AlreadyRegisteredOperator();
/// @dev Invalid redistribution recipient
error InvalidRedistributionRecipient();
/// @dev Zero address
error ZeroAddress();
/// @dev Operator set already created
error OperatorSetAlreadyCreated();
/// @dev Operator doesn't exist
error OperatorDoesntExist();
/// @dev Min magnitude not met
error MinMagnitudeNotMet();
/// @dev Invalid decimals
error InvalidDecimals();
/// @dev Min share not met
error MinShareNotMet();
/// @dev Zero slash
error ZeroSlash();
/// @dev Slash share too small
error SlashShareTooSmall();
/// @dev Operator registered
event OperatorRegistered(
address indexed operator, address indexed eigenOperator, address indexed avs, uint32 operatorSetId
);
/// @dev Emitted on slash
event Slash(address indexed agent, address indexed recipient, uint256 slashShare, uint48 timestamp);
/// @dev Strategy registered
event StrategyRegistered(address indexed strategy, address indexed operator);
/// @dev Epochs between distributions set
event EpochsBetweenDistributionsSet(uint32 epochsBetweenDistributions);
/// @dev Min reward amount set
event MinRewardAmountSet(uint256 minRewardAmount);
/// @dev Distributed rewards
event DistributedRewards(address indexed strategy, address indexed token, uint256 amount);
/// @dev EigenServiceManager storage
/// @param eigen Eigen addresses
/// @param oracle Oracle address
/// @param eigenOperatorInstance Eigen operator instance
/// @param epochsBetweenDistributions Epochs between distributions
/// @param nextOperatorId Next operator id
/// @param pendingRewards Pending rewards
/// @param eigenOperatorToOperator Mapping from eigen operator to operator
struct EigenServiceManagerStorage {
EigenAddresses eigen;
address oracle;
address eigenOperatorInstance;
address[] redistributionRecipients;
uint32 epochsBetweenDistributions;
uint32 nextOperatorId;
mapping(address => uint256) pendingRewardsByToken;
mapping(address => CachedOperatorData) operators;
mapping(address => address) eigenOperatorToOperator;
}
/// @dev Cached operator data
/// @param eigenOperator Eigen operator address
/// @param strategy Strategy address
/// @param createdAtEpoch Epoch at which the operator was created
/// @param operatorSetId Operator set id
/// @param pendingRewards Pending rewards
struct CachedOperatorData {
address eigenOperator;
address strategy;
uint32 createdAtEpoch;
uint32 operatorSetId;
mapping(address => uint256) pendingRewards;
mapping(address => uint32) lastDistributionEpoch;
}
/// @dev Eigen addresses
/// @param allocationManager Allocation manager address
/// @param delegationManager Delegation manager address
/// @param strategyManager Strategy manager address
/// @param rewardsCoordinator Rewards coordinator address
struct EigenAddresses {
address allocationManager;
address delegationManager;
address strategyManager;
address rewardsCoordinator;
}
/// @notice Initialize the EigenServiceManager
/// @param _accessControl Access control contract
/// @param _addresses Eigen addresses
/// @param _oracle Oracle contract
/// @param _rewardDuration Reward duration
function initialize(
address _accessControl,
EigenAddresses memory _addresses,
address _oracle,
uint32 _rewardDuration
) external;
/**
* @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 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.
*/
/**
* @notice Distributes rewards to the operator
* @param _operator The operator to distribute rewards to
* @param _token The token to distribute rewards for
*/
function distributeRewards(address _operator, address _token) external;
/**
* @notice Returns the coverage for an operator
* @param operator The operator to get the coverage for
* @return The coverage of the operator
*/
function coverage(address operator) external view returns (uint256);
/**
* @notice Registers an operator to the AVS, called by the Allocation Manager contract (access control set for the allocation manager).
* @param _operator The operator to register
* @param _avs The AVS to register the operator to
* @param _operatorSetIds The operator set ids to register the operator to
* @param _data Additional data
*/
function registerOperator(address _operator, address _avs, uint32[] calldata _operatorSetIds, bytes calldata _data)
external;
/**
* @notice Registers a strategy to the AVS
* @param _strategy The strategy to register
* @param _operator The operator to register the strategy to
* @param _restaker The restaker to register the strategy to
* @param _operatorMetadata The metadata for the operator
* @return _operatorSetId The operator set id
*/
function registerStrategy(address _strategy, address _operator, address _restaker, string memory _operatorMetadata)
external
returns (uint32 _operatorSetId);
/**
* @notice Slashes an operator
* @param _operator The operator to slash
* @param _recipient The recipient of the slashed collateral
* @param _slashShare The share of the slashable collateral to slash
* @param _timestamp The timestamp of the slash (unused for eigenlayer)
*/
function slash(address _operator, address _recipient, uint256 _slashShare, uint48 _timestamp) external;
/**
* @notice Allocates the operator set, is public and can be called permissionless. We would have allocated on registerStrategy but it needs to wait at least a block.
* @param _operator Operator address
*/
function allocate(address _operator) external;
/**
* @notice Returns the slashable collateral for an operator
* @param operator The operator to get the slashable collateral for
* @param timestamp The timestamp to get the slashable collateral for (unused for eigenlayer)
* @return The slashable collateral of the operator
*/
function slashableCollateral(address operator, uint48 timestamp) external view returns (uint256);
/**
* @notice Sets the epochs between distributions
* @param _epochsBetweenDistributions The epochs between distributions
*/
function setEpochsBetweenDistributions(uint32 _epochsBetweenDistributions) external;
/**
* @notice Updates the AVS metadata URI
* @param _metadataURI The new metadata URI
*/
function updateAVSMetadataURI(string calldata _metadataURI) external;
/**
* @notice Upgrades the eigen operator implementation
* @param _newImplementation The new implementation
*/
function upgradeEigenOperatorImplementation(address _newImplementation) external;
/**
* @notice Returns the eigen addresses
* @return The eigen addresses
*/
function eigenAddresses() external view returns (EigenAddresses memory);
/**
* @notice Returns the operator to strategy mapping
* @return The operator to strategy mapping
*/
function operatorToStrategy(address operator) external view returns (address);
/**
* @notice Returns the operator set id for an operator
* @param operator The operator to get the operator set id for
* @return The operator set id of the operator
*/
function operatorSetId(address operator) external view returns (uint32);
/**
* @notice Returns the epochs between distributions
* @return The epochs between distributions
*/
function epochsBetweenDistributions() external view returns (uint32);
/**
* @notice Returns the created at epoch for an operator
* @param operator The operator to get the created at epoch for
* @return The created at epoch of the operator
*/
function createdAtEpoch(address operator) external view returns (uint32);
/**
* @notice Returns the calculation interval seconds
* @return The calculation interval seconds
*/
function calculationIntervalSeconds() external view returns (uint256);
/**
* @notice Returns the pending rewards for an operator
* @param _strategy The strategy to get the pending rewards for
* @param _token The token to get the pending rewards for
* @return The pending rewards of the strategy
*/
function pendingRewards(address _strategy, address _token) external view returns (uint256);
/**
* @notice Returns the eigen operator for an operator
* @param _operator The operator to get the eigen operator for
* @return The eigen operator of the operator
*/
function getEigenOperator(address _operator) external view returns (address);
}
"
},
"contracts/interfaces/IOracle.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import { IPriceOracle } from "./IPriceOracle.sol";
import { IRateOracle } from "./IRateOracle.sol";
/// @title Oracle
/// @author kexley, Cap Labs
/// @notice Price and rate oracles are unified
interface IOracle is IPriceOracle, IRateOracle {
/// @notice Initialize the oracle
/// @param _accessControl Access control address
function initialize(address _accessControl) external;
}
"
},
"contracts/storage/EigenServiceManagerStorageUtils.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import { IEigenServiceManager } from "../interfaces/IEigenServiceManager.sol";
/// @title EigenServiceManager Storage Utils
/// @author weso, Cap Labs
/// @notice Storage utilities for EigenServiceManager
abstract contract EigenServiceManagerStorageUtils {
/// @dev keccak256(abi.encode(uint256(keccak256("cap.storage.EigenServiceManager")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant EigenServiceManagerStorageLocation =
0x9813e4033b5f31d05a061ad9d06fb8352756b0443d3cc09baeca467c0811ef00;
/// @dev Get EigenServiceManager storage
/// @return $ Storage pointer
function getEigenServiceManagerStorage()
internal
pure
returns (IEigenServiceManager.EigenServiceManagerStorage storage $)
{
assembly {
$.slot := EigenServiceManagerStorageLocation
}
}
}
"
},
"contracts/delegation/providers/eigenlayer/EigenOperator.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { IEigenOperator } from "../../../interfaces/IEigenOperator.sol";
import { IEigenServiceManager } from "../../../interfaces/IEigenServiceManager.sol";
import { EigenOperatorStorageUtils } from "../../../storage/EigenOperatorStorageUtils.sol";
import { IAllocationManager } from "./interfaces/IAllocationManager.sol";
import { IDelegationManager } from "./interfaces/IDelegationManager.sol";
import { IRewardsCoordinator } from "./interfaces/IRewardsCoordinator.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/// @title EigenOperator
/// @author weso, Cap Labs
/// @notice This contract manages the eigen operator as proxy to disable some functionality for operators
contract EigenOperator is IEigenOperator, Initializable, EigenOperatorStorageUtils {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @inheritdoc IEigenOperator
function initialize(address _serviceManager, address _operator, string calldata _metadata) external initializer {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
$.serviceManager = _serviceManager;
$.operator = _operator;
$.totpPeriod = 28 days; // Arbitrary value
// Fetch the eigen addresses
IEigenServiceManager.EigenAddresses memory eigenAddresses =
IEigenServiceManager(_serviceManager).eigenAddresses();
$.allocationManager = eigenAddresses.allocationManager;
$.delegationManager = eigenAddresses.delegationManager;
$.rewardsCoordinator = eigenAddresses.rewardsCoordinator;
// Register as an operator on delegation manager
IDelegationManager($.delegationManager).registerAsOperator(address(this), 0, _metadata);
}
/// @inheritdoc IEigenOperator
function registerOperatorSetToServiceManager(uint32 _operatorSetId, address _staker) external {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
if (msg.sender != $.serviceManager) revert NotServiceManager();
if ($.restaker != address(0)) revert AlreadyRegistered();
if (_staker == address(0)) revert ZeroAddress();
/// @dev The digest is calculated using the staker and operator addresses
bytes32 digest = calculateTotpDigestHash(_staker, address(this));
/// @dev Allowlist the digest for delegation approval from the staker
$.allowlistedDigests[digest] = true;
$.restaker = _staker;
// Build the register params
uint32[] memory operatorSetIds = new uint32[](1);
operatorSetIds[0] = _operatorSetId;
IAllocationManager.RegisterParams memory params =
IAllocationManager.RegisterParams({ avs: msg.sender, operatorSetIds: operatorSetIds, data: "" });
// 1. Register the operator set to the service manager, which in turn calls RegisterOperator on the Eigen Service Manager
IAllocationManager($.allocationManager).registerForOperatorSets(address(this), params);
// 2. Set the operator split to 0, all rewards go to restakers
IRewardsCoordinator($.rewardsCoordinator).setOperatorAVSSplit(address(this), msg.sender, 0);
}
/// @inheritdoc IEigenOperator
function allocate(uint32 _operatorSetId, address _strategy) external {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
if (msg.sender != $.serviceManager) revert NotServiceManager();
(, IAllocationManager.Allocation[] memory _allocations) =
IAllocationManager($.allocationManager).getStrategyAllocations(address(this), _strategy);
if (_allocations.length != 0) revert AlreadyAllocated();
// The strategy that the restakers capital is deployed to
address[] memory strategies = new address[](1);
strategies[0] = _strategy;
// Only 1 allocation so 1e18 just means everything will be allocated to the avs
uint64[] memory magnitudes = new uint64[](1);
magnitudes[0] = 1e18;
// Create the allocation params
IAllocationManager.OperatorSet memory operatorSet =
IAllocationManager.OperatorSet({ avs: msg.sender, id: _operatorSetId });
IAllocationManager.AllocateParams[] memory allocations = new IAllocationManager.AllocateParams[](1);
allocations[0] = IAllocationManager.AllocateParams({
operatorSet: operatorSet,
strategies: strategies,
newMagnitudes: magnitudes
});
// Allocates the operator set. Can only be called after ALLOCATION_CONFIGURATION_DELAY (approximately 17.5 days) has passed since registration.
IAllocationManager($.allocationManager).modifyAllocations(address(this), allocations);
}
/// @inheritdoc IEigenOperator
function updateOperatorMetadataURI(string calldata _metadataURI) external {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
if (msg.sender != $.operator) revert NotOperator();
IDelegationManager($.delegationManager).updateOperatorMetadataURI(address(this), _metadataURI);
}
/// @inheritdoc IEigenOperator
function advanceTotp() external {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
if (msg.sender != $.restaker) revert NotRestaker();
// If for some reason the delegation approval has expired, allowlist the new digest
// This shouldn't matter since only the restaker can call this function
bytes32 digest = calculateTotpDigestHash($.restaker, address(this));
$.allowlistedDigests[digest] = true;
}
/// @inheritdoc IEigenOperator
function eigenServiceManager() external view returns (address) {
return getEigenOperatorStorage().serviceManager;
}
/// @inheritdoc IEigenOperator
function operator() external view returns (address) {
return getEigenOperatorStorage().operator;
}
/// @inheritdoc IEigenOperator
function restaker() external view returns (address) {
return getEigenOperatorStorage().restaker;
}
/// @inheritdoc IEigenOperator
function isValidSignature(bytes32 _digest, bytes memory) external view override returns (bytes4 magicValue) {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
/// If the created at epoch is > the current epoch, the operator is not allowed to delegate
uint32 createdAtEpoch = IEigenServiceManager($.serviceManager).createdAtEpoch($.operator);
uint256 calcIntervalSeconds = IEigenServiceManager($.serviceManager).calculationIntervalSeconds();
uint32 currentEpoch = uint32(block.timestamp / calcIntervalSeconds);
if (createdAtEpoch > currentEpoch) return bytes4(0xffffffff);
// This gets called by the delegation manager to check if the operator is allowed to delegate
if ($.allowlistedDigests[_digest]) {
return bytes4(0x1626ba7e); // ERC1271 magic value for valid signatures
} else {
return bytes4(0xffffffff);
}
}
/// @inheritdoc IEigenOperator
function getCurrentTotpExpiryTimestamp() public view returns (uint256) {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
uint256 current = block.timestamp / $.totpPeriod;
return (current + 1) * $.totpPeriod; // End of the current period
}
/// @inheritdoc IEigenOperator
function currentTotp() public view returns (uint256) {
EigenOperatorStorage storage $ = getEigenOperatorStorage();
return block.timestamp / $.totpPeriod;
}
/// @inheritdoc IEigenOperator
function calculateTotpDigestHash(address _staker, address _operator) public view returns (bytes32) {
uint256 expiryTimestamp = getCurrentTotpExpiryTimestamp();
return IDelegationManager(getEigenOperatorStorage().delegationManager).calculateDelegationApprovalDigestHash(
_staker, _operator, address(this), bytes32(uint256(expiryTimestamp)), expiryTimestamp
);
}
}
"
},
"contracts/delegation/providers/eigenlayer/interfaces/IAllocationManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
interface IAllocationManager {
/**
* @notice Struct containing parameters to slashing
* @param operator the address to slash
* @param operatorSetId the ID of the operatorSet the operator is being slashed on behalf of
* @param strategies the set of strategies to slash
* @param wadsToSlash the parts in 1e18 to slash, this will be proportional to the operator's
* slashable stake allocation for the operatorSet
* @param description the description of the slashing provided by the AVS for legibility
*/
struct SlashingParams {
address operator;
uint32 operatorSetId;
address[] strategies;
uint256[] wadsToSlash;
string description;
}
/**
* @notice Parameters used by an AVS to create new operator sets
* @param operatorSetId the id of the operator set to create
* @param strategies the strategies to add as slashable to the operator set
*/
struct CreateSetParams {
uint32 operatorSetId;
address[] strategies;
}
/**
* @notice An operator set identified by the AVS address and an identifier
* @param avs The address of the AVS this operator set belongs to
* @param id The unique identifier for the operator set
*/
struct OperatorSet {
address avs;
uint32 id;
}
/**
* @notice Called by an AVS to slash an operator in a given operator set. The operator must be registered
* and have slashable stake allocated to the operator set.
*
* @param avs The AVS address initiating the slash.
* @param params The slashing parameters, containing:
* - operator: The operator to slash.
* - operatorSetId: The ID of the operator set the operator is being slashed from.
* - strategies: Array of strategies to slash allocations from (must be in ascending order).
* - wadsToSlash: Array of proportions to slash from each strategy (must be between 0 and 1e18).
* - description: Description of why the operator was slashed.
*
* @return slashId The ID of the slash.
* @return shares The amount of shares that were slashed for each strategy.
*
* @dev For each strategy:
* 1. Reduces the operator's current allocation magnitude by wadToSlash proportion.
* 2. Reduces the strategy's max and encumbered magnitudes proportionally.
* 3. If there is a pending deallocation, reduces it proportionally.
* 4. Updates the operator's shares in the DelegationManager.
*
* @dev Small slashing amounts may not result in actual token burns due to
* rounding, which will result in small amounts of tokens locked in the contract
* rather than fully burning through the burn mechanism.
*/
function slashOperator(address avs, SlashingParams calldata params)
external
returns (uint256 slashId, uint256[] memory shares);
/**
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
*
* @param metadataURI The URI for metadata associated with an AVS.
*
* @dev Note that the `metadataURI` is *never stored* and is only emitted in the `AVSMetadataURIUpdated` event.
*/
function updateAVSMetadataURI(address avs, string calldata metadataURI) external;
/**
* @notice Allows an AVS to create new operator sets, defining strategies that the operator set uses
*/
function createOperatorSets(address avs, CreateSetParams[] calldata params) external;
/**
* @notice Returns the minimum amount of stake that will be slashable as of some future block,
* according to each operator's allocation from each strategy to the operator set. Note that this function
* will return 0 for the slashable stake if the operator is not slashable at the time of the call.
* @dev This method queries actual delegated stakes in the DelegationManager and applies
* each operator's allocation to the stake to produce the slashable stake each allocation
* represents. This method does not consider slashable stake in the withdrawal queue even though there could be
* slashable stake in the queue.
* @dev This minimum takes into account `futureBlock`, and will omit any pending magnitude
* diffs that will not be in effect as of `futureBlock`. NOTE that in order to get the true
* minimum slashable stake as of some future block, `futureBlock` MUST be greater than block.number
* @dev NOTE that `futureBlock` should be fewer than `DEALLOCATION_DELAY` blocks in the future,
* or the values returned from this method may not be accurate due to deallocations.
* @param operatorSet the operator set to query
* @param operators the list of operators whose slashable stakes will be returned
* @param strategies the strategies that each slashable stake corresponds to
* @param futureBlock the block at which to get allocation information. Should be a future block.
*/
function getMinimumSlashableStake(
OperatorSet memory operatorSet,
address[] memory operators,
address[] memory strategies,
uint32 futureBlock
) external view returns (uint256[][] memory slashableStake);
/**
* @notice Returns the address where slashed funds will be sent for a given operator set.
* @param operatorSet The Operator Set to query.
* @return For redistributing Operator Sets, returns the configured redistribution address set during Operator Set creation.
* For non-redistributing operator sets, returns the `DEFAULT_BURN_ADDRESS`.
*/
function getRedistributionRecipient(OperatorSet memory operatorSet) external view returns (address);
/**
* @notice Adds strategies to an operator set.
* @param avs The AVS address initiating the addition.
* @param operatorSetId The ID of the operator set to which strategies are being added.
* @param strategies The strategies to add to the operator set.
*/
function addStrategiesToOperatorSet(address avs, uint32 operatorSetId, address[] calldata strategies) external;
/**
* @notice Allows an AVS to create new operator sets, defining strategies that the operator set uses
*/
function createRedistributingOperatorSets(
address avs,
CreateSetParams[] calldata params,
address[] calldata redistributionRecipients
) external;
/**
* @notice Returns the maximum magnitude of a given operator and strategy.
* @param operator The operator address.
* @param strategy The strategy address.
* @return The maximum magnitude of the operator and strategy.
*/
function getMaxMagnitude(address operator, address strategy) external view returns (uint64);
/**
* @notice Returns the allocatable magnitude of a given operator and strategy.
* @param operator The operator address.
* @param strategy The strategy address.
* @return The allocatable magnitude of the operator and strategy.
*/
function getAllocatableMagnitude(address operator, address strategy) external view returns (uint64);
/**
* @notice Parameters used to register for an AVS's operator sets
* @param avs the AVS being registered for
* @param operatorSetIds the operator sets within the AVS to register for
* @param data extra data to be passed to the AVS to complete registration
*/
struct RegisterParams {
address avs;
uint32[] operatorSetIds;
bytes data;
}
/**
* @notice Allows an operator to register for an operator set.
* @param operator The operator address.
* @param params The register parameters, containing:
* - avs: The AVS being registered for.
* - operatorSetIds: The operator sets within the AVS to register for.
* - data: Extra data to be passed to the AVS to complete registration.
*/
function registerForOperatorSets(address operator, RegisterParams calldata params) external;
/**
* @notice struct used to modify the allocation of slashable magnitude to an operator set
* @param operatorSet the operator set to modify the allocation for
* @param strategies the strategies to modify allocations for
* @param newMagnitudes the new magnitude to allocate for each strategy to this operator set
*/
struct AllocateParams {
OperatorSet operatorSet;
address[] strategies;
uint64[] newMagnitudes;
}
/**
* @notice Modifies the proportions of slashable stake allocated to an operator set from a list of strategies
* Note that deallocations remain slashable for DEALLOCATION_DELAY blocks therefore when they are cleared they may
* free up less allocatable magnitude than initially deallocated.
* @param operator the operator to modify allocations for
* @param params array of magnitude adjustments for one or more operator sets
* @dev Updates encumberedMagnitude for the updated strategies
*/
function modifyAllocations(address operator, AllocateParams[] calldata params) external;
/**
* @notice Defines allocation information from a strategy to an operator set, for an operator
* @param currentMagnitude the current magnitude allocated from the strategy to the operator set
* @param pendingDiff a pending change in magnitude, if it exists (0 otherwise)
* @param effectBlock the block at which the pending magnitude diff will take effect
*/
struct Allocation {
uint64 currentMagnitude;
int128 pendingDiff;
uint32 effectBlock;
}
/**
* @notice Returns the allocation information from a strategy to an operator set, for an operator
* @param operator the operator to get the allocation information for
* @param strategy the strategy to get the allocation information for
* @return operatorSet the operator set to which the strategy is allocated
* @return allocation the allocation information
*/
function getStrategyAllocations(address operator, address strategy)
external
view
returns (OperatorSet[] memory, Allocation[] memory);
}
"
},
"contracts/delegation/providers/eigenlayer/interfaces/IDelegationManager.sol": {
"content": " // SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
interface IDelegationManager {
/// @notice Struct that bundles together a signature and an expiration time for the signature.
/// @dev Used primarily for stack management.
struct SignatureWithExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* @param strategies The strategies to withdraw from
* @param depositShares For each strategy, the number of deposit shares to withdraw. Deposit shares can
* be queried via `getDepositedShares`.
* NOTE: The number of shares ultimately received when a withdrawal is completed may be lower depositShares
* if the staker or their delegated operator has experienced slashing.
* @param __deprecated_withdrawer This field is ignored. The only party that may complete a withdrawal
* is the staker that originally queued it. Alternate withdrawers are not supported.
*/
struct QueuedWithdrawalParams {
address[] strategies;
uint256[] depositShares;
address __deprecated_withdrawer;
}
/**
* @dev A struct representing an existing queued withdrawal. After the withdrawal delay has elapsed, this withdrawal can be completed via `completeQueuedWithdrawal`.
* A `Withdrawal` is created by the `DelegationManager` when `queueWithdrawals` is called. The `withdrawalRoots` hashes returned by `queueWithdrawals` can be used
* to fetch the corresponding `Withdrawal` from storage (via `getQueuedWithdrawal`).
*
* @param staker The address that queued the withdrawal
* @param delegatedTo The address that the staker was delegated to at the time the withdrawal was queued. Used to determine if additional slashing occurred before
* this withdrawal became completable.
* @param withdrawer The address that will call the contract to complete the withdrawal. Note that this will always equal `staker`; alternate withdrawers are not
* supported at this time.
* @param nonce The staker's `cumulativeWithdrawalsQueued` at time of queuing. Used to ensure withdrawals have unique hashes.
* @param startBlock The block number when the withdrawal was queued.
* @param strategies The strategies requested for withdrawal when the withdrawal was queued
* @param scaledShares The staker's deposit shares requested for withdrawal, scaled by the staker's `depositScalingFactor`. Upon completion, these will be
* scaled by the appropriate slashing factor as of the withdrawal's completable block. The result is what is actually withdrawable.
*/
struct Withdrawal {
address staker;
address delegatedTo;
address withdrawer;
uint256 nonce;
uint32 startBlock;
address[] strategies;
uint256[] scaledShares;
}
/// @notice Get the slashable shares in queue for a given operator and strategy
/// @param operator The operator address
/// @param strategy The strategy address
/// @return The slashable shares in queue
function getSlashableSharesInQueue(address operator, address strategy) external view returns (uint256);
/// @notice Get the operator shares for a given operator and strategies
/// @param operator The operator address
/// @param strategies The strategies
/// @return The operator shares
function getOperatorShares(address operator, address[] memory strategies)
external
view
returns (uint256[] memory);
/**
* @notice Returns the number of shares in storage for a staker and all their strategies
*/
function getDepositedShares(address staker) external view returns (address[] memory, uint256[] memory);
function queueWithdrawals(QueuedWithdrawalParams[] calldata params)
external
returns (bytes32[] memory withdrawalRoots);
/**
* @notice Used to complete a queued withdrawal
* @param withdrawal The withdrawal to complete
* @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array.
* @param tokens For each `withdrawal.strategies`, the underlying token of the strategy
* NOTE: if `receiveAsTokens` is false, the `tokens` array is unused and can be filled with default values. However, `tokens.length` MUST still be equal to `withdrawal.strategies.length`.
* NOTE: For the `beaconChainETHStrategy`, the corresponding `tokens` value is ignored (can be 0).
* @param receiveAsTokens If true, withdrawn shares will be converted to tokens and sent to the caller. If false, the caller receives shares that can be delegated to an operator.
* NOTE: if the caller receives shares and is currently delegated to an operator, the received shares are
* automatically delegated to the caller's current operator.
*/
function completeQueuedWithdrawal(Withdrawal callda
Submitted on: 2025-10-24 09:43:14
Comments
Log in to comment.
No comments yet.