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": {
"lib/eigenlayer-middleware/src/StakeRegistry.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IDelegationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol";
import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol";
import {IStakeRegistry, IStakeRegistryTypes} from "./interfaces/IStakeRegistry.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
/**
* @title A `Registry` that keeps track of stakes of operators for up to 256 quorums.
* Specifically, it keeps track of
* 1) The stake of each operator in all the quorums they are a part of for block ranges
* 2) The total stake of all operators in each quorum for block ranges
* 3) The minimum stake required to register for each quorum
* It allows an additional functionality (in addition to registering and deregistering) to update the stake of an operator.
* @author Layr Labs, Inc.
*/
contract StakeRegistry is StakeRegistryStorage {
using BitmapUtils for *;
modifier onlySlashingRegistryCoordinator() {
_checkSlashingRegistryCoordinator();
_;
}
modifier onlyCoordinatorOwner() {
_checkSlashingRegistryCoordinatorOwner();
_;
}
modifier quorumExists(
uint8 quorumNumber
) {
_checkQuorumExists(quorumNumber);
_;
}
constructor(
ISlashingRegistryCoordinator _slashingRegistryCoordinator,
IDelegationManager _delegationManager,
IAVSDirectory _avsDirectory,
IAllocationManager _allocationManager
)
StakeRegistryStorage(
_slashingRegistryCoordinator,
_delegationManager,
_avsDirectory,
_allocationManager
)
{}
/**
*
* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR
*
*/
/// @inheritdoc IStakeRegistry
function registerOperator(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlySlashingRegistryCoordinator returns (uint96[] memory, uint96[] memory) {
uint96[] memory currentStakes = new uint96[](quorumNumbers.length);
uint96[] memory totalStakes = new uint96[](quorumNumbers.length);
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
// Retrieve the operator's current weighted stake for the quorum, reverting if they have not met
// the minimum.
(uint96 currentStake, bool hasMinimumStake) =
_weightOfOperatorForQuorum(quorumNumber, operator);
require(hasMinimumStake, BelowMinimumStakeRequirement());
// Update the operator's stake
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
newStake: currentStake
});
// Update this quorum's total stake by applying the operator's delta
currentStakes[i] = currentStake;
totalStakes[i] = _recordTotalStakeUpdate(quorumNumber, stakeDelta);
}
return (currentStakes, totalStakes);
}
/// @inheritdoc IStakeRegistry
function deregisterOperator(
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlySlashingRegistryCoordinator {
/**
* For each quorum, remove the operator's stake for the quorum and update
* the quorum's total stake to account for the removal
*/
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
// Update the operator's stake for the quorum and retrieve the shares removed
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
newStake: 0
});
// Apply the operator's stake delta to the total stake for this quorum
_recordTotalStakeUpdate(quorumNumber, stakeDelta);
}
}
/// @inheritdoc IStakeRegistry
function updateOperatorsStake(
address[] memory operators,
bytes32[] memory operatorIds,
uint8 quorumNumber
) external onlySlashingRegistryCoordinator returns (bool[] memory) {
bool[] memory shouldBeDeregistered = new bool[](operators.length);
/**
* For each quorum, update the operator's stake and record the delta
* in the quorum's total stake.
*
* If the operator no longer has the minimum stake required to be registered
* in the quorum, the quorum number is added to `quorumsToRemove`, which
* is returned to the registry coordinator.
*/
_checkQuorumExists(quorumNumber);
// Fetch the operators' current stake, applying weighting parameters and checking
// against the minimum stake requirements for the quorum.
(uint96[] memory stakeWeights, bool[] memory hasMinimumStakes) =
_weightOfOperatorsForQuorum(quorumNumber, operators);
int256 totalStakeDelta = 0;
// If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal
/// also handle setting the operator's stake to 0 and remove them from the quorum
for (uint256 i = 0; i < operators.length; i++) {
if (!hasMinimumStakes[i]) {
stakeWeights[i] = 0;
shouldBeDeregistered[i] = true;
}
// Update the operator's stake and retrieve the delta
// If we're deregistering them, their weight is set to 0
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorIds[i],
quorumNumber: quorumNumber,
newStake: stakeWeights[i]
});
totalStakeDelta += stakeDelta;
}
// Apply the delta to the quorum's total stake
_recordTotalStakeUpdate(quorumNumber, totalStakeDelta);
return shouldBeDeregistered;
}
/// @inheritdoc IStakeRegistry
function initializeDelegatedStakeQuorum(
uint8 quorumNumber,
uint96 minimumStake,
StrategyParams[] memory _strategyParams
) public virtual onlySlashingRegistryCoordinator {
require(!_quorumExists(quorumNumber), QuorumAlreadyExists());
_addStrategyParams(quorumNumber, _strategyParams);
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
_setStakeType(quorumNumber, IStakeRegistryTypes.StakeType.TOTAL_DELEGATED);
_totalStakeHistory[quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: 0
})
);
}
/// @inheritdoc IStakeRegistry
function initializeSlashableStakeQuorum(
uint8 quorumNumber,
uint96 minimumStake,
uint32 lookAheadPeriod,
StrategyParams[] memory _strategyParams
) public virtual onlySlashingRegistryCoordinator {
require(!_quorumExists(quorumNumber), QuorumAlreadyExists());
_addStrategyParams(quorumNumber, _strategyParams);
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
_setStakeType(quorumNumber, IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE);
_setLookAheadPeriod(quorumNumber, lookAheadPeriod);
_totalStakeHistory[quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: 0
})
);
}
/// @inheritdoc IStakeRegistry
function setMinimumStakeForQuorum(
uint8 quorumNumber,
uint96 minimumStake
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
}
/// @inheritdoc IStakeRegistry
function setSlashableStakeLookahead(
uint8 quorumNumber,
uint32 _lookAheadBlocks
) external onlyCoordinatorOwner quorumExists(quorumNumber) {
_setLookAheadPeriod(quorumNumber, _lookAheadBlocks);
}
/// @inheritdoc IStakeRegistry
function addStrategies(
uint8 quorumNumber,
StrategyParams[] memory _strategyParams
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
_addStrategyParams(quorumNumber, _strategyParams);
uint256 numStratsToAdd = _strategyParams.length;
address avs = registryCoordinator.avs();
if (allocationManager.isOperatorSet(OperatorSet(avs, quorumNumber))) {
IStrategy[] memory strategiesToAdd = new IStrategy[](numStratsToAdd);
for (uint256 i = 0; i < numStratsToAdd; i++) {
strategiesToAdd[i] = _strategyParams[i].strategy;
}
allocationManager.addStrategiesToOperatorSet({
avs: avs,
operatorSetId: quorumNumber,
strategies: strategiesToAdd
});
}
}
/// @inheritdoc IStakeRegistry
function removeStrategies(
uint8 quorumNumber,
uint256[] memory indicesToRemove
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
uint256 toRemoveLength = indicesToRemove.length;
require(toRemoveLength > 0, InputArrayLengthZero());
StrategyParams[] storage _strategyParams = strategyParams[quorumNumber];
IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber];
IStrategy[] memory _strategiesToRemove = new IStrategy[](toRemoveLength);
for (uint256 i = 0; i < toRemoveLength; i++) {
_strategiesToRemove[i] = _strategyParams[indicesToRemove[i]].strategy;
emit StrategyRemovedFromQuorum(
quorumNumber, _strategyParams[indicesToRemove[i]].strategy
);
emit StrategyMultiplierUpdated(
quorumNumber, _strategyParams[indicesToRemove[i]].strategy, 0
);
// Replace index to remove with the last item in the list, then pop the last item
_strategyParams[indicesToRemove[i]] = _strategyParams[_strategyParams.length - 1];
_strategyParams.pop();
_strategiesPerQuorum[indicesToRemove[i]] =
_strategiesPerQuorum[_strategiesPerQuorum.length - 1];
_strategiesPerQuorum.pop();
}
address avs = registryCoordinator.avs();
if (allocationManager.isOperatorSet(OperatorSet(avs, quorumNumber))) {
allocationManager.removeStrategiesFromOperatorSet({
avs: avs,
operatorSetId: quorumNumber,
strategies: _strategiesToRemove
});
}
}
/// @inheritdoc IStakeRegistry
function modifyStrategyParams(
uint8 quorumNumber,
uint256[] calldata strategyIndices,
uint96[] calldata newMultipliers
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
uint256 numStrats = strategyIndices.length;
require(numStrats > 0, InputArrayLengthZero());
require(newMultipliers.length == numStrats, InputArrayLengthMismatch());
StrategyParams[] storage _strategyParams = strategyParams[quorumNumber];
for (uint256 i = 0; i < numStrats; i++) {
// Change the strategy's associated multiplier
_strategyParams[strategyIndices[i]].multiplier = newMultipliers[i];
emit StrategyMultiplierUpdated(
quorumNumber, _strategyParams[strategyIndices[i]].strategy, newMultipliers[i]
);
}
}
/**
*
* INTERNAL FUNCTIONS
*
*/
function _getStakeUpdateIndexForOperatorAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) internal view returns (uint32) {
uint256 length = operatorStakeHistory[operatorId][quorumNumber].length;
// Iterate backwards through operatorStakeHistory until we find an update that preceeds blockNumber
for (uint256 i = length; i > 0; i--) {
if (
operatorStakeHistory[operatorId][quorumNumber][i - 1].updateBlockNumber
<= blockNumber
) {
return uint32(i - 1);
}
}
// If we hit this point, no stake update exists at blockNumber
revert(
"StakeRegistry._getStakeUpdateIndexForOperatorAtBlockNumber: no stake update found for operatorId and quorumNumber at block number"
);
}
function _setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) internal {
minimumStakeForQuorum[quorumNumber] = minimumStake;
emit MinimumStakeForQuorumUpdated(quorumNumber, minimumStake);
}
/**
* @notice Records that `operatorId`'s current stake for `quorumNumber` is now `newStake`
* @return The change in the operator's stake as a signed int256
*/
function _recordOperatorStakeUpdate(
bytes32 operatorId,
uint8 quorumNumber,
uint96 newStake
) internal returns (int256) {
uint96 prevStake;
uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length;
if (historyLength == 0) {
// No prior stake history - push our first entry
operatorStakeHistory[operatorId][quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: newStake
})
);
} else {
// We have prior stake history - fetch our last-recorded stake
StakeUpdate storage lastUpdate =
operatorStakeHistory[operatorId][quorumNumber][historyLength - 1];
prevStake = lastUpdate.stake;
// Short-circuit in case there's no change in stake
if (prevStake == newStake) {
return 0;
}
/**
* If our last stake entry was made in the current block, update the entry
* Otherwise, push a new entry and update the previous entry's "next" field
*/
if (lastUpdate.updateBlockNumber == uint32(block.number)) {
lastUpdate.stake = newStake;
} else {
lastUpdate.nextUpdateBlockNumber = uint32(block.number);
operatorStakeHistory[operatorId][quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: newStake
})
);
}
}
// Log update and return stake delta
emit OperatorStakeUpdate(operatorId, quorumNumber, newStake);
return _calculateDelta({prev: prevStake, cur: newStake});
}
/// @notice Applies a delta to the total stake recorded for `quorumNumber`
/// @return Returns the new total stake for the quorum
function _recordTotalStakeUpdate(
uint8 quorumNumber,
int256 stakeDelta
) internal returns (uint96) {
// Get our last-recorded stake update
uint256 historyLength = _totalStakeHistory[quorumNumber].length;
StakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1];
// Return early if no update is needed
if (stakeDelta == 0) {
return lastStakeUpdate.stake;
}
// Calculate the new total stake by applying the delta to our previous stake
uint96 newStake = _applyDelta(lastStakeUpdate.stake, stakeDelta);
/**
* If our last stake entry was made in the current block, update the entry
* Otherwise, push a new entry and update the previous entry's "next" field
*/
if (lastStakeUpdate.updateBlockNumber == uint32(block.number)) {
lastStakeUpdate.stake = newStake;
} else {
lastStakeUpdate.nextUpdateBlockNumber = uint32(block.number);
_totalStakeHistory[quorumNumber].push(
StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: newStake
})
);
}
return newStake;
}
/**
* @notice Adds `strategyParams` to the `quorumNumber`-th quorum.
* @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies).
* @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a conscious choice,
* since a middleware may want, e.g., a stablecoin quorum that accepts USDC, USDT, DAI, etc. as underlying assets and trades them as "equivalent".
*/
function _addStrategyParams(
uint8 quorumNumber,
StrategyParams[] memory _strategyParams
) internal {
require(_strategyParams.length > 0, InputArrayLengthZero());
uint256 numStratsToAdd = _strategyParams.length;
uint256 numStratsExisting = strategyParams[quorumNumber].length;
require(
numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH,
InputArrayLengthMismatch()
);
for (uint256 i = 0; i < numStratsToAdd; i++) {
// fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times
for (uint256 j = 0; j < (numStratsExisting + i); j++) {
require(
strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy,
InputDuplicateStrategy()
);
}
require(_strategyParams[i].multiplier > 0, InputMultiplierZero());
strategyParams[quorumNumber].push(_strategyParams[i]);
strategiesPerQuorum[quorumNumber].push(_strategyParams[i].strategy);
emit StrategyAddedToQuorum(quorumNumber, _strategyParams[i].strategy);
emit StrategyMultiplierUpdated(
quorumNumber, _strategyParams[i].strategy, _strategyParams[i].multiplier
);
}
}
/// @notice Returns the change between a previous and current value as a signed int
function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) {
return int256(uint256(cur)) - int256(uint256(prev));
}
/// @notice Adds or subtracts delta from value, according to its sign
function _applyDelta(uint96 value, int256 delta) internal pure returns (uint96) {
if (delta < 0) {
return value - uint96(uint256(-delta));
} else {
return value + uint96(uint256(delta));
}
}
/// @notice Checks that the `stakeUpdate` was valid at the given `blockNumber`
function _validateStakeUpdateAtBlockNumber(
StakeUpdate memory stakeUpdate,
uint32 blockNumber
) internal pure {
/**
* Check that the update is valid for the given blockNumber:
* - blockNumber should be >= the update block number
* - the next update block number should be either 0 or strictly greater than blockNumber
*/
require(blockNumber >= stakeUpdate.updateBlockNumber, InvalidBlockNumber());
require(
stakeUpdate.nextUpdateBlockNumber == 0
|| blockNumber < stakeUpdate.nextUpdateBlockNumber,
InvalidBlockNumber()
);
}
/// Returns total Slashable stake for a list of operators per strategy that can have the weights applied based on strategy multipliers
function _getSlashableStakePerStrategy(
uint8 quorumNumber,
address[] memory operators
) internal view returns (uint256[][] memory) {
uint32 beforeBlock = uint32(block.number + slashableStakeLookAheadPerQuorum[quorumNumber]);
uint256[][] memory slashableShares = allocationManager.getMinimumSlashableStake(
OperatorSet(registryCoordinator.avs(), quorumNumber),
operators,
strategiesPerQuorum[quorumNumber],
beforeBlock
);
return slashableShares;
}
/**
* @notice This function computes the total weight of the @param operators in the quorum @param quorumNumber.
* @dev this method DOES NOT check that the quorum exists
* @return `uint96[] memory` The weighted sum of the operators' shares across each strategy considered by the quorum
* @return `bool[] memory` True if the respective operator meets the quorum's minimum stake
*/
function _weightOfOperatorsForQuorum(
uint8 quorumNumber,
address[] memory operators
) internal view virtual returns (uint96[] memory, bool[] memory) {
uint96[] memory weights = new uint96[](operators.length);
bool[] memory hasMinimumStakes = new bool[](operators.length);
uint256 stratsLength = strategyParamsLength(quorumNumber);
StrategyParams[] memory stratsAndMultipliers = strategyParams[quorumNumber];
uint256[][] memory strategyShares;
if (stakeTypePerQuorum[quorumNumber] == IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE) {
// get slashable stake for the operators from AllocationManager
strategyShares = _getSlashableStakePerStrategy(quorumNumber, operators);
} else {
// get delegated stake for the operators from DelegationManager
strategyShares =
delegation.getOperatorsShares(operators, strategiesPerQuorum[quorumNumber]);
}
// Calculate weight of each operator and whether they contain minimum stake for the quorum
for (uint256 opIndex = 0; opIndex < operators.length; opIndex++) {
// 1. For the given operator, loop through the strategies and calculate the operator's
// weight for the quorum
for (uint256 stratIndex = 0; stratIndex < stratsLength; stratIndex++) {
// get multiplier for strategy
StrategyParams memory strategyAndMultiplier = stratsAndMultipliers[stratIndex];
// calculate added weight for strategy and multiplier
if (strategyShares[opIndex][stratIndex] > 0) {
weights[opIndex] += uint96(
strategyShares[opIndex][stratIndex] * strategyAndMultiplier.multiplier
/ WEIGHTING_DIVISOR
);
}
}
// 2. Check whether operator is above minimum stake threshold
hasMinimumStakes[opIndex] = weights[opIndex] >= minimumStakeForQuorum[quorumNumber];
}
return (weights, hasMinimumStakes);
}
function _weightOfOperatorForQuorum(
uint8 quorumNumber,
address operator
) internal view virtual returns (uint96, bool) {
address[] memory operators = new address[](1);
operators[0] = operator;
(uint96[] memory weights, bool[] memory hasMinimumStakes) =
_weightOfOperatorsForQuorum(quorumNumber, operators);
return (weights[0], hasMinimumStakes[0]);
}
/// @notice Returns `true` if the quorum has been initialized
function _quorumExists(
uint8 quorumNumber
) internal view returns (bool) {
return _totalStakeHistory[quorumNumber].length != 0;
}
/**
*
* VIEW FUNCTIONS
*
*/
/// @inheritdoc IStakeRegistry
function weightOfOperatorForQuorum(
uint8 quorumNumber,
address operator
) public view virtual quorumExists(quorumNumber) returns (uint96) {
(uint96 stake,) = _weightOfOperatorForQuorum(quorumNumber, operator);
return stake;
}
/// @inheritdoc IStakeRegistry
function strategyParamsLength(
uint8 quorumNumber
) public view returns (uint256) {
return strategyParams[quorumNumber].length;
}
/// @inheritdoc IStakeRegistry
function strategyParamsByIndex(
uint8 quorumNumber,
uint256 index
) public view returns (StrategyParams memory) {
return strategyParams[quorumNumber][index];
}
/**
*
* VIEW FUNCTIONS - Operator Stake History
*
*/
/// @inheritdoc IStakeRegistry
function getStakeHistoryLength(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (uint256) {
return operatorStakeHistory[operatorId][quorumNumber].length;
}
/// @inheritdoc IStakeRegistry
function getStakeHistory(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (StakeUpdate[] memory) {
return operatorStakeHistory[operatorId][quorumNumber];
}
/// @inheritdoc IStakeRegistry
function getCurrentStake(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (uint96) {
StakeUpdate memory operatorStakeUpdate = getLatestStakeUpdate(operatorId, quorumNumber);
return operatorStakeUpdate.stake;
}
/// @inheritdoc IStakeRegistry
function getLatestStakeUpdate(
bytes32 operatorId,
uint8 quorumNumber
) public view returns (StakeUpdate memory) {
uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length;
StakeUpdate memory operatorStakeUpdate;
if (historyLength == 0) {
return operatorStakeUpdate;
} else {
operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength - 1];
return operatorStakeUpdate;
}
}
/// @inheritdoc IStakeRegistry
function getStakeUpdateAtIndex(
uint8 quorumNumber,
bytes32 operatorId,
uint256 index
) external view returns (StakeUpdate memory) {
return operatorStakeHistory[operatorId][quorumNumber][index];
}
/// @inheritdoc IStakeRegistry
function getStakeAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint96) {
return operatorStakeHistory[operatorId][quorumNumber][_getStakeUpdateIndexForOperatorAtBlockNumber(
operatorId, quorumNumber, blockNumber
)].stake;
}
/// @inheritdoc IStakeRegistry
function getStakeUpdateIndexAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint32) {
return _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber);
}
/// @inheritdoc IStakeRegistry
function getStakeAtBlockNumberAndIndex(
uint8 quorumNumber,
uint32 blockNumber,
bytes32 operatorId,
uint256 index
) external view returns (uint96) {
StakeUpdate memory operatorStakeUpdate =
operatorStakeHistory[operatorId][quorumNumber][index];
_validateStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber);
return operatorStakeUpdate.stake;
}
/**
*
* VIEW FUNCTIONS - Total Stake History
*
*/
/// @inheritdoc IStakeRegistry
function getTotalStakeHistoryLength(
uint8 quorumNumber
) external view returns (uint256) {
return _totalStakeHistory[quorumNumber].length;
}
/// @inheritdoc IStakeRegistry
function getCurrentTotalStake(
uint8 quorumNumber
) external view returns (uint96) {
return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake;
}
/// @inheritdoc IStakeRegistry
function getTotalStakeUpdateAtIndex(
uint8 quorumNumber,
uint256 index
) external view returns (StakeUpdate memory) {
return _totalStakeHistory[quorumNumber][index];
}
/// @inheritdoc IStakeRegistry
function getTotalStakeAtBlockNumberFromIndex(
uint8 quorumNumber,
uint32 blockNumber,
uint256 index
) external view returns (uint96) {
StakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index];
_validateStakeUpdateAtBlockNumber(totalStakeUpdate, blockNumber);
return totalStakeUpdate.stake;
}
/// @inheritdoc IStakeRegistry
function getTotalStakeIndicesAtBlockNumber(
uint32 blockNumber,
bytes calldata quorumNumbers
) external view returns (uint32[] memory) {
uint32[] memory indices = new uint32[](quorumNumbers.length);
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
_checkQuorumExists(quorumNumber);
require(
_totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber,
EmptyStakeHistory()
);
uint256 length = _totalStakeHistory[quorumNumber].length;
for (uint256 j = 0; j < length; j++) {
if (
_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber
<= blockNumber
) {
indices[i] = uint32(length - j - 1);
break;
}
}
}
return indices;
}
/**
* @notice Sets the stake type for the registry for a specific quorum
* @param quorumNumber The quorum number to set the stake type for
* @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH)
*/
function _setStakeType(uint8 quorumNumber, IStakeRegistryTypes.StakeType _stakeType) internal {
stakeTypePerQuorum[quorumNumber] = _stakeType;
emit StakeTypeSet(_stakeType);
}
/**
* @notice Sets the look ahead time for checking operator shares for a specific quorum
* @param quorumNumber The quorum number to set the look ahead period for
* @param _lookAheadBlocks The number of blocks to look ahead when checking shares
*/
function _setLookAheadPeriod(uint8 quorumNumber, uint32 _lookAheadBlocks) internal {
require(
stakeTypePerQuorum[quorumNumber] == IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE,
QuorumNotSlashable()
);
uint32 oldLookAheadDays = slashableStakeLookAheadPerQuorum[quorumNumber];
slashableStakeLookAheadPerQuorum[quorumNumber] = _lookAheadBlocks;
emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadBlocks);
}
function _checkSlashingRegistryCoordinator() internal view {
require(msg.sender == address(registryCoordinator), OnlySlashingRegistryCoordinator());
}
function _checkSlashingRegistryCoordinatorOwner() internal view {
require(
msg.sender == Ownable(address(registryCoordinator)).owner(),
OnlySlashingRegistryCoordinatorOwner()
);
}
function _checkQuorumExists(
uint8 quorumNumber
) internal view {
require(_quorumExists(quorumNumber), QuorumDoesNotExist());
}
}
"
},
"lib/eigenlayer-middleware/lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"lib/eigenlayer-middleware/lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategy.sol";
import "./IPauserRegistry.sol";
import "./ISignatureUtilsMixin.sol";
import "../libraries/SlashingLib.sol";
interface IDelegationManagerErrors {
/// @dev Thrown when caller is neither the StrategyManager or EigenPodManager contract.
error OnlyStrategyManagerOrEigenPodManager();
/// @dev Thrown when msg.sender is not the EigenPodManager
error OnlyEigenPodManager();
/// @dev Throw when msg.sender is not the AllocationManager
error OnlyAllocationManager();
/// Delegation Status
/// @dev Thrown when an operator attempts to undelegate.
error OperatorsCannotUndelegate();
/// @dev Thrown when an account is actively delegated.
error ActivelyDelegated();
/// @dev Thrown when an account is not actively delegated.
error NotActivelyDelegated();
/// @dev Thrown when `operator` is not a registered operator.
error OperatorNotRegistered();
/// Invalid Inputs
/// @dev Thrown when attempting to execute an action that was not queued.
error WithdrawalNotQueued();
/// @dev Thrown when caller cannot undelegate on behalf of a staker.
error CallerCannotUndelegate();
/// @dev Thrown when two array parameters have mismatching lengths.
error InputArrayLengthMismatch();
/// @dev Thrown when input arrays length is zero.
error InputArrayLengthZero();
/// Slashing
/// @dev Thrown when an operator has been fully slashed(maxMagnitude is 0) for a strategy.
/// or if the staker has had been natively slashed to the point of their beaconChainScalingFactor equalling 0.
error FullySlashed();
/// Signatures
/// @dev Thrown when attempting to spend a spent eip-712 salt.
error SaltSpent();
/// Withdrawal Processing
/// @dev Thrown when attempting to withdraw before delay has elapsed.
error WithdrawalDelayNotElapsed();
/// @dev Thrown when withdrawer is not the current caller.
error WithdrawerNotCaller();
}
interface IDelegationManagerTypes {
// @notice Struct used for storing information about a single operator who has registered with EigenLayer
struct OperatorDetails {
/// @notice DEPRECATED -- this field is no longer used, payments are handled in RewardsCoordinator.sol
address __deprecated_earningsReceiver;
/**
* @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations".
* @dev Signature verification follows these rules:
* 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed.
* 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator.
* 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
*/
address delegationApprover;
/// @notice DEPRECATED -- this field is no longer used. An analogous field is the `allocationDelay` stored in the AllocationManager
uint32 __deprecated_stakerOptOutWindowBlocks;
}
/**
* @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator.
* @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function.
*/
struct DelegationApproval {
// the staker who is delegating
address staker;
// the operator being delegated to
address operator;
// the operator's provided salt
bytes32 salt;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* @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;
IStrategy[] strategies;
uint256[] scaledShares;
}
/**
* @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 {
IStrategy[] strategies;
uint256[] depositShares;
address __deprecated_withdrawer;
}
}
interface IDelegationManagerEvents is IDelegationManagerTypes {
// @notice Emitted when a new operator registers in EigenLayer and provides their delegation approver.
event OperatorRegistered(address indexed operator, address delegationApprover);
/// @notice Emitted when an operator updates their delegation approver
event DelegationApproverUpdated(address indexed operator, address newDelegationApprover);
/**
* @notice Emitted when @param operator indicates that they are updating their MetadataURI string
* @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
*/
event OperatorMetadataURIUpdated(address indexed operator, string metadataURI);
/// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares.
event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);
/// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares.
event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);
/// @notice Emitted when @param staker delegates to @param operator.
event StakerDelegated(address indexed staker, address indexed operator);
/// @notice Emitted when @param staker undelegates from @param operator.
event StakerUndelegated(address indexed staker, address indexed operator);
/// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself
event StakerForceUndelegated(address indexed staker, address indexed operator);
/// @notice Emitted when a staker's depositScalingFactor is updated
event DepositScalingFactorUpdated(address staker, IStrategy strategy, uint256 newDepositScalingFactor);
/**
* @notice Emitted when a new withdrawal is queued.
* @param withdrawalRoot Is the hash of the `withdrawal`.
* @param withdrawal Is the withdrawal itself.
* @param sharesToWithdraw Is an array of the expected shares that were queued for withdrawal corresponding to the strategies in the `withdrawal`.
*/
event SlashingWithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal, uint256[] sharesToWithdraw);
/// @notice Emitted when a queued withdrawal is completed
event SlashingWithdrawalCompleted(bytes32 withdrawalRoot);
/// @notice Emitted whenever an operator's shares are slashed for a given strategy
event OperatorSharesSlashed(address indexed operator, IStrategy strategy, uint256 totalSlashedShares);
}
/**
* @title DelegationManager
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice This is the contract for delegation in EigenLayer. The main functionalities of this contract are
* - enabling anyone to register as an operator in EigenLayer
* - allowing operators to specify parameters related to stakers who delegate to them
* - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
* - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
*/
interface IDelegationManager is ISignatureUtilsMixin, IDelegationManagerErrors, IDelegationManagerEvents {
/**
* @dev Initializes the initial owner and paused status.
*/
function initialize(address initialOwner, uint256 initialPausedStatus) external;
/**
* @notice Registers the caller as an operator in EigenLayer.
* @param initDelegationApprover is an address that, if set, must provide a signature when stakers delegate
* to an operator.
* @param allocationDelay The delay before allocations take effect.
* @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
*
* @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
* @dev This function will revert if the caller is already delegated to an operator.
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function registerAsOperator(
address initDelegationApprover,
uint32 allocationDelay,
string calldata metadataURI
) external;
/**
* @notice Updates an operator's stored `delegationApprover`.
* @param operator is the operator to update the delegationApprover for
* @param newDelegationApprover is the new delegationApprover for the operator
*
* @dev The caller must have previously registered as an operator in EigenLayer.
*/
function modifyOperatorDetails(address operator, address newDelegationApprover) external;
/**
* @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
* @param operator The operator to update metadata for
* @param metadataURI The URI for metadata associated with an operator
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function updateOperatorMetadataURI(address operator, string calldata metadataURI) external;
/**
* @notice Caller delegates their stake to an operator.
* @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer.
* @param approverSignatureAndExpiry (optional) Verifies the operator approves of this delegation
* @param approverSalt (optional) A unique single use value tied to an individual signature.
* @dev The signature/salt are used ONLY if the operator has configured a delegationApprover.
* If they have not, these params can be left empty.
*/
function delegateTo(
address operator,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external;
/**
* @notice Undelegates the staker from their operator and queues a withdrawal for all of their shares
* @param staker The account to be undelegated
* @return withdrawalRoots The roots of the newly queued withdrawals, if a withdrawal was queued. Returns
* an empty array if none was queued.
*
* @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves.
* @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover"
* @dev Reverts if the `staker` is not delegated to an operator
*/
function undelegate(
address staker
) external returns (bytes32[] memory withdrawalRoots);
/**
* @notice Undelegates the staker from their current operator, and redelegates to `newOperator`
* Queues a withdrawal for all of the staker's withdrawable shares. These shares will only be
* delegated to `newOperator` AFTER the withdrawal is completed.
* @dev This method acts like a call to `undelegate`, then `delegateTo`
* @param newOperator the new operator that will be delegated all assets
* @dev NOTE: the following 2 params are ONLY checked if `newOperator` has a `delegationApprover`.
* If not, they can be left empty.
* @param newOperatorApproverSig A signature from the operator's `delegationApprover`
* @param approverSalt A unique single use value tied to the approver's signature
*/
function redelegate(
address newOperator,
SignatureWithExpiry memory newOperatorApproverSig,
bytes32 approverSalt
) external returns (bytes32[] memory withdrawalRoots);
/**
* @notice Allows a staker to queue a withdrawal of their deposit shares. The withdrawal can be
* completed after the MIN_WITHDRAWAL_DELAY_BLOCKS via either of the completeQueuedWithdrawal methods.
*
* While in the queue, these shares are removed from the staker's balance, as well as from their operator's
* delegated share balance (if applicable). Note that while in the queue, deposit shares are still subject
* to slashing. If any slashing has occurred, the shares received may be less than the queued deposit shares.
*
* @dev To view all the staker's strategies/deposit shares that can be queued for withdrawal, see `getDepositedShares`
* @dev To view the current conversion between a staker's deposit shares and withdrawable shares, see `getWithdrawableShares`
*/
function queueWithdrawals(
QueuedWithdrawalParams[] calldata params
) external returns (bytes32[] memory);
/**
* @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 calldata withdrawal,
IERC20[] calldata tokens,
bool receiveAsTokens
) external;
/**
* @notice Used to complete multiple queued withdrawals
* @param withdrawals Array of Withdrawals to complete. See `completeQueuedWithdrawal` for the usage of a single Withdrawal.
* @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
* @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
* @dev See `completeQueuedWithdrawal` for relevant dev tags
*/
function completeQueuedWithdrawals(
Withdrawal[] calldata withdrawals,
IERC20[][] calldata tokens,
bool[] calldata receiveAsTokens
) external;
/**
* @notice Called by a share manager when a staker's deposit share balance in a strategy increases.
* This method delegates any new shares to an operator (if applicable), and updates the staker's
* deposit scaling factor regardless.
* @param staker The address whose deposit shares have increased
* @param strategy The strategy in which shares have been deposited
* @param prevDepositShares The number of deposit shares the staker had in the strategy prior to the increase
* @param addedShares The number of deposit shares added by the staker
*
* @dev Note that if the either the staker's current operator has been slashed 100% for `strategy`, OR the
* staker has been slashed 100% on the beacon chain such that the calculated slashing factor is 0, this
* method WILL REVERT.
*/
function increaseDelegatedShares(
address staker,
IStrategy strategy,
uint256 prevDepositShares,
uint256 addedShares
) external;
/**
* @notice If the staker is delegated, decreases its operator's shares in response to
* a decrease in balance in the beaconChainETHStrategy
* @param staker the staker whose operator's balance will be decreased
* @param curDepositShares the current deposit shares held by the staker
* @param beaconChainSlashingFactorDecrease the amount that the staker's beaconChainSlashingFactor has decreased by
* @dev Note: `beaconChainSlashingFactorDecrease` are assumed to ALWAYS be < 1 WAD.
* These invariants are maintained in the EigenPodManager.
*/
function decreaseDelegatedShares(
address staker,
uint256 curDepositShares,
uint64 beaconChainSlashingFactorDecrease
) external;
/**
* @notice Decreases the operators shares in storage after a slash and increases the burnable shares by calling
* into either the StrategyManager or EigenPodManager (if the strategy is beaconChainETH).
* @param operator The operator to decrease shares for
* @param strategy The strategy to decrease shares for
* @param prevMaxMagnitude the previous maxMagnitude of the operator
* @param newMaxMagnitude the new maxMagnitude of the operator
* @dev Callable only by the AllocationManager
* @dev Note: Assumes `prevMaxMagnitude <= newMaxMagnitude`. This invariant is maintained in
* the AllocationManager.
*/
function slashOperatorShares(
address operator,
IStrategy strategy,
uint64 prevMaxMagnitude,
uint64 newMaxMagnitude
) external;
/**
*
* VIEW FUNCTIONS
*
*/
/**
* @notice returns the address of the operator that `staker` is delegated to.
* @notice Mapping: staker => operator whom the staker is currently delegated to.
* @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
*/
function delegatedTo(
address staker
) external view returns (address);
/**
* @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
* @dev Salts are used in the `delegateTo` function. Note that this function only processes the delegationApprover's
* signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
*/
function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool);
/// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
/// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
function cumulativeWithdrawalsQueued(
address staker
) external view returns (uint256);
/**
* @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
*/
function isDelegated(
address staker
) external view returns (bool);
/**
* @notice Returns true is an operator has previously registered for delegation.
*/
function isOperator(
address operator
) external view returns (bool);
/**
* @notice Returns the delegationApprover account for an operator
*/
function delegationApprover(
address operator
) external view returns (address);
/**
* @notice Returns the shares that an operator has delegated to them in a set of strategies
* @param operator the operator to get shares for
* @param strategies the strategies to get shares for
*/
function getOperatorShares(
address operator,
IStrategy[] memory strategies
) external view returns (uint256[] memory);
/**
* @notice Returns the shares that a set of operators have delegated to them in a set of strategies
* @param operators the operators to get shares for
* @param strategies the strategies to get shares for
*/
function getOperatorsShares(
address[] memory operators,
IStrategy[] memory strategies
) external view returns (uint256[][] memory);
/**
* @notice Returns amount of withdrawable shares from an operator for a strategy that is still in the queue
* and therefore slashable. Note that the *actual* slashable amount could be less than this value as this doesn't account
* for amounts that have already been slashed. This assumes that none of the shares have been slashed.
* @param operator the operator to get shares for
* @param strategy the strategy to get shares for
* @return the amount of shares that are slashable in the withdrawal queue for an operator and a strategy
*/
function getSlashableSharesInQueue(address operator, IStrategy strategy) external view returns (uint256);
/**
* @notice Given a staker and a set of strategies, return the shares they can queue for withdrawal and the
* corresponding depositShares.
* This value depends on which operator the staker is delegated to.
* The shares amount returned is the actual amount of Strategy shares the staker would receive (subject
* to each strategy's underlying shares to token ratio).
*/
function getWithdrawableShares(
address staker,
IStrategy[] memory strategies
) external view returns (uint256[] memory withdrawableShares, uint256[] memory depositShares);
/**
* @notice Returns the number of shares in storage for a staker and all their strategies
*/
function getDepositedShares(
address staker
) external view returns (IStrategy[] memory, uint256[] memory);
/**
* @notice Returns the scaling factor applied to a staker's deposits for a given strategy
*/
function depositScalingFactor(address staker, IStrategy strategy) external view returns (uint256);
/**
* @notice Returns the Withdrawal and corresponding shares associated with a `withdrawalRoot`
* @param withdrawalRoot The hash identifying the queued withdrawal
* @return withdrawal The withdrawal details
* @return shares Array of shares corresponding to each strategy in the withdrawal
* @dev The shares are what a user would receive from completing a queued withdrawal, assuming all slashings are applied
* @dev Withdrawals queued before the slashing release cannot be queried with this method
*/
function getQueuedWithdrawal(
bytes32 withdrawalRoot
) external view returns (Withdrawal memory withdrawal, uint256[] memory shares);
/**
* @notice Returns all queued withdrawals and their corresponding shares for a staker.
* @param staker The address of the staker to query withdrawals for.
* @return withdrawals Array of Withdrawal structs containing details about each queued withdrawal.
* @return shares 2D array of shares, where each inner array corresponds to the strategies in the withdrawal.
* @dev The shares are what a user would receive from completing a queued withdrawal, assuming all slashings are applied.
*/
function getQueuedWithdrawals(
address staker
) external view returns (Withdrawal[] memory withdrawals, uint256[][] memory shares);
/// @notice Returns a list of queued withdrawal roots for the `staker`.
/// NOTE that this only returns withdrawals queued AFTER the slashing release.
function getQueuedWithdrawalRoots(
address staker
) external view returns (bytes32[] memory);
/**
* @notice Converts shares for a set of strategies to deposit shares, likely in order to input into `queueWithdrawals`.
* This function will revert from a division by 0 error if any of the staker's strategies have a slashing factor of 0.
* @param staker the staker to convert shares for
* @param strategies the strategies to convert shares for
* @param withdrawableShares the shares to convert
* @return the deposit shares
* @dev will be a few wei off due to rounding errors
*/
function convertToDepositShares(
address staker,
IStrategy[] memory strategies,
uint256[] memory withdrawableShares
) external view returns (uint256[] memory);
/// @notice Returns the keccak256 hash of `withdrawal`.
function calculateWithdrawalRoot(
Withdrawal memory withdrawal
) external pure returns (bytes32);
/**
* @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` function.
* @param staker The account delegating their stake
* @param operator The account receiving delegated stake
* @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
* @param approverSalt A unique and single use value associated with the approver signature.
* @param expiry Time after which the approver's signature becomes invalid
*/
function calculateDelegationApprovalDigestHash(
address staker,
address operator,
address _delegationApprover,
bytes32 approverSalt,
uint256 expiry
) external view returns (bytes32);
/// @notice return address of the beaconChainETHStrategy
function beaconChainETHStrategy() external view returns (IStrategy);
/**
* @notice Returns the minimum withdrawal delay in blocks to pass for withdrawals queued to be completable.
* Also applies to legacy withdrawals so any withdrawals not completed prior to the slashing upgrade will be subject
* to this longer delay.
* @dev Backwards-compatible interface to return the internal `MIN_WITHDRAWAL_DELAY_BLOCKS` value
* @dev Previous value in storage was deprecated. See `__deprecated_minWithdrawalDelayBlocks`
*/
function minWithdrawalDelayBlocks() external view returns (uint32);
/// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
}
"
},
"lib/eigenlayer-middleware/lib/eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./ISignatureUtilsMixin.sol";
import "./IPauserRegistry.sol";
import "./IStrategy.sol";
interface IAVSDirectoryErrors {
/// Operator Status
/// @dev Thrown when an operator does not exist in the DelegationManager
error OperatorNotRegisteredToEigenLayer();
/// @dev Thrown when an operator is already registered
Submitted on: 2025-10-14 17:34:24
Comments
Log in to comment.
No comments yet.