Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Advanced mode: Intended for advanced users or developers and will display all Internal Transactions including zero value transfers.
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
|||
|---|---|---|---|---|---|---|---|---|
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Total Stake ... | 24770627 | 21 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Total Stake ... | 24764052 | 43 hrs ago | 0 ETH | |||||
| Get Stake At Blo... | 24764048 | 43 hrs ago | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
StakeRegistry
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} 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 onlyRegistryCoordinator() {
require(
msg.sender == address(registryCoordinator),
"StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"
);
_;
}
modifier onlyCoordinatorOwner() {
require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator");
_;
}
modifier quorumExists(uint8 quorumNumber) {
require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist");
_;
}
constructor(
IRegistryCoordinator _registryCoordinator,
IDelegationManager _delegationManager
) StakeRegistryStorage(_registryCoordinator, _delegationManager) {}
/*******************************************************************************
EXTERNAL FUNCTIONS - REGISTRY COORDINATOR
*******************************************************************************/
/**
* @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`.
* @param operator The address of the operator to register.
* @param operatorId The id of the operator to register.
* @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber.
* @return The operator's current stake for each quorum, and the total stake for each quorum
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlyRegistryCoordinator 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]);
require(_quorumExists(quorumNumber), "StakeRegistry.registerOperator: quorum does not exist");
// 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,
"StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum"
);
// 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);
}
/**
* @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`.
* @param operatorId The id of the operator to deregister.
* @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(
bytes32 operatorId,
bytes calldata quorumNumbers
) public virtual onlyRegistryCoordinator {
/**
* 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]);
require(_quorumExists(quorumNumber), "StakeRegistry.deregisterOperator: quorum does not exist");
// 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);
}
}
/**
* @notice Called by the registry coordinator to update an operator's stake for one
* or more quorums.
*
* If the operator no longer has the minimum stake required for a quorum, they are
* added to the `quorumsToRemove`, which is returned to the registry coordinator
* @return A bitmap of quorums where the operator no longer meets the minimum stake
* and should be deregistered.
*/
function updateOperatorStake(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) external onlyRegistryCoordinator returns (uint192) {
uint192 quorumsToRemove;
/**
* 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.
*/
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);
require(_quorumExists(quorumNumber), "StakeRegistry.updateOperatorStake: quorum does not exist");
// Fetch the operator's current stake, applying weighting parameters and checking
// against the minimum stake requirements for the quorum.
(uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator);
// If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal
if (!hasMinimumStake) {
stakeWeight = 0;
quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber));
}
// Update the operator's stake and retrieve the delta
// If we're deregistering them, their weight is set to 0
int256 stakeDelta = _recordOperatorStakeUpdate({
operatorId: operatorId,
quorumNumber: quorumNumber,
newStake: stakeWeight
});
// Apply the delta to the quorum's total stake
_recordTotalStakeUpdate(quorumNumber, stakeDelta);
}
return quorumsToRemove;
}
/// @notice Initialize a new quorum and push its first history update
function initializeQuorum(
uint8 quorumNumber,
uint96 minimumStake,
StrategyParams[] memory _strategyParams
) public virtual onlyRegistryCoordinator {
require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists");
_addStrategyParams(quorumNumber, _strategyParams);
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
_totalStakeHistory[quorumNumber].push(StakeUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
stake: 0
}));
}
function setMinimumStakeForQuorum(
uint8 quorumNumber,
uint96 minimumStake
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
_setMinimumStakeForQuorum(quorumNumber, minimumStake);
}
/**
* @notice Adds strategies and weights to the 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 concious 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 addStrategies(
uint8 quorumNumber,
StrategyParams[] memory _strategyParams
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
_addStrategyParams(quorumNumber, _strategyParams);
}
/**
* @notice Remove strategies and their associated weights from the quorum's considered strategies
* @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise
* the removal of lower index entries will cause a shift in the indices of the other strategies to remove
*/
function removeStrategies(
uint8 quorumNumber,
uint256[] memory indicesToRemove
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
uint256 toRemoveLength = indicesToRemove.length;
require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided");
StrategyParams[] storage _strategyParams = strategyParams[quorumNumber];
IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber];
for (uint256 i = 0; i < toRemoveLength; i++) {
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();
}
}
/**
* @notice Modifies the weights of existing strategies for a specific quorum
* @param quorumNumber is the quorum number to which the strategies belong
* @param strategyIndices are the indices of the strategies to change
* @param newMultipliers are the new multipliers for the strategies
*/
function modifyStrategyParams(
uint8 quorumNumber,
uint256[] calldata strategyIndices,
uint96[] calldata newMultipliers
) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) {
uint256 numStrats = strategyIndices.length;
require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided");
require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch");
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, "StakeRegistry._addStrategyParams: no strategies provided");
uint256 numStratsToAdd = _strategyParams.length;
uint256 numStratsExisting = strategyParams[quorumNumber].length;
require(
numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH,
"StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH"
);
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,
"StakeRegistry._addStrategyParams: cannot add same strategy 2x"
);
}
require(
_strategyParams[i].multiplier > 0,
"StakeRegistry._addStrategyParams: cannot add strategy with zero weight"
);
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,
"StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber"
);
require(
stakeUpdate.nextUpdateBlockNumber == 0 || blockNumber < stakeUpdate.nextUpdateBlockNumber,
"StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber"
);
}
/**
* @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber.
* @dev this method DOES NOT check that the quorum exists
* @return `uint96` The weighted sum of the operator's shares across each strategy considered by the quorum
* @return `bool` True if the operator meets the quorum's minimum stake
*/
function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal virtual view returns (uint96, bool) {
uint96 weight;
uint256 stratsLength = strategyParamsLength(quorumNumber);
StrategyParams memory strategyAndMultiplier;
uint256[] memory strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]);
for (uint256 i = 0; i < stratsLength; i++) {
// accessing i^th StrategyParams struct for the quorumNumber
strategyAndMultiplier = strategyParams[quorumNumber][i];
// add the weight from the shares for this strategy to the total weight
if (strategyShares[i] > 0) {
weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR);
}
}
// Return the weight, and `true` if the operator meets the quorum's minimum stake
bool hasMinimumStake = weight >= minimumStakeForQuorum[quorumNumber];
return (weight, hasMinimumStake);
}
/// @notice Returns `true` if the quorum has been initialized
function _quorumExists(uint8 quorumNumber) internal view returns (bool) {
return _totalStakeHistory[quorumNumber].length != 0;
}
/*******************************************************************************
VIEW FUNCTIONS
*******************************************************************************/
/**
* @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber.
* @dev reverts if the quorum does not exist
*/
function weightOfOperatorForQuorum(
uint8 quorumNumber,
address operator
) public virtual view quorumExists(quorumNumber) returns (uint96) {
(uint96 stake, ) = _weightOfOperatorForQuorum(quorumNumber, operator);
return stake;
}
/// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`.
function strategyParamsLength(uint8 quorumNumber) public view returns (uint256) {
return strategyParams[quorumNumber].length;
}
/// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber`
function strategyParamsByIndex(
uint8 quorumNumber,
uint256 index
) public view returns (StrategyParams memory)
{
return strategyParams[quorumNumber][index];
}
/*******************************************************************************
VIEW FUNCTIONS - Operator Stake History
*******************************************************************************/
/**
* @notice Returns the length of an operator's stake history for the given quorum
*/
function getStakeHistoryLength(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (uint256) {
return operatorStakeHistory[operatorId][quorumNumber].length;
}
/**
* @notice Returns the entire `operatorStakeHistory[operatorId][quorumNumber]` array.
* @param operatorId The id of the operator of interest.
* @param quorumNumber The quorum number to get the stake for.
*/
function getStakeHistory(
bytes32 operatorId,
uint8 quorumNumber
) external view returns (StakeUpdate[] memory) {
return operatorStakeHistory[operatorId][quorumNumber];
}
/**
* @notice Returns the most recent stake weight for the `operatorId` for quorum `quorumNumber`
* @dev Function returns weight of **0** in the event that the operator has no stake history
*/
function getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) {
StakeUpdate memory operatorStakeUpdate = getLatestStakeUpdate(operatorId, quorumNumber);
return operatorStakeUpdate.stake;
}
/**
* @notice Returns the most recent stake weight for the `operatorId` for a certain quorum
* @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history
*/
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;
}
}
/**
* @notice Returns the `index`-th entry in the `operatorStakeHistory[operatorId][quorumNumber]` array.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorStakeHistory[operatorId][quorumNumber]`.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getStakeUpdateAtIndex(
uint8 quorumNumber,
bytes32 operatorId,
uint256 index
) external view returns (StakeUpdate memory) {
return operatorStakeHistory[operatorId][quorumNumber][index];
}
/// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber`
function getStakeAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint96) {
return
operatorStakeHistory[operatorId][quorumNumber][
_getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber)
].stake;
}
/// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber`
function getStakeUpdateIndexAtBlockNumber(
bytes32 operatorId,
uint8 quorumNumber,
uint32 blockNumber
) external view returns (uint32) {
return _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber);
}
/**
* @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the
* `index`-th entry in the `operatorStakeHistory[operatorId][quorumNumber]` array if it was the operator's
* stake at `blockNumber`. Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorStakeHistory[operatorId][quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
*/
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
*******************************************************************************/
/**
* @notice Returns the length of the total stake history for the given quorum
*/
function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) {
return _totalStakeHistory[quorumNumber].length;
}
/**
* @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`.
* @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty.
*/
function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96) {
return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake;
}
/**
* @notice Returns the `index`-th entry in the dynamic array of total stake, `_totalStakeHistory` for quorum `quorumNumber`.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`.
*/
function getTotalStakeUpdateAtIndex(
uint8 quorumNumber,
uint256 index
) external view returns (StakeUpdate memory) {
return _totalStakeHistory[quorumNumber][index];
}
/**
* @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the
* `_totalStakeHistory[quorumNumber]` array if it was the stake at `blockNumber`. Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getTotalStakeAtBlockNumberFromIndex(
uint8 quorumNumber,
uint32 blockNumber,
uint256 index
) external view returns (uint96) {
StakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index];
_validateStakeUpdateAtBlockNumber(totalStakeUpdate, blockNumber);
return totalStakeUpdate.stake;
}
/**
* @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber`
* @param blockNumber Block number to retrieve the stake indices from.
* @param quorumNumbers The quorum numbers to get the stake indices for.
* @dev Function will revert if there are no indices for the given `blockNumber`
*/
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]);
require(_quorumExists(quorumNumber), "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum does not exist");
require(
_totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber,
"StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber"
);
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;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategy.sol";
import "./ISignatureUtils.sol";
import "./IStrategyManager.sol";
/**
* @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 ISignatureUtils {
// @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 PaymentCoordinator.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 A minimum delay -- measured in blocks -- enforced between:
* 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing`
* and
* 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate`
* @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails,
* then they are only allowed to either increase this value or keep it the same.
*/
uint32 stakerOptOutWindowBlocks;
}
/**
* @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator.
* @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function.
*/
struct StakerDelegation {
// the staker who is delegating
address staker;
// the operator being delegated to
address operator;
// the staker's nonce
uint256 nonce;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* @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;
}
/**
* Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored.
* In functions that operate on existing queued withdrawals -- e.g. completeQueuedWithdrawal`, the data is resubmitted and the hash of the submitted
* data is computed by `calculateWithdrawalRoot` and checked against the stored hash in order to confirm the integrity of the submitted data.
*/
struct Withdrawal {
// The address that originated the Withdrawal
address staker;
// The address that the staker was delegated to at the time that the Withdrawal was created
address delegatedTo;
// The address that can complete the Withdrawal + will receive funds when completing the withdrawal
address withdrawer;
// Nonce used to guarantee that otherwise identical withdrawals have unique hashes
uint256 nonce;
// Block number when the Withdrawal was created
uint32 startBlock;
// Array of strategies that the Withdrawal contains
IStrategy[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
}
struct QueuedWithdrawalParams {
// Array of strategies that the QueuedWithdrawal contains
IStrategy[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
// The address of the withdrawer
address withdrawer;
}
// @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails.
event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails);
/// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails
event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails);
/**
* @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 new withdrawal is queued.
* @param withdrawalRoot Is the hash of the `withdrawal`.
* @param withdrawal Is the withdrawal itself.
*/
event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal);
/// @notice Emitted when a queued withdrawal is completed
event WithdrawalCompleted(bytes32 withdrawalRoot);
/// @notice Emitted when the `minWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
event MinWithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue);
/// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`.
event StrategyWithdrawalDelayBlocksSet(IStrategy strategy, uint256 previousValue, uint256 newValue);
/**
* @notice Registers the caller as an operator in EigenLayer.
* @param registeringOperatorDetails is the `OperatorDetails` for the operator.
* @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 Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function registerAsOperator(
OperatorDetails calldata registeringOperatorDetails,
string calldata metadataURI
) external;
/**
* @notice Updates an operator's stored `OperatorDetails`.
* @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`.
*
* @dev The caller must have previously registered as an operator in EigenLayer.
*/
function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external;
/**
* @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
* @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(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 Verifies the operator approves of this delegation
* @param approverSalt A unique single use value tied to an individual signature.
* @dev The approverSignatureAndExpiry is used in the event that:
* 1) the operator's `delegationApprover` address is set to a non-zero value.
* AND
* 2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator
* or their delegationApprover is the `msg.sender`, then approval is assumed.
* @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateTo(
address operator,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external;
/**
* @notice Caller delegates a staker's stake to an operator with valid signatures from both parties.
* @param staker The account delegating stake to an `operator` account
* @param operator The account (`staker`) is delegating its assets to for use in serving applications built on EigenLayer.
* @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator
* @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that:
* @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
*
* @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action.
* @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271.
* @dev the operator's `delegationApprover` address is set to a non-zero value.
* @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover
* is the `msg.sender`, then approval is assumed.
* @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
* @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateToBySignature(
address staker,
address operator,
SignatureWithExpiry memory stakerSignatureAndExpiry,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external;
/**
* @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager
* and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary.
* @param staker The account to be undelegated.
* @return withdrawalRoot The root of the newly queued withdrawal, if a withdrawal was queued. Otherwise just bytes32(0).
*
* @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 already undelegated.
*/
function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot);
/**
* Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed
* from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from
* their operator.
*
* All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay.
*/
function queueWithdrawals(
QueuedWithdrawalParams[] calldata queuedWithdrawalParams
) external returns (bytes32[] memory);
/**
* @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer`
* @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.
* This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused)
* @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array
* @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves
* and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies
* will simply be transferred to the caller directly.
* @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw`
* @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that
* any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in
* any other strategies, which will be transferred to the withdrawer.
*/
function completeQueuedWithdrawal(
Withdrawal calldata withdrawal,
IERC20[] calldata tokens,
uint256 middlewareTimesIndex,
bool receiveAsTokens
) external;
/**
* @notice Array-ified version of `completeQueuedWithdrawal`.
* Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer`
* @param withdrawals The Withdrawals to complete.
* @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
* @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
* @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,
uint256[] calldata middlewareTimesIndexes,
bool[] calldata receiveAsTokens
) external;
/**
* @notice Increases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to increase the delegated shares.
* @param shares The number of shares to increase.
*
* @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function increaseDelegatedShares(
address staker,
IStrategy strategy,
uint256 shares
) external;
/**
* @notice Decreases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to decrease the delegated shares.
* @param shares The number of shares to decrease.
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function decreaseDelegatedShares(
address staker,
IStrategy strategy,
uint256 shares
) external;
/**
* @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 Returns the OperatorDetails struct associated with an `operator`.
*/
function operatorDetails(address operator) external view returns (OperatorDetails memory);
/**
* @notice Returns the delegationApprover account for an operator
*/
function delegationApprover(address operator) external view returns (address);
/**
* @notice Returns the stakerOptOutWindowBlocks for an operator
*/
function stakerOptOutWindowBlocks(address operator) external view returns (uint256);
/**
* @notice Given array of strategies, returns array of shares for the operator
*/
function getOperatorShares(
address operator,
IStrategy[] memory strategies
) external view returns (uint256[] memory);
/**
* @notice Given a list of strategies, return the minimum number of blocks that must pass to withdraw
* from all the inputted strategies. Return value is >= minWithdrawalDelayBlocks as this is the global min withdrawal delay.
* @param strategies The strategies to check withdrawal delays for
*/
function getWithdrawalDelay(IStrategy[] calldata strategies) external view returns (uint256);
/**
* @notice returns the total number of shares in `strategy` that are delegated to `operator`.
* @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator.
* @dev By design, the following invariant should hold for each Strategy:
* (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator)
* = sum (delegateable shares of all stakers delegated to the operator)
*/
function operatorShares(address operator, IStrategy strategy) 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 Mapping: staker => number of signed delegation nonces (used in `delegateToBySignature`) from the staker that the contract has already checked
function stakerNonce(address staker) external view returns (uint256);
/**
* @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` and `delegateToBySignature` functions. Note that these functions only process 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 Minimum delay enforced by this contract for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
* Note that strategies each have a separate withdrawal delay, which can be greater than this value. So the minimum number of blocks that must pass
* to withdraw a strategy is MAX(minWithdrawalDelayBlocks, strategyWithdrawalDelayBlocks[strategy])
*/
function minWithdrawalDelayBlocks() external view returns (uint256);
/**
* @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced).
*/
function strategyWithdrawalDelayBlocks(IStrategy strategy) external view returns (uint256);
/// @notice return address of the beaconChainETHStrategy
function beaconChainETHStrategy() external view returns (IStrategy);
/**
* @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator`
* @param staker The signing staker
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateCurrentStakerDelegationDigestHash(
address staker,
address operator,
uint256 expiry
) external view returns (bytes32);
/**
* @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function
* @param staker The signing staker
* @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]`
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateStakerDelegationDigestHash(
address staker,
uint256 _stakerNonce,
address operator,
uint256 expiry
) external view returns (bytes32);
/**
* @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions.
* @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 The EIP-712 typehash for the contract's domain
function DOMAIN_TYPEHASH() external view returns (bytes32);
/// @notice The EIP-712 typehash for the StakerDelegation struct used by the contract
function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32);
/// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
/**
* @notice Getter function for the current EIP-712 domain separator for this contract.
*
* @dev The domain separator will change in the event of a fork that changes the ChainID.
* @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision.
* for more detailed information please read EIP-712.
*/
function domainSeparator() external view returns (bytes32);
/// @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 the keccak256 hash of `withdrawal`.
function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategyManager, IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
/**
* @title Storage variables for the `StakeRegistry` contract.
* @author Layr Labs, Inc.
* @notice This storage contract is separate from the logic to simplify the upgrade process.
*/
abstract contract StakeRegistryStorage is IStakeRegistry {
/// @notice Constant used as a divisor in calculating weights.
uint256 public constant WEIGHTING_DIVISOR = 1e18;
/// @notice Maximum length of dynamic arrays in the `strategyParams` mapping.
uint8 public constant MAX_WEIGHING_FUNCTION_LENGTH = 32;
/// @notice Constant used as a divisor in dealing with BIPS amounts.
uint256 internal constant MAX_BIPS = 10000;
/// @notice The address of the Delegation contract for EigenLayer.
IDelegationManager public immutable delegation;
/// @notice the coordinator contract that this registry is associated with
address public immutable registryCoordinator;
/// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]`
/// evaluated by this contract's 'VoteWeigher' logic.
mapping(uint8 => uint96) public minimumStakeForQuorum;
/// @notice History of the total stakes for each quorum
mapping(uint8 => StakeUpdate[]) internal _totalStakeHistory;
/// @notice mapping from operator's operatorId to the history of their stake updates
mapping(bytes32 => mapping(uint8 => StakeUpdate[])) internal operatorStakeHistory;
/**
* @notice mapping from quorum number to the list of strategies considered and their
* corresponding multipliers for that specific quorum
*/
mapping(uint8 => StrategyParams[]) public strategyParams;
mapping(uint8 => IStrategy[]) public strategiesPerQuorum;
constructor(
IRegistryCoordinator _registryCoordinator,
IDelegationManager _delegationManager
) {
registryCoordinator = address(_registryCoordinator);
delegation = _delegationManager;
}
// storage gap for upgradeability
// slither-disable-next-line shadowing-state
uint256[45] private __GAP;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IBLSApkRegistry} from "./IBLSApkRegistry.sol";
import {IStakeRegistry} from "./IStakeRegistry.sol";
import {IIndexRegistry} from "./IIndexRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
/**
* @title Interface for a contract that coordinates between various registries for an AVS.
* @author Layr Labs, Inc.
*/
interface IRegistryCoordinator {
// EVENTS
/// Emits when an operator is registered
event OperatorRegistered(address indexed operator, bytes32 indexed operatorId);
/// Emits when an operator is deregistered
event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId);
event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams);
event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover);
event EjectorUpdated(address prevEjector, address newEjector);
/// @notice emitted when all the operators for a quorum are updated at once
event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber);
// DATA STRUCTURES
enum OperatorStatus
{
// default is NEVER_REGISTERED
NEVER_REGISTERED,
REGISTERED,
DEREGISTERED
}
// STRUCTS
/**
* @notice Data structure for storing info on operators
*/
struct OperatorInfo {
// the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegistry
bytes32 operatorId;
// indicates whether the operator is actively registered for serving the middleware or not
OperatorStatus status;
}
/**
* @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the
* quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber`
* @dev nextUpdateBlockNumber is initialized to 0 for the latest update
*/
struct QuorumBitmapUpdate {
uint32 updateBlockNumber;
uint32 nextUpdateBlockNumber;
uint192 quorumBitmap;
}
/**
* @notice Data structure for storing operator set params for a given quorum. Specifically the
* `maxOperatorCount` is the maximum number of operators that can be registered for the quorum,
* `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum,
* and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked.
*/
struct OperatorSetParam {
uint32 maxOperatorCount;
uint16 kickBIPsOfOperatorStake;
uint16 kickBIPsOfTotalStake;
}
/**
* @notice Data structure for the parameters needed to kick an operator from a quorum with number `quorumNumber`, used during registration churn.
* `operator` is the address of the operator to kick
*/
struct OperatorKickParam {
uint8 quorumNumber;
address operator;
}
/// @notice Returns the operator set params for the given `quorumNumber`
function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory);
/// @notice the Stake registry contract that will keep track of operators' stakes
function stakeRegistry() external view returns (IStakeRegistry);
/// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' BLS aggregate pubkeys per quorum
function blsApkRegistry() external view returns (IBLSApkRegistry);
/// @notice the index Registry contract that will keep track of operators' indexes
function indexRegistry() external view returns (IIndexRegistry);
/**
* @notice Ejects the provided operator from the provided quorums from the AVS
* @param operator is the operator to eject
* @param quorumNumbers are the quorum numbers to eject the operator from
*/
function ejectOperator(
address operator,
bytes calldata quorumNumbers
) external;
/// @notice Returns the number of quorums the registry coordinator has created
function quorumCount() external view returns (uint8);
/// @notice Returns the operator struct for the given `operator`
function getOperator(address operator) external view returns (OperatorInfo memory);
/// @notice Returns the operatorId for the given `operator`
function getOperatorId(address operator) external view returns (bytes32);
/// @notice Returns the operator address for the given `operatorId`
function getOperatorFromId(bytes32 operatorId) external view returns (address operator);
/// @notice Returns the status for the given `operator`
function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus);
/// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber`
function getQuorumBitmapIndicesAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory);
/**
* @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`
* @dev reverts if `index` is incorrect
*/
function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192);
/// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history
function getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory);
/// @notice Returns the current quorum bitmap for the given `operatorId`
function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192);
/// @notice Returns the length of the quorum bitmap history for the given `operatorId`
function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256);
/// @notice Returns the registry at the desired index
function registries(uint256) external view returns (address);
/// @notice Returns the number of registries
function numRegistries() external view returns (uint256);
/**
* @notice Returns the message hash that an operator must sign to register their BLS public key.
* @param operator is the address of the operator registering their BLS public key
*/
function pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory);
/// @notice returns the blocknumber the quorum was last updated all at once for all operators
function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256);
/// @notice The owner of the registry coordinator
function owner() external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {IRegistry} from "./IRegistry.sol";
/**
* @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums.
* @author Layr Labs, Inc.
*/
interface IStakeRegistry is IRegistry {
// DATA STRUCTURES
/// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage
struct StakeUpdate {
// the block number at which the stake amounts were updated and stored
uint32 updateBlockNumber;
// the block number at which the *next update* occurred.
/// @notice This entry has the value **0** until another update takes place.
uint32 nextUpdateBlockNumber;
// stake weight for the quorum
uint96 stake;
}
/**
* @notice In weighing a particular strategy, the amount of underlying asset for that strategy is
* multiplied by its multiplier, then divided by WEIGHTING_DIVISOR
*/
struct StrategyParams {
IStrategy strategy;
uint96 multiplier;
}
// EVENTS
/// @notice emitted whenever the stake of `operator` is updated
event OperatorStakeUpdate(
bytes32 indexed operatorId,
uint8 quorumNumber,
uint96 stake
);
/// @notice emitted when the minimum stake for a quorum is updated
event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake);
/// @notice emitted when a new quorum is created
event QuorumCreated(uint8 indexed quorumNumber);
/// @notice emitted when `strategy` has been added to the array at `strategyParams[quorumNumber]`
event StrategyAddedToQuorum(uint8 indexed quorumNumber, IStrategy strategy);
/// @notice emitted when `strategy` has removed from the array at `strategyParams[quorumNumber]`
event StrategyRemovedFromQuorum(uint8 indexed quorumNumber, IStrategy strategy);
/// @notice emitted when `strategy` has its `multiplier` updated in the array at `strategyParams[quorumNumber]`
event StrategyMultiplierUpdated(uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier);
/**
* @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`.
* @param operator The address of the operator to register.
* @param operatorId The id of the operator to register.
* @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber.
* @return The operator's current stake for each quorum, and the total stake for each quorum
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(
address operator,
bytes32 operatorId,
bytes memory quorumNumbers
) external returns (uint96[] memory, uint96[] memory);
/**
* @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`.
* @param operatorId The id of the operator to deregister.
* @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) external;
/**
* @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake
*/
function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external;
/// @notice Adds new strategies and the associated multipliers to the @param quorumNumber.
function addStrategies(
uint8 quorumNumber,
StrategyParams[] memory strategyParams
) external;
/**
* @notice This function is used for removing strategies and their associated weights from the
* mapping strategyParams for a specific @param quorumNumber.
* @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise
* the removal of lower index entries will cause a shift in the indices of the other strategiesToRemove
*/
function removeStrategies(uint8 quorumNumber, uint256[] calldata indicesToRemove) external;
/**
* @notice This function is used for modifying the weights of strategies that are already in the
* mapping strategyParams for a specific
* @param quorumNumber is the quorum number to change the strategy for
* @param strategyIndices are the indices of the strategies to change
* @param newMultipliers are the new multipliers for the strategies
*/
function modifyStrategyParams(
uint8 quorumNumber,
uint256[] calldata strategyIndices,
uint96[] calldata newMultipliers
) external;
/// @notice Constant used as a divisor in calculating weights.
function WEIGHTING_DIVISOR() external pure returns (uint256);
/// @notice Returns the EigenLayer delegation manager contract.
function delegation() external view returns (IDelegationManager);
/// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]`
function minimumStakeForQuorum(uint8 quorumNumber) external view returns (uint96);
/// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`.
function strategyParamsLength(uint8 quorumNumber) external view returns (uint256);
/// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber`
function strategyParamsByIndex(
uint8 quorumNumber,
uint256 index
) external view returns (StrategyParams memory);
/**
* @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber.
* @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount`
*/
function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external view returns (uint96);
/**
* @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array.
* @param operatorId The id of the operator of interest.
* @param quorumNumber The quorum number to get the stake for.
*/
function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate[] memory);
function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256);
/**
* @notice Returns the `index`-th entry in the dynamic array of total stake, `totalStakeHistory` for quorum `quorumNumber`.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`.
*/
function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (StakeUpdate memory);
/// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber`
function getStakeUpdateIndexAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber)
external
view
returns (uint32);
/// @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber`
function getTotalStakeIndicesAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) ;
/**
* @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`.
* @dev Function will revert if `index` is out-of-bounds.
*/
function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index)
external
view
returns (StakeUpdate memory);
/**
* @notice Returns the most recent stake weight for the `operatorId` for a certain quorum
* @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history
*/
function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate memory);
/**
* @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the
* `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry
* corresponds to the operator's stake at `blockNumber`. Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param operatorId The id of the operator of interest.
* @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
* @dev used the BLSSignatureChecker to get past stakes of signing operators
*/
function getStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index)
external
view
returns (uint96);
/**
* @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the
* `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`.
* Reverts otherwise.
* @param quorumNumber The quorum number to get the stake for.
* @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`.
* @param blockNumber Block number to make sure the stake is from.
* @dev Function will revert if `index` is out-of-bounds.
* @dev used the BLSSignatureChecker to get past stakes of signing operators
*/
function getTotalStakeAtBlockNumberFromIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (uint96);
/**
* @notice Returns the most recent stake weight for the `operatorId` for quorum `quorumNumber`
* @dev Function returns weight of **0** in the event that the operator has no stake history
*/
function getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96);
/// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber`
function getStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber)
external
view
returns (uint96);
/**
* @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`.
* @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty.
*/
function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96);
/**
* @notice Called by the registry coordinator to update an operator's stake for one
* or more quorums.
*
* If the operator no longer has the minimum stake required for a quorum, they are
* added to the
* @return A bitmap of quorums where the operator no longer meets the minimum stake
* and should be deregistered.
*/
function updateOperatorStake(
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers
) external returns (uint192);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
/**
* @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap.
* @author Layr Labs, Inc.
*/
library BitmapUtils {
/**
* @notice Byte arrays are meant to contain unique bytes.
* If the array length exceeds 256, then it's impossible for all entries to be unique.
* This constant captures the max allowed array length (inclusive, i.e. 256 is allowed).
*/
uint256 internal constant MAX_BYTE_ARRAY_LENGTH = 256;
/**
* @notice Converts an ordered array of bytes into a bitmap.
* @param orderedBytesArray The array of bytes to convert/compress into a bitmap. Must be in strictly ascending order.
* @return The resulting bitmap.
* @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap.
* @dev This function will eventually revert in the event that the `orderedBytesArray` is not properly ordered (in ascending order).
* @dev This function will also revert if the `orderedBytesArray` input contains any duplicate entries (i.e. duplicate bytes).
*/
function orderedBytesArrayToBitmap(bytes memory orderedBytesArray) internal pure returns (uint256) {
// sanity-check on input. a too-long input would fail later on due to having duplicate entry(s)
require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH,
"BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long");
// return empty bitmap early if length of array is 0
if (orderedBytesArray.length == 0) {
return uint256(0);
}
// initialize the empty bitmap, to be built inside the loop
uint256 bitmap;
// initialize an empty uint256 to be used as a bitmask inside the loop
uint256 bitMask;
// perform the 0-th loop iteration with the ordering check *omitted* (since it is unnecessary / will always pass)
// construct a single-bit mask from the numerical value of the 0th byte of the array, and immediately add it to the bitmap
bitmap = uint256(1 << uint8(orderedBytesArray[0]));
// loop through each byte in the array to construct the bitmap
for (uint256 i = 1; i < orderedBytesArray.length; ++i) {
// construct a single-bit mask from the numerical value of the next byte of the array
bitMask = uint256(1 << uint8(orderedBytesArray[i]));
// check strictly ascending array ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap)
require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered");
// add the entry to the bitmap
bitmap = (bitmap | bitMask);
}
return bitmap;
}
/**
* @notice Converts an ordered byte array to a bitmap, validating that all bits are less than `bitUpperBound`
* @param orderedBytesArray The array to convert to a bitmap; must be in strictly ascending order
* @param bitUpperBound The exclusive largest bit. Each bit must be strictly less than this value.
* @dev Reverts if bitmap contains a bit greater than or equal to `bitUpperBound`
*/
function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) {
uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray);
require((1 << bitUpperBound) > bitmap,
"BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"
);
return bitmap;
}
/**
* @notice Utility function for checking if a bytes array is strictly ordered, in ascending order.
* @param bytesArray the bytes array of interest
* @return Returns 'true' if the array is ordered in strictly ascending order, and 'false' otherwise.
* @dev This function returns 'true' for the edge case of the `bytesArray` having zero length.
* It also returns 'false' early for arrays with length in excess of MAX_BYTE_ARRAY_LENGTH (i.e. so long that they cannot be strictly ordered)
*/
function isArrayStrictlyAscendingOrdered(bytes calldata bytesArray) internal pure returns (bool) {
// Return early if the array is too long, or has a length of 0
if (bytesArray.length > MAX_BYTE_ARRAY_LENGTH) {
return false;
}
if (bytesArray.length == 0) {
return true;
}
// Perform the 0-th loop iteration by pulling the 0th byte out of the array
bytes1 singleByte = bytesArray[0];
// For each byte, validate that each entry is *strictly greater than* the previous
// If it isn't, return false as the array is not ordered
for (uint256 i = 1; i < bytesArray.length; ++i) {
if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) {
return false;
}
// Pull the next byte out of the array
singleByte = bytesArray[i];
}
return true;
}
/**
* @notice Converts a bitmap into an array of bytes.
* @param bitmap The bitmap to decompress/convert to an array of bytes.
* @return bytesArray The resulting bitmap array of bytes.
* @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap
*/
function bitmapToBytesArray(uint256 bitmap) internal pure returns (bytes memory /*bytesArray*/) {
// initialize an empty uint256 to be used as a bitmask inside the loop
uint256 bitMask;
// allocate only the needed amount of memory
bytes memory bytesArray = new bytes(countNumOnes(bitmap));
// track the array index to assign to
uint256 arrayIndex = 0;
/**
* loop through each index in the bitmap to construct the array,
* but short-circuit the loop if we reach the number of ones and thus are done
* assigning to memory
*/
for (uint256 i = 0; (arrayIndex < bytesArray.length) && (i < 256); ++i) {
// construct a single-bit mask for the i-th bit
bitMask = uint256(1 << i);
// check if the i-th bit is flipped in the bitmap
if (bitmap & bitMask != 0) {
// if the i-th bit is flipped, then add a byte encoding the value 'i' to the `bytesArray`
bytesArray[arrayIndex] = bytes1(uint8(i));
// increment the bytesArray slot since we've assigned one more byte of memory
unchecked{ ++arrayIndex; }
}
}
return bytesArray;
}
/// @return count number of ones in binary representation of `n`
function countNumOnes(uint256 n) internal pure returns (uint16) {
uint16 count = 0;
while (n > 0) {
n &= (n - 1); // Clear the least significant bit (turn off the rightmost set bit).
count++; // Increment the count for each cleared bit (each one encountered).
}
return count; // Return the total count of ones in the binary representation of n.
}
/// @notice Returns `true` if `bit` is in `bitmap`. Returns `false` otherwise.
function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) {
return 1 == ((bitmap >> bit) & 1);
}
/**
* @notice Returns a copy of `bitmap` with `bit` set.
* @dev IMPORTANT: we're dealing with stack values here, so this doesn't modify
* the original bitmap. Using this correctly requires an assignment statement:
* `bitmap = bitmap.setBit(bit);`
*/
function setBit(uint256 bitmap, uint8 bit) internal pure returns (uint256) {
return bitmap | (1 << bit);
}
/**
* @notice Returns true if `bitmap` has no set bits
*/
function isEmpty(uint256 bitmap) internal pure returns (bool) {
return bitmap == 0;
}
/**
* @notice Returns true if `a` and `b` have no common set bits
*/
function noBitsInCommon(uint256 a, uint256 b) internal pure returns (bool) {
return a & b == 0;
}
/**
* @notice Returns true if `a` is a subset of `b`: ALL of the bits in `a` are also in `b`
*/
function isSubsetOf(uint256 a, uint256 b) internal pure returns (bool) {
return a & b == a;
}
/**
* @notice Returns a new bitmap that contains all bits set in either `a` or `b`
* @dev Result is the union of `a` and `b`
*/
function plus(uint256 a, uint256 b) internal pure returns (uint256) {
return a | b;
}
/**
* @notice Returns a new bitmap that clears all set bits of `b` from `a`
* @dev Negates `b` and returns the intersection of the result with `a`
*/
function minus(uint256 a, uint256 b) internal pure returns (uint256) {
return a & ~b;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Minimal interface for an `Strategy` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Custom `Strategy` implementations may expand extensively on this interface.
*/
interface IStrategy {
/**
* @notice Used to deposit tokens into this Strategy
* @param token is the ERC20 token being deposited
* @param amount is the amount of token being deposited
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well.
* @return newShares is the number of new shares issued at the current exchange ratio.
*/
function deposit(IERC20 token, uint256 amount) external returns (uint256);
/**
* @notice Used to withdraw tokens from this Strategy, to the `recipient`'s address
* @param recipient is the address to receive the withdrawn funds
* @param token is the ERC20 token being transferred out
* @param amountShares is the amount of shares being withdrawn
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* other functions, and individual share balances are recorded in the strategyManager as well.
*/
function withdraw(address recipient, IERC20 token, uint256 amountShares) external;
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlying(uint256 amountShares) external returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToShares(uint256 amountUnderlying) external returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications
*/
function userUnderlying(address user) external returns (uint256);
/**
* @notice convenience function for fetching the current total shares of `user` in this strategy, by
* querying the `strategyManager` contract
*/
function shares(address user) external view returns (uint256);
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlyingView(uint256 amountShares) external view returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToSharesView(uint256 amountUnderlying) external view returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
*/
function userUnderlyingView(address user) external view returns (uint256);
/// @notice The underlying token for shares in this Strategy
function underlyingToken() external view returns (IERC20);
/// @notice The total number of extant shares in this Strategy
function totalShares() external view returns (uint256);
/// @notice Returns either a brief string explaining the strategy's goal & purpose, or a link to metadata that explains in more detail.
function explanation() external view returns (string memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title The interface for common signature utilities.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface ISignatureUtils {
// @notice Struct that bundles together a signature and an expiration time for the signature. 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;
}
// @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management.
struct SignatureWithSaltAndExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the salt used to generate the signature
bytes32 salt;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategy.sol";
import "./ISlasher.sol";
import "./IDelegationManager.sol";
import "./IEigenPodManager.sol";
/**
* @title Interface for the primary entrypoint for funds into EigenLayer.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice See the `StrategyManager` contract itself for implementation details.
*/
interface IStrategyManager {
/**
* @notice Emitted when a new deposit occurs on behalf of `staker`.
* @param staker Is the staker who is depositing funds into EigenLayer.
* @param strategy Is the strategy that `staker` has deposited into.
* @param token Is the token that `staker` deposited.
* @param shares Is the number of new shares `staker` has been granted in `strategy`.
*/
event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares);
/// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner
event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value);
/// @notice Emitted when the `strategyWhitelister` is changed
event StrategyWhitelisterChanged(address previousAddress, address newAddress);
/// @notice Emitted when a strategy is added to the approved list of strategies for deposit
event StrategyAddedToDepositWhitelist(IStrategy strategy);
/// @notice Emitted when a strategy is removed from the approved list of strategies for deposit
event StrategyRemovedFromDepositWhitelist(IStrategy strategy);
/**
* @notice Deposits `amount` of `token` into the specified `strategy`, with the resultant shares credited to `msg.sender`
* @param strategy is the specified strategy where deposit is to be made,
* @param token is the denomination in which the deposit is to be made,
* @param amount is the amount of token to be deposited in the strategy by the staker
* @return shares The amount of new shares in the `strategy` created as part of the action.
* @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
* @dev Cannot be called by an address that is 'frozen' (this function will revert if the `msg.sender` is frozen).
*
* WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors
* where the token balance and corresponding strategy shares are not in sync upon reentrancy.
*/
function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares);
/**
* @notice Used for depositing an asset into the specified strategy with the resultant shares credited to `staker`,
* who must sign off on the action.
* Note that the assets are transferred out/from the `msg.sender`, not from the `staker`; this function is explicitly designed
* purely to help one address deposit 'for' another.
* @param strategy is the specified strategy where deposit is to be made,
* @param token is the denomination in which the deposit is to be made,
* @param amount is the amount of token to be deposited in the strategy by the staker
* @param staker the staker that the deposited assets will be credited to
* @param expiry the timestamp at which the signature expires
* @param signature is a valid signature from the `staker`. either an ECDSA signature if the `staker` is an EOA, or data to forward
* following EIP-1271 if the `staker` is a contract
* @return shares The amount of new shares in the `strategy` created as part of the action.
* @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
* @dev A signature is required for this function to eliminate the possibility of griefing attacks, specifically those
* targeting stakers who may be attempting to undelegate.
* @dev Cannot be called if thirdPartyTransfersForbidden is set to true for this strategy
*
* WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors
* where the token balance and corresponding strategy shares are not in sync upon reentrancy
*/
function depositIntoStrategyWithSignature(
IStrategy strategy,
IERC20 token,
uint256 amount,
address staker,
uint256 expiry,
bytes memory signature
) external returns (uint256 shares);
/// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue
function removeShares(address staker, IStrategy strategy, uint256 shares) external;
/// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external;
/// @notice Used by the DelegationManager to convert withdrawn shares to tokens and send them to a recipient
function withdrawSharesAsTokens(address recipient, IStrategy strategy, uint256 shares, IERC20 token) external;
/// @notice Returns the current shares of `user` in `strategy`
function stakerStrategyShares(address user, IStrategy strategy) external view returns (uint256 shares);
/**
* @notice Get all details on the staker's deposits and corresponding shares
* @return (staker's strategies, shares in these strategies)
*/
function getDeposits(address staker) external view returns (IStrategy[] memory, uint256[] memory);
/// @notice Simple getter function that returns `stakerStrategyList[staker].length`.
function stakerStrategyListLength(address staker) external view returns (uint256);
/**
* @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into
* @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already)
* @param thirdPartyTransfersForbiddenValues bool values to set `thirdPartyTransfersForbidden` to for each strategy
*/
function addStrategiesToDepositWhitelist(
IStrategy[] calldata strategiesToWhitelist,
bool[] calldata thirdPartyTransfersForbiddenValues
) external;
/**
* @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into
* @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it)
*/
function removeStrategiesFromDepositWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external;
/// @notice Returns the single, central Delegation contract of EigenLayer
function delegation() external view returns (IDelegationManager);
/// @notice Returns the single, central Slasher contract of EigenLayer
function slasher() external view returns (ISlasher);
/// @notice Returns the EigenPodManager contract of EigenLayer
function eigenPodManager() external view returns (IEigenPodManager);
/// @notice Returns the address of the `strategyWhitelister`
function strategyWhitelister() external view returns (address);
/// @notice Returns bool for whether or not `strategy` is whitelisted for deposit
function strategyIsWhitelistedForDeposit(IStrategy strategy) external view returns (bool);
/**
* @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling
* depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker.
*/
function thirdPartyTransfersForbidden(IStrategy strategy) external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IRegistry} from "./IRegistry.sol";
import {BN254} from "../libraries/BN254.sol";
/**
* @title Minimal interface for a registry that keeps track of aggregate operator public keys across many quorums.
* @author Layr Labs, Inc.
*/
interface IBLSApkRegistry is IRegistry {
// STRUCTS
/// @notice Data structure used to track the history of the Aggregate Public Key of all operators
struct ApkUpdate {
// first 24 bytes of keccak256(apk_x0, apk_x1, apk_y0, apk_y1)
bytes24 apkHash;
// block number at which the update occurred
uint32 updateBlockNumber;
// block number at which the next update occurred
uint32 nextUpdateBlockNumber;
}
/**
* @notice Struct used when registering a new public key
* @param pubkeyRegistrationSignature is the registration message signed by the private key of the operator
* @param pubkeyG1 is the corresponding G1 public key of the operator
* @param pubkeyG2 is the corresponding G2 public key of the operator
*/
struct PubkeyRegistrationParams {
BN254.G1Point pubkeyRegistrationSignature;
BN254.G1Point pubkeyG1;
BN254.G2Point pubkeyG2;
}
// EVENTS
/// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`.
event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2);
// @notice Emitted when a new operator pubkey is registered for a set of quorums
event OperatorAddedToQuorums(
address operator,
bytes32 operatorId,
bytes quorumNumbers
);
// @notice Emitted when an operator pubkey is removed from a set of quorums
event OperatorRemovedFromQuorums(
address operator,
bytes32 operatorId,
bytes quorumNumbers
);
/**
* @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`.
* @param operator The address of the operator to register.
* @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(address operator, bytes calldata quorumNumbers) external;
/**
* @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`.
* @param operator The address of the operator to deregister.
* @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber.
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(address operator, bytes calldata quorumNumbers) external;
/**
* @notice Initializes a new quorum by pushing its first apk update
* @param quorumNumber The number of the new quorum
*/
function initializeQuorum(uint8 quorumNumber) external;
/**
* @notice mapping from operator address to pubkey hash.
* Returns *zero* if the `operator` has never registered, and otherwise returns the hash of the public key of the operator.
*/
function operatorToPubkeyHash(address operator) external view returns (bytes32);
/**
* @notice mapping from pubkey hash to operator address.
* Returns *zero* if no operator has ever registered the public key corresponding to `pubkeyHash`,
* and otherwise returns the (unique) registered operator who owns the BLS public key that is the preimage of `pubkeyHash`.
*/
function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address);
/**
* @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key.
* @param operator is the operator for whom the key is being registered
* @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership
* @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership
*/
function registerBLSPublicKey(
address operator,
PubkeyRegistrationParams calldata params,
BN254.G1Point calldata pubkeyRegistrationMessageHash
) external returns (bytes32 operatorId);
/**
* @notice Returns the pubkey and pubkey hash of an operator
* @dev Reverts if the operator has not registered a valid pubkey
*/
function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32);
/// @notice Returns the current APK for the provided `quorumNumber `
function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory);
/// @notice Returns the index of the quorumApk index at `blockNumber` for the provided `quorumNumber`
function getApkIndicesAtBlockNumber(bytes calldata quorumNumbers, uint256 blockNumber) external view returns(uint32[] memory);
/// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber`
function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory);
/// @notice Returns the operator address for the given `pubkeyHash`
function getOperatorFromPubkeyHash(bytes32 pubkeyHash) external view returns (address);
/**
* @notice get 24 byte hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`;
* called by checkSignatures in BLSSignatureChecker.sol.
* @param quorumNumber is the quorum whose ApkHash is being retrieved
* @param blockNumber is the number of the block for which the latest ApkHash will be retrieved
* @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage
*/
function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24);
/// @notice returns the ID used to identify the `operator` within this AVS.
/// @dev Returns zero in the event that the `operator` has never registered for the AVS
function getOperatorId(address operator) external view returns (bytes32);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IRegistry} from "./IRegistry.sol";
/**
* @title Interface for a `Registry`-type contract that keeps track of an ordered list of operators for up to 256 quorums.
* @author Layr Labs, Inc.
*/
interface IIndexRegistry is IRegistry {
// EVENTS
// emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated
event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex);
// DATA STRUCTURES
// struct used to give definitive ordering to operators at each blockNumber.
struct OperatorUpdate {
// blockNumber number from which `operatorIndex` was the operators index
// the operator's index is the first entry such that `blockNumber >= entry.fromBlockNumber`
uint32 fromBlockNumber;
// the operator at this index
bytes32 operatorId;
}
// struct used to denote the number of operators in a quorum at a given blockNumber
struct QuorumUpdate {
// The total number of operators at a `blockNumber` is the first entry such that `blockNumber >= entry.fromBlockNumber`
uint32 fromBlockNumber;
// The number of operators at `fromBlockNumber`
uint32 numOperators;
}
/**
* @notice Registers the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`.
* @param operatorId is the id of the operator that is being registered
* @param quorumNumbers is the quorum numbers the operator is registered for
* @return numOperatorsPerQuorum is a list of the number of operators (including the registering operator) in each of the quorums the operator is registered for
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already registered
*/
function registerOperator(bytes32 operatorId, bytes calldata quorumNumbers) external returns(uint32[] memory);
/**
* @notice Deregisters the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`.
* @param operatorId is the id of the operator that is being deregistered
* @param quorumNumbers is the quorum numbers the operator is deregistered for
* @dev access restricted to the RegistryCoordinator
* @dev Preconditions (these are assumed, not validated in this contract):
* 1) `quorumNumbers` has no duplicates
* 2) `quorumNumbers.length` != 0
* 3) `quorumNumbers` is ordered in ascending order
* 4) the operator is not already deregistered
* 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for
*/
function deregisterOperator(bytes32 operatorId, bytes calldata quorumNumbers) external;
/**
* @notice Initialize a quorum by pushing its first quorum update
* @param quorumNumber The number of the new quorum
*/
function initializeQuorum(uint8 quorumNumber) external;
/// @notice Returns the OperatorUpdate entry for the specified `operatorIndex` and `quorumNumber` at the specified `arrayIndex`
function getOperatorUpdateAtIndex(
uint8 quorumNumber,
uint32 operatorIndex,
uint32 arrayIndex
) external view returns (OperatorUpdate memory);
/// @notice Returns the QuorumUpdate entry for the specified `quorumNumber` at the specified `quorumIndex`
function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 quorumIndex) external view returns (QuorumUpdate memory);
/// @notice Returns the most recent OperatorUpdate entry for the specified quorumNumber and operatorIndex
function getLatestOperatorUpdate(uint8 quorumNumber, uint32 operatorIndex) external view returns (OperatorUpdate memory);
/// @notice Returns the most recent QuorumUpdate entry for the specified quorumNumber
function getLatestQuorumUpdate(uint8 quorumNumber) external view returns (QuorumUpdate memory);
/// @notice Returns the current number of operators of this service for `quorumNumber`.
function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32);
/// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber`
function getOperatorListAtBlockNumber(uint8 quorumNumber, uint32 blockNumber) external view returns (bytes32[] memory);
}// SPDX-License-Identifier: MIT
// several functions are taken or adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol (MIT license):
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license
pragma solidity ^0.8.12;
/**
* @title Library for operations on the BN254 elliptic curve.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contains BN254 parameters, common operations (addition, scalar mul, pairing), and BLS signature functionality.
*/
library BN254 {
// modulus for the underlying field F_p of the elliptic curve
uint256 internal constant FP_MODULUS =
21888242871839275222246405745257275088696311157297823662689037894645226208583;
// modulus for the underlying field F_r of the elliptic curve
uint256 internal constant FR_MODULUS =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[1] * i + X[0]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
function generatorG1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
// generator of group G2
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
uint256 internal constant G2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 internal constant G2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 internal constant G2y1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
uint256 internal constant G2y0 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
/// @notice returns the G2 generator
/// @dev mind the ordering of the 1s and 0s!
/// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract
/// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)."
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding
function generatorG2() internal pure returns (G2Point memory) {
return G2Point([G2x1, G2x0], [G2y1, G2y0]);
}
// negation of the generator of group G2
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
uint256 internal constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 internal constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;
function negGeneratorG2() internal pure returns (G2Point memory) {
return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]);
}
bytes32 internal constant powersOfTauMerkleRoot =
0x22c998e49752bbb1918ba87d6d59dd0e83620a311ba91dd4b2cc84990b31b56f;
/**
* @param p Some point in G1.
* @return The negation of `p`, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, FP_MODULUS - (p.Y % FP_MODULUS));
}
}
/**
* @return r the sum of two points of G1
*/
function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "ec-add-failed");
}
/**
* @notice an optimized ecMul implementation that takes O(log_2(s)) ecAdds
* @param p the point to multiply
* @param s the scalar to multiply by
* @dev this function is only safe to use if the scalar is 9 bits or less
*/
function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) {
require(s < 2**9, "scalar-too-large");
// if s is 1 return p
if(s == 1) {
return p;
}
// the accumulated product to return
BN254.G1Point memory acc = BN254.G1Point(0, 0);
// the 2^n*p to add to the accumulated product in each iteration
BN254.G1Point memory p2n = p;
// value of most significant bit
uint16 m = 1;
// index of most significant bit
uint8 i = 0;
//loop until we reach the most significant bit
while(s >= m){
unchecked {
// if the current bit is 1, add the 2^n*p to the accumulated product
if ((s >> i) & 1 == 1) {
acc = plus(acc, p2n);
}
// double the 2^n*p for the next iteration
p2n = plus(p2n, p2n);
// increment the index and double the value of the most significant bit
m <<= 1;
++i;
}
}
// return the accumulated product
return acc;
}
/**
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
*/
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "ec-mul-failed");
}
/**
* @return The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
*/
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
) internal view returns (bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];
uint256[12] memory input;
for (uint256 i = 0; i < 2; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, input, mul(12, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
/**
* @notice This function is functionally the same as pairing(), however it specifies a gas limit
* the user can set, as a precompile may use the entire gas budget if it reverts.
*/
function safePairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
uint256 pairingGas
) internal view returns (bool, bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];
uint256[12] memory input;
for (uint256 i = 0; i < 2; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(pairingGas, 8, input, mul(12, 0x20), out, 0x20)
}
//Out is the output of the pairing precompile, either 0 or 1 based on whether the two pairings are equal.
//Success is true if the precompile actually goes through (aka all inputs are valid)
return (success, out[0] != 0);
}
/// @return hashedG1 the keccak256 hash of the G1 Point
/// @dev used for BLS signatures
function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) {
assembly {
mstore(0, mload(pk))
mstore(0x20, mload(add(0x20, pk)))
hashedG1 := keccak256(0, 0x40)
}
}
/// @return the keccak256 hash of the G2 Point
/// @dev used for BLS signatures
function hashG2Point(
BN254.G2Point memory pk
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1]));
}
/**
* @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol
*/
function hashToG1(bytes32 _x) internal view returns (G1Point memory) {
uint256 beta = 0;
uint256 y = 0;
uint256 x = uint256(_x) % FP_MODULUS;
while (true) {
(beta, y) = findYFromX(x);
// y^2 == beta
if( beta == mulmod(y, y, FP_MODULUS) ) {
return G1Point(x, y);
}
x = addmod(x, 1, FP_MODULUS);
}
return G1Point(0, 0);
}
/**
* Given X, find Y
*
* where y = sqrt(x^3 + b)
*
* Returns: (x^3 + b), y
*/
function findYFromX(uint256 x) internal view returns (uint256, uint256) {
// beta = (x^3 + b) % p
uint256 beta = addmod(mulmod(mulmod(x, x, FP_MODULUS), x, FP_MODULUS), 3, FP_MODULUS);
// y^2 = x^3 + b
// this acts like: y = sqrt(beta) = beta^((p+1) / 4)
uint256 y = expMod(beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS);
return (beta, y);
}
function expMod(uint256 _base, uint256 _exponent, uint256 _modulus) internal view returns (uint256 retval) {
bool success;
uint256[1] memory output;
uint[6] memory input;
input[0] = 0x20; // baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
input[1] = 0x20; // expLen = new(big.Int).SetBytes(getData(input, 32, 32))
input[2] = 0x20; // modLen = new(big.Int).SetBytes(getData(input, 64, 32))
input[3] = _base;
input[4] = _exponent;
input[5] = _modulus;
assembly {
success := staticcall(sub(gas(), 2000), 5, input, 0xc0, output, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "BN254.expMod: call failure");
return output[0];
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Minimal interface for a `Registry`-type contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Functions related to the registration process itself have been intentionally excluded
* because their function signatures may vary significantly.
*/
interface IRegistry {
function registryCoordinator() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./IStrategyManager.sol";
import "./IDelegationManager.sol";
/**
* @title Interface for the primary 'slashing' contract for EigenLayer.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice See the `Slasher` contract itself for implementation details.
*/
interface ISlasher {
// struct used to store information about the current state of an operator's obligations to middlewares they are serving
struct MiddlewareTimes {
// The update block for the middleware whose most recent update was earliest, i.e. the 'stalest' update out of all middlewares the operator is serving
uint32 stalestUpdateBlock;
// The latest 'serveUntilBlock' from all of the middleware that the operator is serving
uint32 latestServeUntilBlock;
}
// struct used to store details relevant to a single middleware that an operator has opted-in to serving
struct MiddlewareDetails {
// the block at which the contract begins being able to finalize the operator's registration with the service via calling `recordFirstStakeUpdate`
uint32 registrationMayBeginAtBlock;
// the block before which the contract is allowed to slash the user
uint32 contractCanSlashOperatorUntilBlock;
// the block at which the middleware's view of the operator's stake was most recently updated
uint32 latestUpdateBlock;
}
/// @notice Emitted when a middleware times is added to `operator`'s array.
event MiddlewareTimesAdded(
address operator,
uint256 index,
uint32 stalestUpdateBlock,
uint32 latestServeUntilBlock
);
/// @notice Emitted when `operator` begins to allow `contractAddress` to slash them.
event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
/// @notice Emitted when `contractAddress` signals that it will no longer be able to slash `operator` after the `contractCanSlashOperatorUntilBlock`.
event SlashingAbilityRevoked(
address indexed operator,
address indexed contractAddress,
uint32 contractCanSlashOperatorUntilBlock
);
/**
* @notice Emitted when `slashingContract` 'freezes' the `slashedOperator`.
* @dev The `slashingContract` must have permission to slash the `slashedOperator`, i.e. `canSlash(slasherOperator, slashingContract)` must return 'true'.
*/
event OperatorFrozen(address indexed slashedOperator, address indexed slashingContract);
/// @notice Emitted when `previouslySlashedAddress` is 'unfrozen', allowing them to again move deposited funds within EigenLayer.
event FrozenStatusReset(address indexed previouslySlashedAddress);
/**
* @notice Gives the `contractAddress` permission to slash the funds of the caller.
* @dev Typically, this function must be called prior to registering for a middleware.
*/
function optIntoSlashing(address contractAddress) external;
/**
* @notice Used for 'slashing' a certain operator.
* @param toBeFrozen The operator to be frozen.
* @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop.
* @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`.
*/
function freezeOperator(address toBeFrozen) external;
/**
* @notice Removes the 'frozen' status from each of the `frozenAddresses`
* @dev Callable only by the contract owner (i.e. governance).
*/
function resetFrozenStatus(address[] calldata frozenAddresses) external;
/**
* @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration
* is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param serveUntilBlock the block until which the operator's stake at the current block is slashable
* @dev adds the middleware's slashing contract to the operator's linked list
*/
function recordFirstStakeUpdate(address operator, uint32 serveUntilBlock) external;
/**
* @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals)
* to make sure the operator's stake at updateBlock is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param updateBlock the block for which the stake update is being recorded
* @param serveUntilBlock the block until which the operator's stake at updateBlock is slashable
* @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after
* @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions,
* but it is anticipated to be rare and not detrimental.
*/
function recordStakeUpdate(
address operator,
uint32 updateBlock,
uint32 serveUntilBlock,
uint256 insertAfter
) external;
/**
* @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration
* is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param serveUntilBlock the block until which the operator's stake at the current block is slashable
* @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to
* slash `operator` once `serveUntil` is reached
*/
function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilBlock) external;
/// @notice The StrategyManager contract of EigenLayer
function strategyManager() external view returns (IStrategyManager);
/// @notice The DelegationManager contract of EigenLayer
function delegation() external view returns (IDelegationManager);
/**
* @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to
* slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed
* and the staker's status is reset (to 'unfrozen').
* @param staker The staker of interest.
* @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated
* to an operator who has their status set to frozen. Otherwise returns 'false'.
*/
function isFrozen(address staker) external view returns (bool);
/// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
function canSlash(address toBeSlashed, address slashingContract) external view returns (bool);
/// @notice Returns the block until which `serviceContract` is allowed to slash the `operator`.
function contractCanSlashOperatorUntilBlock(
address operator,
address serviceContract
) external view returns (uint32);
/// @notice Returns the block at which the `serviceContract` last updated its view of the `operator`'s stake
function latestUpdateBlock(address operator, address serviceContract) external view returns (uint32);
/// @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`.
function getCorrectValueForInsertAfter(address operator, uint32 updateBlock) external view returns (uint256);
/**
* @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartBlock`, with `middlewareTimesIndex` used
* to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `operatorToMiddlewareTimes[operator]`). The specified
* struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal.
* This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartBlock`, *or* in the event
* that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist.
* @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator,
* this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartBlock`.
* @param withdrawalStartBlock The block number at which the withdrawal was initiated.
* @param middlewareTimesIndex Indicates an index in `operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw
* @dev The correct `middlewareTimesIndex` input should be computable off-chain.
*/
function canWithdraw(
address operator,
uint32 withdrawalStartBlock,
uint256 middlewareTimesIndex
) external returns (bool);
/**
* operator =>
* [
* (
* the least recent update block of all of the middlewares it's serving/served,
* latest time that the stake bonded at that update needed to serve until
* )
* ]
*/
function operatorToMiddlewareTimes(
address operator,
uint256 arrayIndex
) external view returns (MiddlewareTimes memory);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator].length`
function middlewareTimesLength(address operator) external view returns (uint256);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].stalestUpdateBlock`.
function getMiddlewareTimesIndexStalestUpdateBlock(address operator, uint32 index) external view returns (uint32);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
function getMiddlewareTimesIndexServeUntilBlock(address operator, uint32 index) external view returns (uint32);
/// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256);
/// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`).
function operatorWhitelistedContractsLinkedListEntry(
address operator,
address node
) external view returns (bool, uint256, uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import "./IETHPOSDeposit.sol";
import "./IStrategyManager.sol";
import "./IEigenPod.sol";
import "./IBeaconChainOracle.sol";
import "./IPausable.sol";
import "./ISlasher.sol";
import "./IStrategy.sol";
/**
* @title Interface for factory that creates and manages solo staking pods that have their withdrawal credentials pointed to EigenLayer.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IEigenPodManager is IPausable {
/// @notice Emitted to notify the update of the beaconChainOracle address
event BeaconOracleUpdated(address indexed newOracleAddress);
/// @notice Emitted to notify the deployment of an EigenPod
event PodDeployed(address indexed eigenPod, address indexed podOwner);
/// @notice Emitted to notify a deposit of beacon chain ETH recorded in the strategy manager
event BeaconChainETHDeposited(address indexed podOwner, uint256 amount);
/// @notice Emitted when the balance of an EigenPod is updated
event PodSharesUpdated(address indexed podOwner, int256 sharesDelta);
/// @notice Emitted when a withdrawal of beacon chain ETH is completed
event BeaconChainETHWithdrawalCompleted(
address indexed podOwner,
uint256 shares,
uint96 nonce,
address delegatedAddress,
address withdrawer,
bytes32 withdrawalRoot
);
event DenebForkTimestampUpdated(uint64 newValue);
/**
* @notice Creates an EigenPod for the sender.
* @dev Function will revert if the `msg.sender` already has an EigenPod.
* @dev Returns EigenPod address
*/
function createPod() external returns (address);
/**
* @notice Stakes for a new beacon chain validator on the sender's EigenPod.
* Also creates an EigenPod for the sender if they don't have one already.
* @param pubkey The 48 bytes public key of the beacon chain validator.
* @param signature The validator's signature of the deposit data.
* @param depositDataRoot The root/hash of the deposit data for the validator's deposit.
*/
function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;
/**
* @notice Changes the `podOwner`'s shares by `sharesDelta` and performs a call to the DelegationManager
* to ensure that delegated shares are also tracked correctly
* @param podOwner is the pod owner whose balance is being updated.
* @param sharesDelta is the change in podOwner's beaconChainETHStrategy shares
* @dev Callable only by the podOwner's EigenPod contract.
* @dev Reverts if `sharesDelta` is not a whole Gwei amount
*/
function recordBeaconChainETHBalanceUpdate(address podOwner, int256 sharesDelta) external;
/**
* @notice Updates the oracle contract that provides the beacon chain state root
* @param newBeaconChainOracle is the new oracle contract being pointed to
* @dev Callable only by the owner of this contract (i.e. governance)
*/
function updateBeaconChainOracle(IBeaconChainOracle newBeaconChainOracle) external;
/// @notice Returns the address of the `podOwner`'s EigenPod if it has been deployed.
function ownerToPod(address podOwner) external view returns (IEigenPod);
/// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
function getPod(address podOwner) external view returns (IEigenPod);
/// @notice The ETH2 Deposit Contract
function ethPOS() external view returns (IETHPOSDeposit);
/// @notice Beacon proxy to which the EigenPods point
function eigenPodBeacon() external view returns (IBeacon);
/// @notice Oracle contract that provides updates to the beacon chain's state
function beaconChainOracle() external view returns (IBeaconChainOracle);
/// @notice Returns the beacon block root at `timestamp`. Reverts if the Beacon block root at `timestamp` has not yet been finalized.
function getBlockRootAtTimestamp(uint64 timestamp) external view returns (bytes32);
/// @notice EigenLayer's StrategyManager contract
function strategyManager() external view returns (IStrategyManager);
/// @notice EigenLayer's Slasher contract
function slasher() external view returns (ISlasher);
/// @notice Returns 'true' if the `podOwner` has created an EigenPod, and 'false' otherwise.
function hasPod(address podOwner) external view returns (bool);
/// @notice Returns the number of EigenPods that have been created
function numPods() external view returns (uint256);
/**
* @notice Mapping from Pod owner owner to the number of shares they have in the virtual beacon chain ETH strategy.
* @dev The share amount can become negative. This is necessary to accommodate the fact that a pod owner's virtual beacon chain ETH shares can
* decrease between the pod owner queuing and completing a withdrawal.
* When the pod owner's shares would otherwise increase, this "deficit" is decreased first _instead_.
* Likewise, when a withdrawal is completed, this "deficit" is decreased and the withdrawal amount is decreased; We can think of this
* as the withdrawal "paying off the deficit".
*/
function podOwnerShares(address podOwner) external view returns (int256);
/// @notice returns canonical, virtual beaconChainETH strategy
function beaconChainETHStrategy() external view returns (IStrategy);
/**
* @notice Used by the DelegationManager to remove a pod owner's shares while they're in the withdrawal queue.
* Simply decreases the `podOwner`'s shares by `shares`, down to a minimum of zero.
* @dev This function reverts if it would result in `podOwnerShares[podOwner]` being less than zero, i.e. it is forbidden for this function to
* result in the `podOwner` incurring a "share deficit". This behavior prevents a Staker from queuing a withdrawal which improperly removes excessive
* shares from the operator to whom the staker is delegated.
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function removeShares(address podOwner, uint256 shares) external;
/**
* @notice Increases the `podOwner`'s shares by `shares`, paying off deficit if possible.
* Used by the DelegationManager to award a pod owner shares on exiting the withdrawal queue
* @dev Returns the number of shares added to `podOwnerShares[podOwner]` above zero, which will be less than the `shares` input
* in the event that the podOwner has an existing shares deficit (i.e. `podOwnerShares[podOwner]` starts below zero)
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function addShares(address podOwner, uint256 shares) external returns (uint256);
/**
* @notice Used by the DelegationManager to complete a withdrawal, sending tokens to some destination address
* @dev Prioritizes decreasing the podOwner's share deficit, if they have one
* @dev Reverts if `shares` is not a whole Gwei amount
*/
function withdrawSharesAsTokens(address podOwner, address destination, uint256 shares) external;
/**
* @notice the deneb hard fork timestamp used to determine which proof path to use for proving a withdrawal
*/
function denebForkTimestamp() external view returns (uint64);
/**
* setting the deneb hard fork timestamp by the eigenPodManager owner
* @dev this function is designed to be called twice. Once, it is set to type(uint64).max
* prior to the actual deneb fork timestamp being set, and then the second time it is set
* to the actual deneb fork timestamp.
*/
function setDenebForkTimestamp(uint64 newDenebForkTimestamp) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.5.0;
// This interface is designed to be compatible with the Vyper version.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
interface IETHPOSDeposit {
/// @notice A processed deposit event.
event DepositEvent(bytes pubkey, bytes withdrawal_credentials, bytes amount, bytes signature, bytes index);
/// @notice Submit a Phase 0 DepositData object.
/// @param pubkey A BLS12-381 public key.
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
/// @param signature A BLS12-381 signature.
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
/// Used as a protection against malformed input.
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
/// @notice Query the current deposit root hash.
/// @return The deposit root hash.
function get_deposit_root() external view returns (bytes32);
/// @notice Query the current deposit count.
/// @return The deposit count encoded as a little endian 64-bit number.
function get_deposit_count() external view returns (bytes memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "../libraries/BeaconChainProofs.sol";
import "./IEigenPodManager.sol";
import "./IBeaconChainOracle.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title The implementation contract used for restaking beacon chain ETH on EigenLayer
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice The main functionalities are:
* - creating new ETH validators with their withdrawal credentials pointed to this contract
* - proving from beacon chain state roots that withdrawal credentials are pointed to this contract
* - proving from beacon chain state roots the balances of ETH validators with their withdrawal credentials
* pointed to this contract
* - updating aggregate balances in the EigenPodManager
* - withdrawing eth when withdrawals are initiated
* @dev Note that all beacon chain balances are stored as gwei within the beacon chain datastructures. We choose
* to account balances in terms of gwei in the EigenPod contract and convert to wei when making calls to other contracts
*/
interface IEigenPod {
enum VALIDATOR_STATUS {
INACTIVE, // doesnt exist
ACTIVE, // staked on ethpos and withdrawal credentials are pointed to the EigenPod
WITHDRAWN // withdrawn from the Beacon Chain
}
struct ValidatorInfo {
// index of the validator in the beacon chain
uint64 validatorIndex;
// amount of beacon chain ETH restaked on EigenLayer in gwei
uint64 restakedBalanceGwei;
//timestamp of the validator's most recent balance update
uint64 mostRecentBalanceUpdateTimestamp;
// status of the validator
VALIDATOR_STATUS status;
}
/**
* @notice struct used to store amounts related to proven withdrawals in memory. Used to help
* manage stack depth and optimize the number of external calls, when batching withdrawal operations.
*/
struct VerifiedWithdrawal {
// amount to send to a podOwner from a proven withdrawal
uint256 amountToSendGwei;
// difference in shares to be recorded in the eigenPodManager, as a result of the withdrawal
int256 sharesDeltaGwei;
}
enum PARTIAL_WITHDRAWAL_CLAIM_STATUS {
REDEEMED,
PENDING,
FAILED
}
/// @notice Emitted when an ETH validator stakes via this eigenPod
event EigenPodStaked(bytes pubkey);
/// @notice Emitted when an ETH validator's withdrawal credentials are successfully verified to be pointed to this eigenPod
event ValidatorRestaked(uint40 validatorIndex);
/// @notice Emitted when an ETH validator's balance is proven to be updated. Here newValidatorBalanceGwei
// is the validator's balance that is credited on EigenLayer.
event ValidatorBalanceUpdated(uint40 validatorIndex, uint64 balanceTimestamp, uint64 newValidatorBalanceGwei);
/// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain
event FullWithdrawalRedeemed(
uint40 validatorIndex,
uint64 withdrawalTimestamp,
address indexed recipient,
uint64 withdrawalAmountGwei
);
/// @notice Emitted when a partial withdrawal claim is successfully redeemed
event PartialWithdrawalRedeemed(
uint40 validatorIndex,
uint64 withdrawalTimestamp,
address indexed recipient,
uint64 partialWithdrawalAmountGwei
);
/// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod.
event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount);
/// @notice Emitted when podOwner enables restaking
event RestakingActivated(address indexed podOwner);
/// @notice Emitted when ETH is received via the `receive` fallback
event NonBeaconChainETHReceived(uint256 amountReceived);
/// @notice Emitted when ETH that was previously received via the `receive` fallback is withdrawn
event NonBeaconChainETHWithdrawn(address indexed recipient, uint256 amountWithdrawn);
/// @notice The max amount of eth, in gwei, that can be restaked per validator
function MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() external view returns (uint64);
/// @notice the amount of execution layer ETH in this contract that is staked in EigenLayer (i.e. withdrawn from beaconchain but not EigenLayer),
function withdrawableRestakedExecutionLayerGwei() external view returns (uint64);
/// @notice any ETH deposited into the EigenPod contract via the `receive` fallback function
function nonBeaconChainETHBalanceWei() external view returns (uint256);
/// @notice Used to initialize the pointers to contracts crucial to the pod's functionality, in beacon proxy construction from EigenPodManager
function initialize(address owner) external;
/// @notice Called by EigenPodManager when the owner wants to create another ETH validator.
function stake(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) external payable;
/**
* @notice Transfers `amountWei` in ether from this contract to the specified `recipient` address
* @notice Called by EigenPodManager to withdrawBeaconChainETH that has been added to the EigenPod's balance due to a withdrawal from the beacon chain.
* @dev The podOwner must have already proved sufficient withdrawals, so that this pod's `withdrawableRestakedExecutionLayerGwei` exceeds the
* `amountWei` input (when converted to GWEI).
* @dev Reverts if `amountWei` is not a whole Gwei amount
*/
function withdrawRestakedBeaconChainETH(address recipient, uint256 amount) external;
/// @notice The single EigenPodManager for EigenLayer
function eigenPodManager() external view returns (IEigenPodManager);
/// @notice The owner of this EigenPod
function podOwner() external view returns (address);
/// @notice an indicator of whether or not the podOwner has ever "fully restaked" by successfully calling `verifyCorrectWithdrawalCredentials`.
function hasRestaked() external view returns (bool);
/**
* @notice The latest timestamp at which the pod owner withdrew the balance of the pod, via calling `withdrawBeforeRestaking`.
* @dev This variable is only updated when the `withdrawBeforeRestaking` function is called, which can only occur before `hasRestaked` is set to true for this pod.
* Proofs for this pod are only valid against Beacon Chain state roots corresponding to timestamps after the stored `mostRecentWithdrawalTimestamp`.
*/
function mostRecentWithdrawalTimestamp() external view returns (uint64);
/// @notice Returns the validatorInfo struct for the provided pubkeyHash
function validatorPubkeyHashToInfo(bytes32 validatorPubkeyHash) external view returns (ValidatorInfo memory);
/// @notice Returns the validatorInfo struct for the provided pubkey
function validatorPubkeyToInfo(bytes calldata validatorPubkey) external view returns (ValidatorInfo memory);
///@notice mapping that tracks proven withdrawals
function provenWithdrawal(bytes32 validatorPubkeyHash, uint64 slot) external view returns (bool);
/// @notice This returns the status of a given validator
function validatorStatus(bytes32 pubkeyHash) external view returns (VALIDATOR_STATUS);
/// @notice This returns the status of a given validator pubkey
function validatorStatus(bytes calldata validatorPubkey) external view returns (VALIDATOR_STATUS);
/**
* @notice This function verifies that the withdrawal credentials of validator(s) owned by the podOwner are pointed to
* this contract. It also verifies the effective balance of the validator. It verifies the provided proof of the ETH validator against the beacon chain state
* root, marks the validator as 'active' in EigenLayer, and credits the restaked ETH in Eigenlayer.
* @param oracleTimestamp is the Beacon Chain timestamp whose state root the `proof` will be proven against.
* @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
* @param withdrawalCredentialProofs is an array of proofs, where each proof proves each ETH validator's balance and withdrawal credentials
* against a beacon chain state root
* @param validatorFields are the fields of the "Validator Container", refer to consensus specs
* for details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
*/
function verifyWithdrawalCredentials(
uint64 oracleTimestamp,
BeaconChainProofs.StateRootProof calldata stateRootProof,
uint40[] calldata validatorIndices,
bytes[] calldata withdrawalCredentialProofs,
bytes32[][] calldata validatorFields
)
external;
/**
* @notice This function records an update (either increase or decrease) in the pod's balance in the StrategyManager.
It also verifies a merkle proof of the validator's current beacon chain balance.
* @param oracleTimestamp The oracleTimestamp whose state root the `proof` will be proven against.
* Must be within `VERIFY_BALANCE_UPDATE_WINDOW_SECONDS` of the current block.
* @param validatorIndices is the list of indices of the validators being proven, refer to consensus specs
* @param validatorFieldsProofs proofs against the `beaconStateRoot` for each validator in `validatorFields`
* @param validatorFields are the fields of the "Validator Container", refer to consensus specs
* @dev For more details on the Beacon Chain spec, see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
*/
function verifyBalanceUpdates(
uint64 oracleTimestamp,
uint40[] calldata validatorIndices,
BeaconChainProofs.StateRootProof calldata stateRootProof,
bytes[] calldata validatorFieldsProofs,
bytes32[][] calldata validatorFields
) external;
/**
* @notice This function records full and partial withdrawals on behalf of one of the Ethereum validators for this EigenPod
* @param oracleTimestamp is the timestamp of the oracle slot that the withdrawal is being proven against
* @param withdrawalProofs is the information needed to check the veracity of the block numbers and withdrawals being proven
* @param validatorFieldsProofs is the proof of the validator's fields' in the validator tree
* @param withdrawalFields are the fields of the withdrawals being proven
* @param validatorFields are the fields of the validators being proven
*/
function verifyAndProcessWithdrawals(
uint64 oracleTimestamp,
BeaconChainProofs.StateRootProof calldata stateRootProof,
BeaconChainProofs.WithdrawalProof[] calldata withdrawalProofs,
bytes[] calldata validatorFieldsProofs,
bytes32[][] calldata validatorFields,
bytes32[][] calldata withdrawalFields
) external;
/**
* @notice Called by the pod owner to activate restaking by withdrawing
* all existing ETH from the pod and preventing further withdrawals via
* "withdrawBeforeRestaking()"
*/
function activateRestaking() external;
/// @notice Called by the pod owner to withdraw the balance of the pod when `hasRestaked` is set to false
function withdrawBeforeRestaking() external;
/// @notice Called by the pod owner to withdraw the nonBeaconChainETHBalanceWei
function withdrawNonBeaconChainETHBalanceWei(address recipient, uint256 amountToWithdraw) external;
/// @notice called by owner of a pod to remove any ERC20s deposited in the pod
function recoverTokens(IERC20[] memory tokenList, uint256[] memory amountsToWithdraw, address recipient) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Interface for the BeaconStateOracle contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IBeaconChainOracle {
/// @notice The block number to state root mapping.
function timestampToBlockRoot(uint256 timestamp) external view returns (bytes32);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "../interfaces/IPauserRegistry.sol";
/**
* @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
* These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
* @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
* Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
* For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
* you can only flip (any number of) switches to off/0 (aka "paused").
* If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
* 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
* 2) update the paused state to this new value
* @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
* indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
*/
interface IPausable {
/// @notice Emitted when the `pauserRegistry` is set to `newPauserRegistry`.
event PauserRegistrySet(IPauserRegistry pauserRegistry, IPauserRegistry newPauserRegistry);
/// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
event Paused(address indexed account, uint256 newPausedStatus);
/// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
event Unpaused(address indexed account, uint256 newPausedStatus);
/// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
function pauserRegistry() external view returns (IPauserRegistry);
/**
* @notice This function is used to pause an EigenLayer contract's functionality.
* It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
*/
function pause(uint256 newPausedStatus) external;
/**
* @notice Alias for `pause(type(uint256).max)`.
*/
function pauseAll() external;
/**
* @notice This function is used to unpause an EigenLayer contract's functionality.
* It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
*/
function unpause(uint256 newPausedStatus) external;
/// @notice Returns the current paused status as a uint256.
function paused() external view returns (uint256);
/// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
function paused(uint8 index) external view returns (bool);
/// @notice Allows the unpauser to set a new pauser registry
function setPauserRegistry(IPauserRegistry newPauserRegistry) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "./Merkle.sol";
import "../libraries/Endian.sol";
//Utility library for parsing and PHASE0 beacon chain block headers
//SSZ Spec: https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization
//BeaconBlockHeader Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
//BeaconState Spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconstate
library BeaconChainProofs {
// constants are the number of fields and the heights of the different merkle trees used in merkleizing beacon chain containers
uint256 internal constant BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT = 3;
uint256 internal constant BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT = 4;
uint256 internal constant BEACON_STATE_FIELD_TREE_HEIGHT = 5;
uint256 internal constant VALIDATOR_FIELD_TREE_HEIGHT = 3;
//Note: changed in the deneb hard fork from 4->5
uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB = 5;
uint256 internal constant EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA = 4;
// SLOTS_PER_HISTORICAL_ROOT = 2**13, so tree height is 13
uint256 internal constant BLOCK_ROOTS_TREE_HEIGHT = 13;
//HISTORICAL_ROOTS_LIMIT = 2**24, so tree height is 24
uint256 internal constant HISTORICAL_SUMMARIES_TREE_HEIGHT = 24;
//Index of block_summary_root in historical_summary container
uint256 internal constant BLOCK_SUMMARY_ROOT_INDEX = 0;
// tree height for hash tree of an individual withdrawal container
uint256 internal constant WITHDRAWAL_FIELD_TREE_HEIGHT = 2;
uint256 internal constant VALIDATOR_TREE_HEIGHT = 40;
// MAX_WITHDRAWALS_PER_PAYLOAD = 2**4, making tree height = 4
uint256 internal constant WITHDRAWALS_TREE_HEIGHT = 4;
//in beacon block body https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconblockbody
uint256 internal constant EXECUTION_PAYLOAD_INDEX = 9;
// in beacon block header https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader
uint256 internal constant SLOT_INDEX = 0;
uint256 internal constant STATE_ROOT_INDEX = 3;
uint256 internal constant BODY_ROOT_INDEX = 4;
// in beacon state https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate
uint256 internal constant VALIDATOR_TREE_ROOT_INDEX = 11;
uint256 internal constant HISTORICAL_SUMMARIES_INDEX = 27;
// in validator https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator
uint256 internal constant VALIDATOR_PUBKEY_INDEX = 0;
uint256 internal constant VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX = 1;
uint256 internal constant VALIDATOR_BALANCE_INDEX = 2;
uint256 internal constant VALIDATOR_WITHDRAWABLE_EPOCH_INDEX = 7;
// in execution payload header
uint256 internal constant TIMESTAMP_INDEX = 9;
//in execution payload
uint256 internal constant WITHDRAWALS_INDEX = 14;
// in withdrawal
uint256 internal constant WITHDRAWAL_VALIDATOR_INDEX_INDEX = 1;
uint256 internal constant WITHDRAWAL_VALIDATOR_AMOUNT_INDEX = 3;
//Misc Constants
/// @notice The number of slots each epoch in the beacon chain
uint64 internal constant SLOTS_PER_EPOCH = 32;
/// @notice The number of seconds in a slot in the beacon chain
uint64 internal constant SECONDS_PER_SLOT = 12;
/// @notice Number of seconds per epoch: 384 == 32 slots/epoch * 12 seconds/slot
uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT;
bytes8 internal constant UINT64_MASK = 0xffffffffffffffff;
/// @notice This struct contains the merkle proofs and leaves needed to verify a partial/full withdrawal
struct WithdrawalProof {
bytes withdrawalProof;
bytes slotProof;
bytes executionPayloadProof;
bytes timestampProof;
bytes historicalSummaryBlockRootProof;
uint64 blockRootIndex;
uint64 historicalSummaryIndex;
uint64 withdrawalIndex;
bytes32 blockRoot;
bytes32 slotRoot;
bytes32 timestampRoot;
bytes32 executionPayloadRoot;
}
/// @notice This struct contains the root and proof for verifying the state root against the oracle block root
struct StateRootProof {
bytes32 beaconStateRoot;
bytes proof;
}
/**
* @notice This function verifies merkle proofs of the fields of a certain validator against a beacon chain state root
* @param validatorIndex the index of the proven validator
* @param beaconStateRoot is the beacon chain state root to be proven against.
* @param validatorFieldsProof is the data used in proving the validator's fields
* @param validatorFields the claimed fields of the validator
*/
function verifyValidatorFields(
bytes32 beaconStateRoot,
bytes32[] calldata validatorFields,
bytes calldata validatorFieldsProof,
uint40 validatorIndex
) internal view {
require(
validatorFields.length == 2 ** VALIDATOR_FIELD_TREE_HEIGHT,
"BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length"
);
/**
* Note: the length of the validator merkle proof is BeaconChainProofs.VALIDATOR_TREE_HEIGHT + 1.
* There is an additional layer added by hashing the root with the length of the validator list
*/
require(
validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyValidatorFields: Proof has incorrect length"
);
uint256 index = (VALIDATOR_TREE_ROOT_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex);
// merkleize the validatorFields to get the leaf to prove
bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields);
// verify the proof of the validatorRoot against the beaconStateRoot
require(
Merkle.verifyInclusionSha256({
proof: validatorFieldsProof,
root: beaconStateRoot,
leaf: validatorRoot,
index: index
}),
"BeaconChainProofs.verifyValidatorFields: Invalid merkle proof"
);
}
/**
* @notice This function verifies the latestBlockHeader against the state root. the latestBlockHeader is
* a tracked in the beacon state.
* @param beaconStateRoot is the beacon chain state root to be proven against.
* @param stateRootProof is the provided merkle proof
* @param latestBlockRoot is hashtree root of the latest block header in the beacon state
*/
function verifyStateRootAgainstLatestBlockRoot(
bytes32 latestBlockRoot,
bytes32 beaconStateRoot,
bytes calldata stateRootProof
) internal view {
require(
stateRootProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Proof has incorrect length"
);
//Next we verify the slot against the blockRoot
require(
Merkle.verifyInclusionSha256({
proof: stateRootProof,
root: latestBlockRoot,
leaf: beaconStateRoot,
index: STATE_ROOT_INDEX
}),
"BeaconChainProofs.verifyStateRootAgainstLatestBlockRoot: Invalid latest block header root merkle proof"
);
}
/**
* @notice This function verifies the slot and the withdrawal fields for a given withdrawal
* @param withdrawalProof is the provided set of merkle proofs
* @param withdrawalFields is the serialized withdrawal container to be proven
*/
function verifyWithdrawal(
bytes32 beaconStateRoot,
bytes32[] calldata withdrawalFields,
WithdrawalProof calldata withdrawalProof,
uint64 denebForkTimestamp
) internal view {
require(
withdrawalFields.length == 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: withdrawalFields has incorrect length"
);
require(
withdrawalProof.blockRootIndex < 2 ** BLOCK_ROOTS_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: blockRootIndex is too large"
);
require(
withdrawalProof.withdrawalIndex < 2 ** WITHDRAWALS_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: withdrawalIndex is too large"
);
require(
withdrawalProof.historicalSummaryIndex < 2 ** HISTORICAL_SUMMARIES_TREE_HEIGHT,
"BeaconChainProofs.verifyWithdrawal: historicalSummaryIndex is too large"
);
//Note: post deneb hard fork, the number of exection payload header fields increased from 15->17, adding an extra level to the tree height
uint256 executionPayloadHeaderFieldTreeHeight = (getWithdrawalTimestamp(withdrawalProof) < denebForkTimestamp) ? EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_CAPELLA : EXECUTION_PAYLOAD_HEADER_FIELD_TREE_HEIGHT_DENEB;
require(
withdrawalProof.withdrawalProof.length ==
32 * (executionPayloadHeaderFieldTreeHeight + WITHDRAWALS_TREE_HEIGHT + 1),
"BeaconChainProofs.verifyWithdrawal: withdrawalProof has incorrect length"
);
require(
withdrawalProof.executionPayloadProof.length ==
32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT + BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyWithdrawal: executionPayloadProof has incorrect length"
);
require(
withdrawalProof.slotProof.length == 32 * (BEACON_BLOCK_HEADER_FIELD_TREE_HEIGHT),
"BeaconChainProofs.verifyWithdrawal: slotProof has incorrect length"
);
require(
withdrawalProof.timestampProof.length == 32 * (executionPayloadHeaderFieldTreeHeight),
"BeaconChainProofs.verifyWithdrawal: timestampProof has incorrect length"
);
require(
withdrawalProof.historicalSummaryBlockRootProof.length ==
32 *
(BEACON_STATE_FIELD_TREE_HEIGHT +
(HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) +
1 +
(BLOCK_ROOTS_TREE_HEIGHT)),
"BeaconChainProofs.verifyWithdrawal: historicalSummaryBlockRootProof has incorrect length"
);
/**
* Note: Here, the "1" in "1 + (BLOCK_ROOTS_TREE_HEIGHT)" signifies that extra step of choosing the "block_root_summary" within the individual
* "historical_summary". Everywhere else it signifies merkelize_with_mixin, where the length of an array is hashed with the root of the array,
* but not here.
*/
uint256 historicalBlockHeaderIndex = (HISTORICAL_SUMMARIES_INDEX <<
((HISTORICAL_SUMMARIES_TREE_HEIGHT + 1) + 1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
(uint256(withdrawalProof.historicalSummaryIndex) << (1 + (BLOCK_ROOTS_TREE_HEIGHT))) |
(BLOCK_SUMMARY_ROOT_INDEX << (BLOCK_ROOTS_TREE_HEIGHT)) |
uint256(withdrawalProof.blockRootIndex);
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.historicalSummaryBlockRootProof,
root: beaconStateRoot,
leaf: withdrawalProof.blockRoot,
index: historicalBlockHeaderIndex
}),
"BeaconChainProofs.verifyWithdrawal: Invalid historicalsummary merkle proof"
);
//Next we verify the slot against the blockRoot
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.slotProof,
root: withdrawalProof.blockRoot,
leaf: withdrawalProof.slotRoot,
index: SLOT_INDEX
}),
"BeaconChainProofs.verifyWithdrawal: Invalid slot merkle proof"
);
{
// Next we verify the executionPayloadRoot against the blockRoot
uint256 executionPayloadIndex = (BODY_ROOT_INDEX << (BEACON_BLOCK_BODY_FIELD_TREE_HEIGHT)) |
EXECUTION_PAYLOAD_INDEX;
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.executionPayloadProof,
root: withdrawalProof.blockRoot,
leaf: withdrawalProof.executionPayloadRoot,
index: executionPayloadIndex
}),
"BeaconChainProofs.verifyWithdrawal: Invalid executionPayload merkle proof"
);
}
// Next we verify the timestampRoot against the executionPayload root
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.timestampProof,
root: withdrawalProof.executionPayloadRoot,
leaf: withdrawalProof.timestampRoot,
index: TIMESTAMP_INDEX
}),
"BeaconChainProofs.verifyWithdrawal: Invalid timestamp merkle proof"
);
{
/**
* Next we verify the withdrawal fields against the executionPayloadRoot:
* First we compute the withdrawal_index, then we merkleize the
* withdrawalFields container to calculate the withdrawalRoot.
*
* Note: Merkleization of the withdrawals root tree uses MerkleizeWithMixin, i.e., the length of the array is hashed with the root of
* the array. Thus we shift the WITHDRAWALS_INDEX over by WITHDRAWALS_TREE_HEIGHT + 1 and not just WITHDRAWALS_TREE_HEIGHT.
*/
uint256 withdrawalIndex = (WITHDRAWALS_INDEX << (WITHDRAWALS_TREE_HEIGHT + 1)) |
uint256(withdrawalProof.withdrawalIndex);
bytes32 withdrawalRoot = Merkle.merkleizeSha256(withdrawalFields);
require(
Merkle.verifyInclusionSha256({
proof: withdrawalProof.withdrawalProof,
root: withdrawalProof.executionPayloadRoot,
leaf: withdrawalRoot,
index: withdrawalIndex
}),
"BeaconChainProofs.verifyWithdrawal: Invalid withdrawal merkle proof"
);
}
}
/**
* @notice This function replicates the ssz hashing of a validator's pubkey, outlined below:
* hh := ssz.NewHasher()
* hh.PutBytes(validatorPubkey[:])
* validatorPubkeyHash := hh.Hash()
* hh.Reset()
*/
function hashValidatorBLSPubkey(bytes memory validatorPubkey) internal pure returns (bytes32 pubkeyHash) {
require(validatorPubkey.length == 48, "Input should be 48 bytes in length");
return sha256(abi.encodePacked(validatorPubkey, bytes16(0)));
}
/**
* @dev Retrieve the withdrawal timestamp
*/
function getWithdrawalTimestamp(WithdrawalProof memory withdrawalProof) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(withdrawalProof.timestampRoot);
}
/**
* @dev Converts the withdrawal's slot to an epoch
*/
function getWithdrawalEpoch(WithdrawalProof memory withdrawalProof) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(withdrawalProof.slotRoot) / SLOTS_PER_EPOCH;
}
/**
* Indices for validator fields (refer to consensus specs):
* 0: pubkey
* 1: withdrawal credentials
* 2: effective balance
* 3: slashed?
* 4: activation elligibility epoch
* 5: activation epoch
* 6: exit epoch
* 7: withdrawable epoch
*/
/**
* @dev Retrieves a validator's pubkey hash
*/
function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) {
return
validatorFields[VALIDATOR_PUBKEY_INDEX];
}
function getWithdrawalCredentials(bytes32[] memory validatorFields) internal pure returns (bytes32) {
return
validatorFields[VALIDATOR_WITHDRAWAL_CREDENTIALS_INDEX];
}
/**
* @dev Retrieves a validator's effective balance (in gwei)
*/
function getEffectiveBalanceGwei(bytes32[] memory validatorFields) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_BALANCE_INDEX]);
}
/**
* @dev Retrieves a validator's withdrawable epoch
*/
function getWithdrawableEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_WITHDRAWABLE_EPOCH_INDEX]);
}
/**
* Indices for withdrawal fields (refer to consensus specs):
* 0: withdrawal index
* 1: validator index
* 2: execution address
* 3: withdrawal amount
*/
/**
* @dev Retrieves a withdrawal's validator index
*/
function getValidatorIndex(bytes32[] memory withdrawalFields) internal pure returns (uint40) {
return
uint40(Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_INDEX_INDEX]));
}
/**
* @dev Retrieves a withdrawal's withdrawal amount (in gwei)
*/
function getWithdrawalAmountGwei(bytes32[] memory withdrawalFields) internal pure returns (uint64) {
return
Endian.fromLittleEndianUint64(withdrawalFields[WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/**
* @title Interface for the `PauserRegistry` contract.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
*/
interface IPauserRegistry {
event PauserStatusChanged(address pauser, bool canPause);
event UnpauserChanged(address previousUnpauser, address newUnpauser);
/// @notice Mapping of addresses to whether they hold the pauser role.
function isPauser(address pauser) external view returns (bool);
/// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
function unpauser() external view returns (address);
}// SPDX-License-Identifier: MIT
// Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates merkle trees that are safe
* against this attack out of the box.
*/
library Merkle {
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* Note this is for a Merkle tree using the keccak/sha3 hash function
*/
function verifyInclusionKeccak(
bytes memory proof,
bytes32 root,
bytes32 leaf,
uint256 index
) internal pure returns (bool) {
return processInclusionProofKeccak(proof, leaf, index) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
* @dev If the proof length is 0 then the leaf hash is returned.
*
* _Available since v4.4._
*
* Note this is for a Merkle tree using the keccak/sha3 hash function
*/
function processInclusionProofKeccak(
bytes memory proof,
bytes32 leaf,
uint256 index
) internal pure returns (bytes32) {
require(
proof.length % 32 == 0,
"Merkle.processInclusionProofKeccak: proof length should be a multiple of 32"
);
bytes32 computedHash = leaf;
for (uint256 i = 32; i <= proof.length; i += 32) {
if (index % 2 == 0) {
// if ith bit of index is 0, then computedHash is a left sibling
assembly {
mstore(0x00, computedHash)
mstore(0x20, mload(add(proof, i)))
computedHash := keccak256(0x00, 0x40)
index := div(index, 2)
}
} else {
// if ith bit of index is 1, then computedHash is a right sibling
assembly {
mstore(0x00, mload(add(proof, i)))
mstore(0x20, computedHash)
computedHash := keccak256(0x00, 0x40)
index := div(index, 2)
}
}
}
return computedHash;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* Note this is for a Merkle tree using the sha256 hash function
*/
function verifyInclusionSha256(
bytes memory proof,
bytes32 root,
bytes32 leaf,
uint256 index
) internal view returns (bool) {
return processInclusionProofSha256(proof, leaf, index) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. The tree is built assuming `leaf` is
* the 0 indexed `index`'th leaf from the bottom left of the tree.
*
* _Available since v4.4._
*
* Note this is for a Merkle tree using the sha256 hash function
*/
function processInclusionProofSha256(
bytes memory proof,
bytes32 leaf,
uint256 index
) internal view returns (bytes32) {
require(
proof.length != 0 && proof.length % 32 == 0,
"Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32"
);
bytes32[1] memory computedHash = [leaf];
for (uint256 i = 32; i <= proof.length; i += 32) {
if (index % 2 == 0) {
// if ith bit of index is 0, then computedHash is a left sibling
assembly {
mstore(0x00, mload(computedHash))
mstore(0x20, mload(add(proof, i)))
if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) {
revert(0, 0)
}
index := div(index, 2)
}
} else {
// if ith bit of index is 1, then computedHash is a right sibling
assembly {
mstore(0x00, mload(add(proof, i)))
mstore(0x20, mload(computedHash))
if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) {
revert(0, 0)
}
index := div(index, 2)
}
}
}
return computedHash[0];
}
/**
@notice this function returns the merkle root of a tree created from a set of leaves using sha256 as its hash function
@param leaves the leaves of the merkle tree
@return The computed Merkle root of the tree.
@dev A pre-condition to this function is that leaves.length is a power of two. If not, the function will merkleize the inputs incorrectly.
*/
function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) {
//there are half as many nodes in the layer above the leaves
uint256 numNodesInLayer = leaves.length / 2;
//create a layer to store the internal nodes
bytes32[] memory layer = new bytes32[](numNodesInLayer);
//fill the layer with the pairwise hashes of the leaves
for (uint256 i = 0; i < numNodesInLayer; i++) {
layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1]));
}
//the next layer above has half as many nodes
numNodesInLayer /= 2;
//while we haven't computed the root
while (numNodesInLayer != 0) {
//overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children
for (uint256 i = 0; i < numNodesInLayer; i++) {
layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1]));
}
//the next layer above has half as many nodes
numNodesInLayer /= 2;
}
//the first node in the layer is the root
return layer[0];
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library Endian {
/**
* @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64
* @param lenum little endian-formatted uint64 input, provided as 'bytes32' type
* @return n The big endian-formatted uint64
* @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits)
* through a right-shift/shr operation.
*/
function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) {
// the number needs to be stored in little-endian encoding (ie in bytes 0-8)
n = uint64(uint256(lenum >> 192));
return
(n >> 56) |
((0x00FF000000000000 & n) >> 40) |
((0x0000FF0000000000 & n) >> 24) |
((0x000000FF00000000 & n) >> 8) |
((0x00000000FF000000 & n) << 8) |
((0x0000000000FF0000 & n) << 24) |
((0x000000000000FF00 & n) << 40) |
((0x00000000000000FF & n) << 56);
}
}{
"remappings": [
"eigenlayer-middleware/=lib/eigenlayer-middleware/src/",
"eigenlayer-core/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/src/",
"eigenlayer-core-contracts/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/src/contracts/core/",
"eigenlayer-scripts/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/script/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts/=lib/eigenlayer-middleware/lib/openzeppelin-contracts/contracts/",
"@openzeppelin-upgrades/contracts/=lib/eigenlayer-middleware/lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin-upgrades-v4.9.0/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
"@openzeppelin-v4.9.0/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"ds-test/=lib/eigenlayer-middleware/lib/ds-test/src/",
"eigenlayer-contracts/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"openzeppelin-contracts-upgradeable-v4.9.0/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts-v4.9.0/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/eigenlayer-middleware/lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IRegistryCoordinator","name":"_registryCoordinator","type":"address"},{"internalType":"contract IDelegationManager","name":"_delegationManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"minimumStake","type":"uint96"}],"name":"MinimumStakeForQuorumUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"indexed":false,"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"indexed":false,"internalType":"uint96","name":"stake","type":"uint96"}],"name":"OperatorStakeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"QuorumCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"}],"name":"StrategyAddedToQuorum","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"multiplier","type":"uint256"}],"name":"StrategyMultiplierUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"}],"name":"StrategyRemovedFromQuorum","type":"event"},{"inputs":[],"name":"MAX_WEIGHING_FUNCTION_LENGTH","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WEIGHTING_DIVISOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"components":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"internalType":"uint96","name":"multiplier","type":"uint96"}],"internalType":"struct IStakeRegistry.StrategyParams[]","name":"_strategyParams","type":"tuple[]"}],"name":"addStrategies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegation","outputs":[{"internalType":"contract IDelegationManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"bytes","name":"quorumNumbers","type":"bytes"}],"name":"deregisterOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"getCurrentStake","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"getCurrentTotalStake","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"getLatestStakeUpdate","outputs":[{"components":[{"internalType":"uint32","name":"updateBlockNumber","type":"uint32"},{"internalType":"uint32","name":"nextUpdateBlockNumber","type":"uint32"},{"internalType":"uint96","name":"stake","type":"uint96"}],"internalType":"struct IStakeRegistry.StakeUpdate","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"getStakeAtBlockNumber","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getStakeAtBlockNumberAndIndex","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"getStakeHistory","outputs":[{"components":[{"internalType":"uint32","name":"updateBlockNumber","type":"uint32"},{"internalType":"uint32","name":"nextUpdateBlockNumber","type":"uint32"},{"internalType":"uint96","name":"stake","type":"uint96"}],"internalType":"struct IStakeRegistry.StakeUpdate[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"getStakeHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getStakeUpdateAtIndex","outputs":[{"components":[{"internalType":"uint32","name":"updateBlockNumber","type":"uint32"},{"internalType":"uint32","name":"nextUpdateBlockNumber","type":"uint32"},{"internalType":"uint96","name":"stake","type":"uint96"}],"internalType":"struct IStakeRegistry.StakeUpdate","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"getStakeUpdateIndexAtBlockNumber","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getTotalStakeAtBlockNumberFromIndex","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"getTotalStakeHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes","name":"quorumNumbers","type":"bytes"}],"name":"getTotalStakeIndicesAtBlockNumber","outputs":[{"internalType":"uint32[]","name":"","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getTotalStakeUpdateAtIndex","outputs":[{"components":[{"internalType":"uint32","name":"updateBlockNumber","type":"uint32"},{"internalType":"uint32","name":"nextUpdateBlockNumber","type":"uint32"},{"internalType":"uint96","name":"stake","type":"uint96"}],"internalType":"struct IStakeRegistry.StakeUpdate","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint96","name":"minimumStake","type":"uint96"},{"components":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"internalType":"uint96","name":"multiplier","type":"uint96"}],"internalType":"struct IStakeRegistry.StrategyParams[]","name":"_strategyParams","type":"tuple[]"}],"name":"initializeQuorum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"minimumStakeForQuorum","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint256[]","name":"strategyIndices","type":"uint256[]"},{"internalType":"uint96[]","name":"newMultipliers","type":"uint96[]"}],"name":"modifyStrategyParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"bytes","name":"quorumNumbers","type":"bytes"}],"name":"registerOperator","outputs":[{"internalType":"uint96[]","name":"","type":"uint96[]"},{"internalType":"uint96[]","name":"","type":"uint96[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registryCoordinator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint256[]","name":"indicesToRemove","type":"uint256[]"}],"name":"removeStrategies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint96","name":"minimumStake","type":"uint96"}],"name":"setMinimumStakeForQuorum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"strategiesPerQuorum","outputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"strategyParams","outputs":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"internalType":"uint96","name":"multiplier","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"strategyParamsByIndex","outputs":[{"components":[{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"internalType":"uint96","name":"multiplier","type":"uint96"}],"internalType":"struct IStakeRegistry.StrategyParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"}],"name":"strategyParamsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bytes32","name":"operatorId","type":"bytes32"},{"internalType":"bytes","name":"quorumNumbers","type":"bytes"}],"name":"updateOperatorStake","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"address","name":"operator","type":"address"}],"name":"weightOfOperatorForQuorum","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c060405234801561001057600080fd5b5060405161389c38038061389c83398101604081905261002f9161005e565b6001600160a01b0391821660a05216608052610098565b6001600160a01b038116811461005b57600080fd5b50565b6000806040838503121561007157600080fd5b825161007c81610046565b602084015190925061008d81610046565b809150509250929050565b60805160a0516137a06100fc6000396000818161037a015281816106340152818161096701528181610ccf015281816110f0015281816116d0015281816117d2015281816118eb0152611cad0152600081816105290152611e6e01526137a06000f3fe608060405234801561001057600080fd5b50600436106101e55760003560e01c80639f3ccf651161010f578063c8294c56116100a2578063f2be94ae11610071578063f2be94ae1461054b578063f851e1981461055e578063fa28c62714610571578063ff694a771461058457600080fd5b8063c8294c56146104d6578063d5eccc05146104e9578063dd9846b9146104fc578063df5cf7231461052457600080fd5b8063bc9a40c3116100de578063bc9a40c314610474578063bd29b8cd14610487578063c46778a51461049a578063c601527d146104c357600080fd5b80639f3ccf65146103ee578063ac6bfb0314610401578063adc804da14610421578063b6904b781461046157600080fd5b80634bd26e091161018757806366acfefe1161015657806366acfefe1461034a5780636d14a987146103755780637c172347146103b457806381c07502146103ce57600080fd5b80634bd26e09146102e55780635401ed27146103155780635e5a6775146103285780635f1f2d771461033757600080fd5b806320b66298116101c357806320b662981461026c57806325504777146102815780632cd95940146102a25780633ca5a5f5146102c257600080fd5b80630491b41c146101ea57806308732461146102205780631f9b74e014610241575b600080fd5b61020d6101f8366004612bca565b60ff1660009081526001602052604090205490565b6040519081526020015b60405180910390f35b61023361022e366004612be5565b610597565b604051610217929190612c0f565b61025461024f366004612c49565b6105e0565b6040516001600160601b039091168152602001610217565b61027f61027a366004612cc4565b610632565b005b61029461028f366004612d89565b610959565b604051610217929190612e29565b6102b56102b0366004612e4e565b610c15565b6040516102179190612e7a565b61020d6102d0366004612bca565b60ff1660009081526003602052604090205490565b61020d6102f3366004612e4e565b600091825260026020908152604080842060ff93909316845291905290205490565b610254610323366004612e4e565b610cb4565b61020d670de0b6b3a764000081565b61027f610345366004612f85565b610ccd565b61035d610358366004612d89565b6110e3565b6040516001600160c01b039091168152602001610217565b61039c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610217565b6103bc602081565b60405160ff9091168152602001610217565b6103e16103dc366004613046565b611240565b6040516102179190613098565b61039c6103fc366004612be5565b6114f5565b61041461040f3660046130d6565b61152d565b6040516102179190613109565b61043461042f366004612be5565b6115c5565b6040805182516001600160a01b031681526020928301516001600160601b03169281019290925201610217565b61041461046f366004612be5565b61163f565b61027f61048236600461315a565b6116ce565b61027f610495366004613184565b6117c7565b6102546104a8366004612bca565b6000602081905290815260409020546001600160601b031681565b61027f6104d136600461324f565b6118e9565b6102546104e436600461329c565b6119dd565b6102546104f7366004612bca565b611a5b565b61050f61050a3660046132d9565b611aae565b60405163ffffffff9091168152602001610217565b61039c7f000000000000000000000000000000000000000000000000000000000000000081565b610254610559366004613315565b611ac3565b61041461056c366004612e4e565b611b58565b61025461057f3660046132d9565b611c41565b61027f610592366004613357565b611ca2565b600360205281600052604060002081815481106105b357600080fd5b6000918252602090912001546001600160a01b0381169250600160a01b90046001600160601b0316905082565b60ff8216600090815260016020526040812054839061061a5760405162461bcd60e51b8152600401610611906133b4565b60405180910390fd5b60006106268585611e0d565b509250505b5092915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b49190613405565b6001600160a01b0316336001600160a01b0316146106e45760405162461bcd60e51b815260040161061190613422565b846107008160ff16600090815260016020526040902054151590565b61071c5760405162461bcd60e51b8152600401610611906133b4565b8380610792576040805162461bcd60e51b81526020600482015260248101919091527f5374616b6552656769737472792e6d6f6469667953747261746567795061726160448201527f6d733a206e6f20737472617465677920696e64696365732070726f76696465646064820152608401610611565b8281146108075760405162461bcd60e51b815260206004820152603960248201527f5374616b6552656769737472792e6d6f6469667953747261746567795061726160448201527f6d733a20696e707574206c656e677468206d69736d61746368000000000000006064820152608401610611565b60ff87166000908152600360205260408120905b8281101561094e578585828181106108355761083561349e565b905060200201602081019061084a91906134b4565b8289898481811061085d5761085d61349e565b90506020020135815481106108745761087461349e565b9060005260206000200160000160146101000a8154816001600160601b0302191690836001600160601b031602179055508860ff167f11a5641322da1dff56a4b66eaac31ffa465295ece907cd163437793b4d009a75838a8a858181106108dd576108dd61349e565b90506020020135815481106108f4576108f461349e565b6000918252602090912001546001600160a01b031688888581811061091b5761091b61349e565b905060200201602081019061093091906134b4565b60405161093e929190612c0f565b60405180910390a260010161081b565b505050505050505050565b606080336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109a45760405162461bcd60e51b8152600401610611906134cf565b6000836001600160401b038111156109be576109be612ef4565b6040519080825280602002602001820160405280156109e7578160200160208202803683370190505b5090506000846001600160401b03811115610a0457610a04612ef4565b604051908082528060200260200182016040528015610a2d578160200160208202803683370190505b50905060005b85811015610c07576000878783818110610a4f57610a4f61349e565b919091013560f81c60008181526001602052604090205490925015159050610ad75760405162461bcd60e51b815260206004820152603560248201527f5374616b6552656769737472792e72656769737465724f70657261746f723a206044820152741c5d5bdc9d5b48191bd95cc81b9bdd08195e1a5cdd605a1b6064820152608401610611565b600080610ae4838d611e0d565b9150915080610b815760405162461bcd60e51b815260206004820152605b60248201527f5374616b6552656769737472792e72656769737465724f70657261746f723a2060448201527f4f70657261746f7220646f6573206e6f74206d656574206d696e696d756d207360648201527f74616b6520726571756972656d656e7420666f722071756f72756d0000000000608482015260a401610611565b6000610b8e8c8585612001565b905082878681518110610ba357610ba361349e565b60200260200101906001600160601b031690816001600160601b031681525050610bcd8482612280565b868681518110610bdf57610bdf61349e565b6001600160601b0390921660209283029190910190910152505060019092019150610a339050565b509097909650945050505050565b600082815260026020908152604080832060ff851684528252808320805482518185028101850190935280835260609492939192909184015b82821015610ca7576000848152602090819020604080516060810182529185015463ffffffff8082168452600160201b82041683850152600160401b90046001600160601b031690820152825260019092019101610c4e565b5050505090505b92915050565b600080610cc18484611b58565b60400151949350505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f9190613405565b6001600160a01b0316336001600160a01b031614610d7f5760405162461bcd60e51b815260040161061190613422565b81610d9b8160ff16600090815260016020526040902054151590565b610db75760405162461bcd60e51b8152600401610611906133b4565b815180610e2c5760405162461bcd60e51b815260206004820152603d60248201527f5374616b6552656769737472792e72656d6f7665537472617465676965733a2060448201527f6e6f20696e646963657320746f2072656d6f76652070726f76696465640000006064820152608401610611565b60ff841660009081526003602090815260408083206004909252822090915b838110156110da578660ff167f31fa2e2cd280c9375e13ffcf3d81e2378100186e4058f8d3ddb690b82dcd31f784888481518110610e8b57610e8b61349e565b602002602001015181548110610ea357610ea361349e565b600091825260209182902001546040516001600160a01b0390911681520160405180910390a28660ff167f11a5641322da1dff56a4b66eaac31ffa465295ece907cd163437793b4d009a7584888481518110610f0157610f0161349e565b602002602001015181548110610f1957610f1961349e565b600091825260208083209190910154604080516001600160a01b039092168252918101929092520160405180910390a282548390610f5990600190613557565b81548110610f6957610f6961349e565b9060005260206000200183878381518110610f8657610f8661349e565b602002602001015181548110610f9e57610f9e61349e565b600091825260209091208254910180546001600160a01b0319166001600160a01b03909216918217815591546001600160601b03600160a01b9182900416021790558254839080610ff157610ff161356a565b6000828152602081208201600019908101919091550190558154829061101990600190613557565b815481106110295761102961349e565b9060005260206000200160009054906101000a90046001600160a01b03168287838151811061105a5761105a61349e565b6020026020010151815481106110725761107261349e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818054806110b0576110b061356a565b600082815260209020810160001990810180546001600160a01b0319169055019055600101610e4b565b50505050505050565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461112d5760405162461bcd60e51b8152600401610611906134cf565b6000805b8381101561123657600085858381811061114d5761114d61349e565b919091013560f81c600081815260016020526040902054909250151590506111dd5760405162461bcd60e51b815260206004820152603860248201527f5374616b6552656769737472792e7570646174654f70657261746f725374616b60448201527f653a2071756f72756d20646f6573206e6f7420657869737400000000000000006064820152608401610611565b6000806111ea838b611e0d565b915091508061120c5760009150600160ff84161b6001600160c01b0386161794505b60006112198a8585612001565b90506112258482612280565b505060019093019250611131915050565b5095945050505050565b60606000826001600160401b0381111561125c5761125c612ef4565b604051908082528060200260200182016040528015611285578160200160208202803683370190505b50905060005b838110156114ea5760008585838181106112a7576112a761349e565b919091013560f81c600081815260016020526040902054909250151590506113465760405162461bcd60e51b815260206004820152604660248201527f5374616b6552656769737472792e676574546f74616c5374616b65496e64696360448201527f65734174426c6f636b4e756d6265723a2071756f72756d20646f6573206e6f7460648201526508195e1a5cdd60d21b608482015260a401610611565b60ff81166000908152600160205260408120805463ffffffff8a16929061136f5761136f61349e565b60009182526020909120015463ffffffff16111561141b5760405162461bcd60e51b815260206004820152605b60248201527f5374616b6552656769737472792e676574546f74616c5374616b65496e64696360448201527f65734174426c6f636b4e756d6265723a2071756f72756d20686173206e6f207360648201527f74616b6520686973746f727920617420626c6f636b4e756d6265720000000000608482015260a401610611565b60ff8116600090815260016020526040812054905b818110156114df5760ff8316600090815260016020819052604090912063ffffffff8b169161145f8486613557565b6114699190613557565b815481106114795761147961349e565b60009182526020909120015463ffffffff16116114d757600161149c8284613557565b6114a69190613557565b8585815181106114b8576114b861349e565b602002602001019063ffffffff16908163ffffffff16815250506114df565b600101611430565b50505060010161128b565b5090505b9392505050565b6004602052816000526040600020818154811061151157600080fd5b6000918252602090912001546001600160a01b03169150829050565b60408051606081018252600080825260208083018290528284018290528582526002815283822060ff881683529052919091208054839081106115725761157261349e565b600091825260209182902060408051606081018252929091015463ffffffff8082168452600160201b82041693830193909352600160401b9092046001600160601b031691810191909152949350505050565b604080518082019091526000808252602082015260ff831660009081526003602052604090208054839081106115fd576115fd61349e565b6000918252602091829020604080518082019091529101546001600160a01b0381168252600160a01b90046001600160601b0316918101919091529392505050565b604080516060810182526000808252602080830182905282840182905260ff86168252600190529190912080548390811061167c5761167c61349e565b600091825260209182902060408051606081018252929091015463ffffffff8082168452600160201b82041693830193909352600160401b9092046001600160601b0316918101919091529392505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561172c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117509190613405565b6001600160a01b0316336001600160a01b0316146117805760405162461bcd60e51b815260040161061190613422565b8161179c8160ff16600090815260016020526040902054151590565b6117b85760405162461bcd60e51b8152600401610611906133b4565b6117c283836123f7565b505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461180f5760405162461bcd60e51b8152600401610611906134cf565b60005b818110156118e357600083838381811061182e5761182e61349e565b919091013560f81c600081815260016020526040902054909250151590506118be5760405162461bcd60e51b815260206004820152603760248201527f5374616b6552656769737472792e646572656769737465724f70657261746f7260448201527f3a2071756f72756d20646f6573206e6f742065786973740000000000000000006064820152608401610611565b60006118cc86836000612001565b90506118d88282612280565b505050600101611812565b50505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611947573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196b9190613405565b6001600160a01b0316336001600160a01b03161461199b5760405162461bcd60e51b815260040161061190613422565b816119b78160ff16600090815260016020526040902054151590565b6119d35760405162461bcd60e51b8152600401610611906133b4565b6117c28383612460565b60ff83166000908152600160205260408120805482919084908110611a0457611a0461349e565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b90930492909216908201529050610cc1818561288e565b60ff81166000908152600160208190526040822080549091611a7c91613557565b81548110611a8c57611a8c61349e565b600091825260209091200154600160401b90046001600160601b031692915050565b6000611abb848484612a08565b949350505050565b600082815260026020908152604080832060ff881684529091528120805482919084908110611af457611af461349e565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b90930492909216908201529050611b4b818661288e565b6040015195945050505050565b6040805160608082018352600080835260208084018290528385018290528682526002815284822060ff8716835281528482205485519384018652828452908301829052938201819052919291829003611bb5579150610cae9050565b600085815260026020908152604080832060ff881684529091529020611bdc600184613557565b81548110611bec57611bec61349e565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b90930492909216908201529250610cae915050565b600083815260026020908152604080832060ff861684529091528120611c68858585612a08565b63ffffffff1681548110611c7e57611c7e61349e565b600091825260209091200154600160401b90046001600160601b0316949350505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611cea5760405162461bcd60e51b8152600401610611906134cf565b60ff831660009081526001602052604090205415611d685760405162461bcd60e51b815260206004820152603560248201527f5374616b6552656769737472792e696e697469616c697a6551756f72756d3a2060448201527471756f72756d20616c72656164792065786973747360581b6064820152608401610611565b611d728382612460565b611d7c83836123f7565b505060ff166000908152600160208181526040808420815160608101835263ffffffff438116825281850187815293820187815283549687018455928752939095209451949093018054915193516001600160601b0316600160401b02600160401b600160a01b0319948416600160201b0267ffffffffffffffff1990931695909316949094171791909116179055565b600080600080611e2c8660ff1660009081526003602052604090205490565b604080518082019091526000808252602082015290915060ff871660009081526004602081905260408083209051639004134760e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001692639004134792611ea1928c9201613580565b600060405180830381865afa158015611ebe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ee691908101906135e2565b905060005b83811015611fcd5760ff89166000908152600360205260409020805482908110611f1757611f1761349e565b60009182526020808320604080518082019091529201546001600160a01b0381168352600160a01b90046001600160601b0316908201528351909450839083908110611f6557611f6561349e565b60200260200101511115611fc557670de0b6b3a764000083602001516001600160601b0316838381518110611f9c57611f9c61349e565b6020026020010151611fae919061366d565b611fb89190613684565b611fc290866136a6565b94505b600101611eeb565b50505060ff8616600090815260208190526040902054919350506001600160601b03908116908316101590505b9250929050565b600083815260026020908152604080832060ff8616845290915281205481908082036120c757600086815260026020908152604080832060ff891684528252808320815160608101835263ffffffff43811682528185018681526001600160601b03808c16958401958652845460018101865594885295909620915191909201805495519351909416600160401b02600160401b600160a01b0319938316600160201b0267ffffffffffffffff1990961691909216179390931716919091179055612226565b600086815260026020908152604080832060ff8916845290915281206120ee600184613557565b815481106120fe576120fe61349e565b600091825260209091200180546001600160601b03600160401b909104811694509091508516830361213657600093505050506114ee565b805463ffffffff43811691160361216e578054600160401b600160a01b031916600160401b6001600160601b03871602178155612224565b805467ffffffff000000001916600160201b4363ffffffff90811682810293909317845560008a815260026020908152604080832060ff8d168452825280832081516060810183529687528683018481526001600160601b038d81169389019384528254600181018455928652939094209651960180549351915196851667ffffffffffffffff1990941693909317931690930291909117600160401b600160a01b031916600160401b93909216929092021790555b505b6040805160ff871681526001600160601b038616602082015287917f2f527d527e95d8fe40aec55377743bb779087da3f6d0d08f12e36444da62327d910160405180910390a26122768285612b6e565b9695505050505050565b60ff8216600090815260016020819052604082208054918391906122a49084613557565b815481106122b4576122b461349e565b906000526020600020019050836000036122e25754600160401b90046001600160601b03169150610cae9050565b805460009061230190600160401b90046001600160601b031686612b86565b825490915063ffffffff43811691160361233c578154600160401b600160a01b031916600160401b6001600160601b038316021782556123ee565b815463ffffffff438116600160201b81810267ffffffff000000001990941693909317855560ff8916600090815260016020818152604080842081516060810183529586528583018581526001600160601b03808b169388019384528254958601835591865292909420945194909201805491519251909316600160401b02600160401b600160a01b031992861690960267ffffffffffffffff19909116939094169290921792909217169190911790555b95945050505050565b60ff82166000818152602081815260409182902080546bffffffffffffffffffffffff19166001600160601b03861690811790915591519182527f26eecff2b70b0a71104ff4d940ba7162d23a95c248771fc487a7be17a596b3cf910160405180910390a25050565b60008151116124c55760405162461bcd60e51b8152602060048201526038602482015260008051602061374b83398151915260448201527f3a206e6f20737472617465676965732070726f766964656400000000000000006064820152608401610611565b805160ff8316600090815260036020908152604090912054906124e883836136c5565b11156125585760405162461bcd60e51b8152602060048201526045602482015260008051602061374b83398151915260448201527f3a20657863656564204d41585f5745494748494e475f46554e4354494f4e5f4c60648201526408a9c8ea8960db1b608482015260a401610611565b60005b828110156128875760005b61257082846136c5565b811015612646578482815181106125895761258961349e565b6020026020010151600001516001600160a01b0316600360008860ff1660ff16815260200190815260200160002082815481106125c8576125c861349e565b6000918252602090912001546001600160a01b03160361263e5760405162461bcd60e51b815260206004820152603d602482015260008051602061374b83398151915260448201527f3a2063616e6e6f74206164642073616d652073747261746567792032780000006064820152608401610611565b600101612566565b50600084828151811061265b5761265b61349e565b6020026020010151602001516001600160601b0316116126e05760405162461bcd60e51b8152602060048201526046602482015260008051602061374b83398151915260448201527f3a2063616e6e6f74206164642073747261746567792077697468207a65726f206064820152651dd95a59da1d60d21b608482015260a401610611565b60ff8516600090815260036020526040902084518590839081106127065761270661349e565b602090810291909101810151825460018101845560009384528284208251928401516001600160601b0316600160a01b026001600160a01b039093169290921791015560ff871682526004905260409020845185908390811061276b5761276b61349e565b6020908102919091018101515182546001810184556000938452919092200180546001600160a01b0319166001600160a01b03909216919091179055835160ff8616907f10565e56cacbf32eca267945f054fec02e59750032d113d3302182ad967f5404908690849081106127e2576127e261349e565b602090810291909101810151516040516001600160a01b0390911681520160405180910390a28460ff167f11a5641322da1dff56a4b66eaac31ffa465295ece907cd163437793b4d009a7585838151811061283f5761283f61349e565b60200260200101516000015186848151811061285d5761285d61349e565b602002602001015160200151604051612877929190612c0f565b60405180910390a260010161255b565b5050505050565b816000015163ffffffff168163ffffffff1610156129335760405162461bcd60e51b815260206004820152605660248201527f5374616b6552656769737472792e5f76616c69646174655374616b655570646160448201527f74654174426c6f636b4e756d6265723a207374616b6555706461746520697320606482015275333937b69030b33a32b910313637b1b5a73ab6b132b960511b608482015260a401610611565b602082015163ffffffff1615806129595750816020015163ffffffff168163ffffffff16105b612a045760405162461bcd60e51b815260206004820152606a60248201527f5374616b6552656769737472792e5f76616c69646174655374616b655570646160448201527f74654174426c6f636b4e756d6265723a2074686572652069732061206e65776560648201527f72207374616b6555706461746520617661696c61626c65206265666f726520626084820152693637b1b5a73ab6b132b960b11b60a482015260c401610611565b5050565b600083815260026020908152604080832060ff86168452909152812054805b8015612aa957600086815260026020908152604080832060ff89168452909152902063ffffffff851690612a5c600184613557565b81548110612a6c57612a6c61349e565b60009182526020909120015463ffffffff1611612a9757612a8e600182613557565b925050506114ee565b80612aa1816136d8565b915050612a27565b5060405162461bcd60e51b815260206004820152608160248201527f5374616b6552656769737472792e5f6765745374616b65557064617465496e6460448201527f6578466f724f70657261746f724174426c6f636b4e756d6265723a206e6f207360648201527f74616b652075706461746520666f756e6420666f72206f70657261746f72496460848201527f20616e642071756f72756d4e756d62657220617420626c6f636b206e756d626560a4820152603960f91b60c482015260e401610611565b60006114ee6001600160601b038085169084166136ef565b600080821215612baa57612b998261370f565b612ba3908461372b565b9050610cae565b612ba382846136a6565b803560ff81168114612bc557600080fd5b919050565b600060208284031215612bdc57600080fd5b6114ee82612bb4565b60008060408385031215612bf857600080fd5b612c0183612bb4565b946020939093013593505050565b6001600160a01b039290921682526001600160601b0316602082015260400190565b6001600160a01b0381168114612c4657600080fd5b50565b60008060408385031215612c5c57600080fd5b612c6583612bb4565b91506020830135612c7581612c31565b809150509250929050565b60008083601f840112612c9257600080fd5b5081356001600160401b03811115612ca957600080fd5b6020830191508360208260051b8501011115611ffa57600080fd5b600080600080600060608688031215612cdc57600080fd5b612ce586612bb4565b945060208601356001600160401b03811115612d0057600080fd5b612d0c88828901612c80565b90955093505060408601356001600160401b03811115612d2b57600080fd5b612d3788828901612c80565b969995985093965092949392505050565b60008083601f840112612d5a57600080fd5b5081356001600160401b03811115612d7157600080fd5b602083019150836020828501011115611ffa57600080fd5b60008060008060608587031215612d9f57600080fd5b8435612daa81612c31565b93506020850135925060408501356001600160401b03811115612dcc57600080fd5b612dd887828801612d48565b95989497509550505050565b600081518084526020840193506020830160005b82811015612e1f5781516001600160601b0316865260209586019590910190600101612df8565b5093949350505050565b604081526000612e3c6040830185612de4565b82810360208401526123ee8185612de4565b60008060408385031215612e6157600080fd5b82359150612e7160208401612bb4565b90509250929050565b602080825282518282018190526000918401906040840190835b81811015612ee957612ed383855163ffffffff815116825263ffffffff60208201511660208301526001600160601b0360408201511660408301525050565b6020939093019260609290920191600101612e94565b509095945050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715612f2c57612f2c612ef4565b60405290565b604051601f8201601f191681016001600160401b0381118282101715612f5a57612f5a612ef4565b604052919050565b60006001600160401b03821115612f7b57612f7b612ef4565b5060051b60200190565b60008060408385031215612f9857600080fd5b612fa183612bb4565b915060208301356001600160401b03811115612fbc57600080fd5b8301601f81018513612fcd57600080fd5b8035612fe0612fdb82612f62565b612f32565b8082825260208201915060208360051b85010192508783111561300257600080fd5b6020840193505b82841015613024578335825260209384019390910190613009565b809450505050509250929050565b803563ffffffff81168114612bc557600080fd5b60008060006040848603121561305b57600080fd5b61306484613032565b925060208401356001600160401b0381111561307f57600080fd5b61308b86828701612d48565b9497909650939450505050565b602080825282518282018190526000918401906040840190835b81811015612ee957835163ffffffff168352602093840193909201916001016130b2565b6000806000606084860312156130eb57600080fd5b6130f484612bb4565b95602085013595506040909401359392505050565b60608101610cae828463ffffffff815116825263ffffffff60208201511660208301526001600160601b0360408201511660408301525050565b80356001600160601b0381168114612bc557600080fd5b6000806040838503121561316d57600080fd5b61317683612bb4565b9150612e7160208401613143565b60008060006040848603121561319957600080fd5b8335925060208401356001600160401b0381111561307f57600080fd5b600082601f8301126131c757600080fd5b81356131d5612fdb82612f62565b8082825260208201915060208360061b8601019250858311156131f757600080fd5b602085015b83811015611236576040818803121561321457600080fd5b61321c612f0a565b813561322781612c31565b815261323560208301613143565b6020820152808452506020830192506040810190506131fc565b6000806040838503121561326257600080fd5b61326b83612bb4565b915060208301356001600160401b0381111561328657600080fd5b613292858286016131b6565b9150509250929050565b6000806000606084860312156132b157600080fd5b6132ba84612bb4565b92506132c860208501613032565b929592945050506040919091013590565b6000806000606084860312156132ee57600080fd5b833592506132fe60208501612bb4565b915061330c60408501613032565b90509250925092565b6000806000806080858703121561332b57600080fd5b61333485612bb4565b935061334260208601613032565b93969395505050506040820135916060013590565b60008060006060848603121561336c57600080fd5b61337584612bb4565b925061338360208501613143565b915060408401356001600160401b0381111561339e57600080fd5b6133aa868287016131b6565b9150509250925092565b60208082526031908201527f5374616b6552656769737472792e71756f72756d4578697374733a2071756f726040820152701d5b48191bd95cc81b9bdd08195e1a5cdd607a1b606082015260800190565b60006020828403121561341757600080fd5b81516114ee81612c31565b60208082526056908201527f5374616b6552656769737472792e6f6e6c79436f6f7264696e61746f724f776e60408201527f65723a2063616c6c6572206973206e6f7420746865206f776e6572206f6620746060820152753432903932b3b4b9ba393ca1b7b7b93234b730ba37b960511b608082015260a00190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156134c657600080fd5b6114ee82613143565b6020808252604c908201527f5374616b6552656769737472792e6f6e6c795265676973747279436f6f72646960408201527f6e61746f723a2063616c6c6572206973206e6f7420746865205265676973747260608201526b3ca1b7b7b93234b730ba37b960a11b608082015260a00190565b634e487b7160e01b600052601160045260246000fd5b81810381811115610cae57610cae613541565b634e487b7160e01b600052603160045260246000fd5b6001600160a01b0383168152604060208083018290528354918301829052600084815290812090916060840190835b818110156135d65783546001600160a01b03168352600193840193602090930192016135af565b50909695505050505050565b6000602082840312156135f457600080fd5b81516001600160401b0381111561360a57600080fd5b8201601f8101841361361b57600080fd5b8051613629612fdb82612f62565b8082825260208201915060208360051b85010192508683111561364b57600080fd5b6020840193505b82841015612276578351825260209384019390910190613652565b8082028115828204841417610cae57610cae613541565b6000826136a157634e487b7160e01b600052601260045260246000fd5b500490565b6001600160601b038181168382160190811115610cae57610cae613541565b80820180821115610cae57610cae613541565b6000816136e7576136e7613541565b506000190190565b818103600083128015838313168383128216171561062b5761062b613541565b6000600160ff1b820161372457613724613541565b5060000390565b6001600160601b038281168282160390811115610cae57610cae61354156fe5374616b6552656769737472792e5f6164645374726174656779506172616d73a2646970667358221220f4dc644e179e618719550cbb12175c010d6aa3cf2a79dc04e19813770c0c8c4764736f6c634300081c0033000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b900000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101e55760003560e01c80639f3ccf651161010f578063c8294c56116100a2578063f2be94ae11610071578063f2be94ae1461054b578063f851e1981461055e578063fa28c62714610571578063ff694a771461058457600080fd5b8063c8294c56146104d6578063d5eccc05146104e9578063dd9846b9146104fc578063df5cf7231461052457600080fd5b8063bc9a40c3116100de578063bc9a40c314610474578063bd29b8cd14610487578063c46778a51461049a578063c601527d146104c357600080fd5b80639f3ccf65146103ee578063ac6bfb0314610401578063adc804da14610421578063b6904b781461046157600080fd5b80634bd26e091161018757806366acfefe1161015657806366acfefe1461034a5780636d14a987146103755780637c172347146103b457806381c07502146103ce57600080fd5b80634bd26e09146102e55780635401ed27146103155780635e5a6775146103285780635f1f2d771461033757600080fd5b806320b66298116101c357806320b662981461026c57806325504777146102815780632cd95940146102a25780633ca5a5f5146102c257600080fd5b80630491b41c146101ea57806308732461146102205780631f9b74e014610241575b600080fd5b61020d6101f8366004612bca565b60ff1660009081526001602052604090205490565b6040519081526020015b60405180910390f35b61023361022e366004612be5565b610597565b604051610217929190612c0f565b61025461024f366004612c49565b6105e0565b6040516001600160601b039091168152602001610217565b61027f61027a366004612cc4565b610632565b005b61029461028f366004612d89565b610959565b604051610217929190612e29565b6102b56102b0366004612e4e565b610c15565b6040516102179190612e7a565b61020d6102d0366004612bca565b60ff1660009081526003602052604090205490565b61020d6102f3366004612e4e565b600091825260026020908152604080842060ff93909316845291905290205490565b610254610323366004612e4e565b610cb4565b61020d670de0b6b3a764000081565b61027f610345366004612f85565b610ccd565b61035d610358366004612d89565b6110e3565b6040516001600160c01b039091168152602001610217565b61039c7f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b981565b6040516001600160a01b039091168152602001610217565b6103bc602081565b60405160ff9091168152602001610217565b6103e16103dc366004613046565b611240565b6040516102179190613098565b61039c6103fc366004612be5565b6114f5565b61041461040f3660046130d6565b61152d565b6040516102179190613109565b61043461042f366004612be5565b6115c5565b6040805182516001600160a01b031681526020928301516001600160601b03169281019290925201610217565b61041461046f366004612be5565b61163f565b61027f61048236600461315a565b6116ce565b61027f610495366004613184565b6117c7565b6102546104a8366004612bca565b6000602081905290815260409020546001600160601b031681565b61027f6104d136600461324f565b6118e9565b6102546104e436600461329c565b6119dd565b6102546104f7366004612bca565b611a5b565b61050f61050a3660046132d9565b611aae565b60405163ffffffff9091168152602001610217565b61039c7f00000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a81565b610254610559366004613315565b611ac3565b61041461056c366004612e4e565b611b58565b61025461057f3660046132d9565b611c41565b61027f610592366004613357565b611ca2565b600360205281600052604060002081815481106105b357600080fd5b6000918252602090912001546001600160a01b0381169250600160a01b90046001600160601b0316905082565b60ff8216600090815260016020526040812054839061061a5760405162461bcd60e51b8152600401610611906133b4565b60405180910390fd5b60006106268585611e0d565b509250505b5092915050565b7f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b49190613405565b6001600160a01b0316336001600160a01b0316146106e45760405162461bcd60e51b815260040161061190613422565b846107008160ff16600090815260016020526040902054151590565b61071c5760405162461bcd60e51b8152600401610611906133b4565b8380610792576040805162461bcd60e51b81526020600482015260248101919091527f5374616b6552656769737472792e6d6f6469667953747261746567795061726160448201527f6d733a206e6f20737472617465677920696e64696365732070726f76696465646064820152608401610611565b8281146108075760405162461bcd60e51b815260206004820152603960248201527f5374616b6552656769737472792e6d6f6469667953747261746567795061726160448201527f6d733a20696e707574206c656e677468206d69736d61746368000000000000006064820152608401610611565b60ff87166000908152600360205260408120905b8281101561094e578585828181106108355761083561349e565b905060200201602081019061084a91906134b4565b8289898481811061085d5761085d61349e565b90506020020135815481106108745761087461349e565b9060005260206000200160000160146101000a8154816001600160601b0302191690836001600160601b031602179055508860ff167f11a5641322da1dff56a4b66eaac31ffa465295ece907cd163437793b4d009a75838a8a858181106108dd576108dd61349e565b90506020020135815481106108f4576108f461349e565b6000918252602090912001546001600160a01b031688888581811061091b5761091b61349e565b905060200201602081019061093091906134b4565b60405161093e929190612c0f565b60405180910390a260010161081b565b505050505050505050565b606080336001600160a01b037f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b916146109a45760405162461bcd60e51b8152600401610611906134cf565b6000836001600160401b038111156109be576109be612ef4565b6040519080825280602002602001820160405280156109e7578160200160208202803683370190505b5090506000846001600160401b03811115610a0457610a04612ef4565b604051908082528060200260200182016040528015610a2d578160200160208202803683370190505b50905060005b85811015610c07576000878783818110610a4f57610a4f61349e565b919091013560f81c60008181526001602052604090205490925015159050610ad75760405162461bcd60e51b815260206004820152603560248201527f5374616b6552656769737472792e72656769737465724f70657261746f723a206044820152741c5d5bdc9d5b48191bd95cc81b9bdd08195e1a5cdd605a1b6064820152608401610611565b600080610ae4838d611e0d565b9150915080610b815760405162461bcd60e51b815260206004820152605b60248201527f5374616b6552656769737472792e72656769737465724f70657261746f723a2060448201527f4f70657261746f7220646f6573206e6f74206d656574206d696e696d756d207360648201527f74616b6520726571756972656d656e7420666f722071756f72756d0000000000608482015260a401610611565b6000610b8e8c8585612001565b905082878681518110610ba357610ba361349e565b60200260200101906001600160601b031690816001600160601b031681525050610bcd8482612280565b868681518110610bdf57610bdf61349e565b6001600160601b0390921660209283029190910190910152505060019092019150610a339050565b509097909650945050505050565b600082815260026020908152604080832060ff851684528252808320805482518185028101850190935280835260609492939192909184015b82821015610ca7576000848152602090819020604080516060810182529185015463ffffffff8082168452600160201b82041683850152600160401b90046001600160601b031690820152825260019092019101610c4e565b5050505090505b92915050565b600080610cc18484611b58565b60400151949350505050565b7f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4f9190613405565b6001600160a01b0316336001600160a01b031614610d7f5760405162461bcd60e51b815260040161061190613422565b81610d9b8160ff16600090815260016020526040902054151590565b610db75760405162461bcd60e51b8152600401610611906133b4565b815180610e2c5760405162461bcd60e51b815260206004820152603d60248201527f5374616b6552656769737472792e72656d6f7665537472617465676965733a2060448201527f6e6f20696e646963657320746f2072656d6f76652070726f76696465640000006064820152608401610611565b60ff841660009081526003602090815260408083206004909252822090915b838110156110da578660ff167f31fa2e2cd280c9375e13ffcf3d81e2378100186e4058f8d3ddb690b82dcd31f784888481518110610e8b57610e8b61349e565b602002602001015181548110610ea357610ea361349e565b600091825260209182902001546040516001600160a01b0390911681520160405180910390a28660ff167f11a5641322da1dff56a4b66eaac31ffa465295ece907cd163437793b4d009a7584888481518110610f0157610f0161349e565b602002602001015181548110610f1957610f1961349e565b600091825260208083209190910154604080516001600160a01b039092168252918101929092520160405180910390a282548390610f5990600190613557565b81548110610f6957610f6961349e565b9060005260206000200183878381518110610f8657610f8661349e565b602002602001015181548110610f9e57610f9e61349e565b600091825260209091208254910180546001600160a01b0319166001600160a01b03909216918217815591546001600160601b03600160a01b9182900416021790558254839080610ff157610ff161356a565b6000828152602081208201600019908101919091550190558154829061101990600190613557565b815481106110295761102961349e565b9060005260206000200160009054906101000a90046001600160a01b03168287838151811061105a5761105a61349e565b6020026020010151815481106110725761107261349e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818054806110b0576110b061356a565b600082815260209020810160001990810180546001600160a01b0319169055019055600101610e4b565b50505050505050565b6000336001600160a01b037f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b9161461112d5760405162461bcd60e51b8152600401610611906134cf565b6000805b8381101561123657600085858381811061114d5761114d61349e565b919091013560f81c600081815260016020526040902054909250151590506111dd5760405162461bcd60e51b815260206004820152603860248201527f5374616b6552656769737472792e7570646174654f70657261746f725374616b60448201527f653a2071756f72756d20646f6573206e6f7420657869737400000000000000006064820152608401610611565b6000806111ea838b611e0d565b915091508061120c5760009150600160ff84161b6001600160c01b0386161794505b60006112198a8585612001565b90506112258482612280565b505060019093019250611131915050565b5095945050505050565b60606000826001600160401b0381111561125c5761125c612ef4565b604051908082528060200260200182016040528015611285578160200160208202803683370190505b50905060005b838110156114ea5760008585838181106112a7576112a761349e565b919091013560f81c600081815260016020526040902054909250151590506113465760405162461bcd60e51b815260206004820152604660248201527f5374616b6552656769737472792e676574546f74616c5374616b65496e64696360448201527f65734174426c6f636b4e756d6265723a2071756f72756d20646f6573206e6f7460648201526508195e1a5cdd60d21b608482015260a401610611565b60ff81166000908152600160205260408120805463ffffffff8a16929061136f5761136f61349e565b60009182526020909120015463ffffffff16111561141b5760405162461bcd60e51b815260206004820152605b60248201527f5374616b6552656769737472792e676574546f74616c5374616b65496e64696360448201527f65734174426c6f636b4e756d6265723a2071756f72756d20686173206e6f207360648201527f74616b6520686973746f727920617420626c6f636b4e756d6265720000000000608482015260a401610611565b60ff8116600090815260016020526040812054905b818110156114df5760ff8316600090815260016020819052604090912063ffffffff8b169161145f8486613557565b6114699190613557565b815481106114795761147961349e565b60009182526020909120015463ffffffff16116114d757600161149c8284613557565b6114a69190613557565b8585815181106114b8576114b861349e565b602002602001019063ffffffff16908163ffffffff16815250506114df565b600101611430565b50505060010161128b565b5090505b9392505050565b6004602052816000526040600020818154811061151157600080fd5b6000918252602090912001546001600160a01b03169150829050565b60408051606081018252600080825260208083018290528284018290528582526002815283822060ff881683529052919091208054839081106115725761157261349e565b600091825260209182902060408051606081018252929091015463ffffffff8082168452600160201b82041693830193909352600160401b9092046001600160601b031691810191909152949350505050565b604080518082019091526000808252602082015260ff831660009081526003602052604090208054839081106115fd576115fd61349e565b6000918252602091829020604080518082019091529101546001600160a01b0381168252600160a01b90046001600160601b0316918101919091529392505050565b604080516060810182526000808252602080830182905282840182905260ff86168252600190529190912080548390811061167c5761167c61349e565b600091825260209182902060408051606081018252929091015463ffffffff8082168452600160201b82041693830193909352600160401b9092046001600160601b0316918101919091529392505050565b7f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561172c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117509190613405565b6001600160a01b0316336001600160a01b0316146117805760405162461bcd60e51b815260040161061190613422565b8161179c8160ff16600090815260016020526040902054151590565b6117b85760405162461bcd60e51b8152600401610611906133b4565b6117c283836123f7565b505050565b336001600160a01b037f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b9161461180f5760405162461bcd60e51b8152600401610611906134cf565b60005b818110156118e357600083838381811061182e5761182e61349e565b919091013560f81c600081815260016020526040902054909250151590506118be5760405162461bcd60e51b815260206004820152603760248201527f5374616b6552656769737472792e646572656769737465724f70657261746f7260448201527f3a2071756f72756d20646f6573206e6f742065786973740000000000000000006064820152608401610611565b60006118cc86836000612001565b90506118d88282612280565b505050600101611812565b50505050565b7f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b96001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611947573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061196b9190613405565b6001600160a01b0316336001600160a01b03161461199b5760405162461bcd60e51b815260040161061190613422565b816119b78160ff16600090815260016020526040902054151590565b6119d35760405162461bcd60e51b8152600401610611906133b4565b6117c28383612460565b60ff83166000908152600160205260408120805482919084908110611a0457611a0461349e565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b90930492909216908201529050610cc1818561288e565b60ff81166000908152600160208190526040822080549091611a7c91613557565b81548110611a8c57611a8c61349e565b600091825260209091200154600160401b90046001600160601b031692915050565b6000611abb848484612a08565b949350505050565b600082815260026020908152604080832060ff881684529091528120805482919084908110611af457611af461349e565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b90930492909216908201529050611b4b818661288e565b6040015195945050505050565b6040805160608082018352600080835260208084018290528385018290528682526002815284822060ff8716835281528482205485519384018652828452908301829052938201819052919291829003611bb5579150610cae9050565b600085815260026020908152604080832060ff881684529091529020611bdc600184613557565b81548110611bec57611bec61349e565b600091825260209182902060408051606081018252919092015463ffffffff8082168352600160201b820416938201939093526001600160601b03600160401b90930492909216908201529250610cae915050565b600083815260026020908152604080832060ff861684529091528120611c68858585612a08565b63ffffffff1681548110611c7e57611c7e61349e565b600091825260209091200154600160401b90046001600160601b0316949350505050565b336001600160a01b037f000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b91614611cea5760405162461bcd60e51b8152600401610611906134cf565b60ff831660009081526001602052604090205415611d685760405162461bcd60e51b815260206004820152603560248201527f5374616b6552656769737472792e696e697469616c697a6551756f72756d3a2060448201527471756f72756d20616c72656164792065786973747360581b6064820152608401610611565b611d728382612460565b611d7c83836123f7565b505060ff166000908152600160208181526040808420815160608101835263ffffffff438116825281850187815293820187815283549687018455928752939095209451949093018054915193516001600160601b0316600160401b02600160401b600160a01b0319948416600160201b0267ffffffffffffffff1990931695909316949094171791909116179055565b600080600080611e2c8660ff1660009081526003602052604090205490565b604080518082019091526000808252602082015290915060ff871660009081526004602081905260408083209051639004134760e01b81526001600160a01b037f00000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a1692639004134792611ea1928c9201613580565b600060405180830381865afa158015611ebe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611ee691908101906135e2565b905060005b83811015611fcd5760ff89166000908152600360205260409020805482908110611f1757611f1761349e565b60009182526020808320604080518082019091529201546001600160a01b0381168352600160a01b90046001600160601b0316908201528351909450839083908110611f6557611f6561349e565b60200260200101511115611fc557670de0b6b3a764000083602001516001600160601b0316838381518110611f9c57611f9c61349e565b6020026020010151611fae919061366d565b611fb89190613684565b611fc290866136a6565b94505b600101611eeb565b50505060ff8616600090815260208190526040902054919350506001600160601b03908116908316101590505b9250929050565b600083815260026020908152604080832060ff8616845290915281205481908082036120c757600086815260026020908152604080832060ff891684528252808320815160608101835263ffffffff43811682528185018681526001600160601b03808c16958401958652845460018101865594885295909620915191909201805495519351909416600160401b02600160401b600160a01b0319938316600160201b0267ffffffffffffffff1990961691909216179390931716919091179055612226565b600086815260026020908152604080832060ff8916845290915281206120ee600184613557565b815481106120fe576120fe61349e565b600091825260209091200180546001600160601b03600160401b909104811694509091508516830361213657600093505050506114ee565b805463ffffffff43811691160361216e578054600160401b600160a01b031916600160401b6001600160601b03871602178155612224565b805467ffffffff000000001916600160201b4363ffffffff90811682810293909317845560008a815260026020908152604080832060ff8d168452825280832081516060810183529687528683018481526001600160601b038d81169389019384528254600181018455928652939094209651960180549351915196851667ffffffffffffffff1990941693909317931690930291909117600160401b600160a01b031916600160401b93909216929092021790555b505b6040805160ff871681526001600160601b038616602082015287917f2f527d527e95d8fe40aec55377743bb779087da3f6d0d08f12e36444da62327d910160405180910390a26122768285612b6e565b9695505050505050565b60ff8216600090815260016020819052604082208054918391906122a49084613557565b815481106122b4576122b461349e565b906000526020600020019050836000036122e25754600160401b90046001600160601b03169150610cae9050565b805460009061230190600160401b90046001600160601b031686612b86565b825490915063ffffffff43811691160361233c578154600160401b600160a01b031916600160401b6001600160601b038316021782556123ee565b815463ffffffff438116600160201b81810267ffffffff000000001990941693909317855560ff8916600090815260016020818152604080842081516060810183529586528583018581526001600160601b03808b169388019384528254958601835591865292909420945194909201805491519251909316600160401b02600160401b600160a01b031992861690960267ffffffffffffffff19909116939094169290921792909217169190911790555b95945050505050565b60ff82166000818152602081815260409182902080546bffffffffffffffffffffffff19166001600160601b03861690811790915591519182527f26eecff2b70b0a71104ff4d940ba7162d23a95c248771fc487a7be17a596b3cf910160405180910390a25050565b60008151116124c55760405162461bcd60e51b8152602060048201526038602482015260008051602061374b83398151915260448201527f3a206e6f20737472617465676965732070726f766964656400000000000000006064820152608401610611565b805160ff8316600090815260036020908152604090912054906124e883836136c5565b11156125585760405162461bcd60e51b8152602060048201526045602482015260008051602061374b83398151915260448201527f3a20657863656564204d41585f5745494748494e475f46554e4354494f4e5f4c60648201526408a9c8ea8960db1b608482015260a401610611565b60005b828110156128875760005b61257082846136c5565b811015612646578482815181106125895761258961349e565b6020026020010151600001516001600160a01b0316600360008860ff1660ff16815260200190815260200160002082815481106125c8576125c861349e565b6000918252602090912001546001600160a01b03160361263e5760405162461bcd60e51b815260206004820152603d602482015260008051602061374b83398151915260448201527f3a2063616e6e6f74206164642073616d652073747261746567792032780000006064820152608401610611565b600101612566565b50600084828151811061265b5761265b61349e565b6020026020010151602001516001600160601b0316116126e05760405162461bcd60e51b8152602060048201526046602482015260008051602061374b83398151915260448201527f3a2063616e6e6f74206164642073747261746567792077697468207a65726f206064820152651dd95a59da1d60d21b608482015260a401610611565b60ff8516600090815260036020526040902084518590839081106127065761270661349e565b602090810291909101810151825460018101845560009384528284208251928401516001600160601b0316600160a01b026001600160a01b039093169290921791015560ff871682526004905260409020845185908390811061276b5761276b61349e565b6020908102919091018101515182546001810184556000938452919092200180546001600160a01b0319166001600160a01b03909216919091179055835160ff8616907f10565e56cacbf32eca267945f054fec02e59750032d113d3302182ad967f5404908690849081106127e2576127e261349e565b602090810291909101810151516040516001600160a01b0390911681520160405180910390a28460ff167f11a5641322da1dff56a4b66eaac31ffa465295ece907cd163437793b4d009a7585838151811061283f5761283f61349e565b60200260200101516000015186848151811061285d5761285d61349e565b602002602001015160200151604051612877929190612c0f565b60405180910390a260010161255b565b5050505050565b816000015163ffffffff168163ffffffff1610156129335760405162461bcd60e51b815260206004820152605660248201527f5374616b6552656769737472792e5f76616c69646174655374616b655570646160448201527f74654174426c6f636b4e756d6265723a207374616b6555706461746520697320606482015275333937b69030b33a32b910313637b1b5a73ab6b132b960511b608482015260a401610611565b602082015163ffffffff1615806129595750816020015163ffffffff168163ffffffff16105b612a045760405162461bcd60e51b815260206004820152606a60248201527f5374616b6552656769737472792e5f76616c69646174655374616b655570646160448201527f74654174426c6f636b4e756d6265723a2074686572652069732061206e65776560648201527f72207374616b6555706461746520617661696c61626c65206265666f726520626084820152693637b1b5a73ab6b132b960b11b60a482015260c401610611565b5050565b600083815260026020908152604080832060ff86168452909152812054805b8015612aa957600086815260026020908152604080832060ff89168452909152902063ffffffff851690612a5c600184613557565b81548110612a6c57612a6c61349e565b60009182526020909120015463ffffffff1611612a9757612a8e600182613557565b925050506114ee565b80612aa1816136d8565b915050612a27565b5060405162461bcd60e51b815260206004820152608160248201527f5374616b6552656769737472792e5f6765745374616b65557064617465496e6460448201527f6578466f724f70657261746f724174426c6f636b4e756d6265723a206e6f207360648201527f74616b652075706461746520666f756e6420666f72206f70657261746f72496460848201527f20616e642071756f72756d4e756d62657220617420626c6f636b206e756d626560a4820152603960f91b60c482015260e401610611565b60006114ee6001600160601b038085169084166136ef565b600080821215612baa57612b998261370f565b612ba3908461372b565b9050610cae565b612ba382846136a6565b803560ff81168114612bc557600080fd5b919050565b600060208284031215612bdc57600080fd5b6114ee82612bb4565b60008060408385031215612bf857600080fd5b612c0183612bb4565b946020939093013593505050565b6001600160a01b039290921682526001600160601b0316602082015260400190565b6001600160a01b0381168114612c4657600080fd5b50565b60008060408385031215612c5c57600080fd5b612c6583612bb4565b91506020830135612c7581612c31565b809150509250929050565b60008083601f840112612c9257600080fd5b5081356001600160401b03811115612ca957600080fd5b6020830191508360208260051b8501011115611ffa57600080fd5b600080600080600060608688031215612cdc57600080fd5b612ce586612bb4565b945060208601356001600160401b03811115612d0057600080fd5b612d0c88828901612c80565b90955093505060408601356001600160401b03811115612d2b57600080fd5b612d3788828901612c80565b969995985093965092949392505050565b60008083601f840112612d5a57600080fd5b5081356001600160401b03811115612d7157600080fd5b602083019150836020828501011115611ffa57600080fd5b60008060008060608587031215612d9f57600080fd5b8435612daa81612c31565b93506020850135925060408501356001600160401b03811115612dcc57600080fd5b612dd887828801612d48565b95989497509550505050565b600081518084526020840193506020830160005b82811015612e1f5781516001600160601b0316865260209586019590910190600101612df8565b5093949350505050565b604081526000612e3c6040830185612de4565b82810360208401526123ee8185612de4565b60008060408385031215612e6157600080fd5b82359150612e7160208401612bb4565b90509250929050565b602080825282518282018190526000918401906040840190835b81811015612ee957612ed383855163ffffffff815116825263ffffffff60208201511660208301526001600160601b0360408201511660408301525050565b6020939093019260609290920191600101612e94565b509095945050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715612f2c57612f2c612ef4565b60405290565b604051601f8201601f191681016001600160401b0381118282101715612f5a57612f5a612ef4565b604052919050565b60006001600160401b03821115612f7b57612f7b612ef4565b5060051b60200190565b60008060408385031215612f9857600080fd5b612fa183612bb4565b915060208301356001600160401b03811115612fbc57600080fd5b8301601f81018513612fcd57600080fd5b8035612fe0612fdb82612f62565b612f32565b8082825260208201915060208360051b85010192508783111561300257600080fd5b6020840193505b82841015613024578335825260209384019390910190613009565b809450505050509250929050565b803563ffffffff81168114612bc557600080fd5b60008060006040848603121561305b57600080fd5b61306484613032565b925060208401356001600160401b0381111561307f57600080fd5b61308b86828701612d48565b9497909650939450505050565b602080825282518282018190526000918401906040840190835b81811015612ee957835163ffffffff168352602093840193909201916001016130b2565b6000806000606084860312156130eb57600080fd5b6130f484612bb4565b95602085013595506040909401359392505050565b60608101610cae828463ffffffff815116825263ffffffff60208201511660208301526001600160601b0360408201511660408301525050565b80356001600160601b0381168114612bc557600080fd5b6000806040838503121561316d57600080fd5b61317683612bb4565b9150612e7160208401613143565b60008060006040848603121561319957600080fd5b8335925060208401356001600160401b0381111561307f57600080fd5b600082601f8301126131c757600080fd5b81356131d5612fdb82612f62565b8082825260208201915060208360061b8601019250858311156131f757600080fd5b602085015b83811015611236576040818803121561321457600080fd5b61321c612f0a565b813561322781612c31565b815261323560208301613143565b6020820152808452506020830192506040810190506131fc565b6000806040838503121561326257600080fd5b61326b83612bb4565b915060208301356001600160401b0381111561328657600080fd5b613292858286016131b6565b9150509250929050565b6000806000606084860312156132b157600080fd5b6132ba84612bb4565b92506132c860208501613032565b929592945050506040919091013590565b6000806000606084860312156132ee57600080fd5b833592506132fe60208501612bb4565b915061330c60408501613032565b90509250925092565b6000806000806080858703121561332b57600080fd5b61333485612bb4565b935061334260208601613032565b93969395505050506040820135916060013590565b60008060006060848603121561336c57600080fd5b61337584612bb4565b925061338360208501613143565b915060408401356001600160401b0381111561339e57600080fd5b6133aa868287016131b6565b9150509250925092565b60208082526031908201527f5374616b6552656769737472792e71756f72756d4578697374733a2071756f726040820152701d5b48191bd95cc81b9bdd08195e1a5cdd607a1b606082015260800190565b60006020828403121561341757600080fd5b81516114ee81612c31565b60208082526056908201527f5374616b6552656769737472792e6f6e6c79436f6f7264696e61746f724f776e60408201527f65723a2063616c6c6572206973206e6f7420746865206f776e6572206f6620746060820152753432903932b3b4b9ba393ca1b7b7b93234b730ba37b960511b608082015260a00190565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156134c657600080fd5b6114ee82613143565b6020808252604c908201527f5374616b6552656769737472792e6f6e6c795265676973747279436f6f72646960408201527f6e61746f723a2063616c6c6572206973206e6f7420746865205265676973747260608201526b3ca1b7b7b93234b730ba37b960a11b608082015260a00190565b634e487b7160e01b600052601160045260246000fd5b81810381811115610cae57610cae613541565b634e487b7160e01b600052603160045260246000fd5b6001600160a01b0383168152604060208083018290528354918301829052600084815290812090916060840190835b818110156135d65783546001600160a01b03168352600193840193602090930192016135af565b50909695505050505050565b6000602082840312156135f457600080fd5b81516001600160401b0381111561360a57600080fd5b8201601f8101841361361b57600080fd5b8051613629612fdb82612f62565b8082825260208201915060208360051b85010192508683111561364b57600080fd5b6020840193505b82841015612276578351825260209384019390910190613652565b8082028115828204841417610cae57610cae613541565b6000826136a157634e487b7160e01b600052601260045260246000fd5b500490565b6001600160601b038181168382160190811115610cae57610cae613541565b80820180821115610cae57610cae613541565b6000816136e7576136e7613541565b506000190190565b818103600083128015838313168383128216171561062b5761062b613541565b6000600160ff1b820161372457613724613541565b5060000390565b6001600160601b038281168282160390811115610cae57610cae61354156fe5374616b6552656769737472792e5f6164645374726174656779506172616d73a2646970667358221220f4dc644e179e618719550cbb12175c010d6aa3cf2a79dc04e19813770c0c8c4764736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b900000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a
-----Decoded View---------------
Arg [0] : _registryCoordinator (address): 0xA8CC0749b4409c3c47012323E625aEcBA92f64b9
Arg [1] : _delegationManager (address): 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000a8cc0749b4409c3c47012323e625aecba92f64b9
Arg [1] : 00000000000000000000000039053d51b77dc0d36036fc1fcc8cb819df8ef37a
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.