Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ServiceManager
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
No with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {Ownable2StepUpgradeable} from "openzeppelin-upgradeable/access/Ownable2StepUpgradeable.sol";
import {Initializable} from "openzeppelin-upgradeable/proxy/utils/Initializable.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IPredicateManager, Task, SignatureWithSaltAndExpiry} from "./interfaces/IPredicateManager.sol";
contract ServiceManager is IPredicateManager, Initializable, Ownable2StepUpgradeable {
error ServiceManager__Unauthorized();
error ServiceManager__InvalidOperator();
error ServiceManager__InvalidStrategy();
error ServiceManager__ArrayLengthMismatch();
enum OperatorStatus {
NEVER_REGISTERED, // default is NEVER_REGISTERED
REGISTERED,
DEREGISTERED
}
struct OperatorInfo {
uint256 totalStake;
OperatorStatus status;
}
mapping(address => OperatorInfo) public operators;
mapping(address => address) public signingKeyToOperator;
mapping(address => mapping(string => bool)) public clientToPolicy; // DEPRECATED: use clientToPolicyID
mapping(string => string) public idToPolicy;
mapping(string => bool) public spentTaskIds;
mapping(string => string) public idToSocialGraph; // DEPRECATED
string[] public deployedPolicyIDs;
string[] public socialGraphIDs; // DEPRECATED
address[] public strategies;
address public aggregator; // DEPRECATED
address public delegationManager;
address public stakeRegistry;
address public avsDirectory;
uint256 public thresholdStake;
mapping(string => uint256) public policyIdToThreshold;
mapping(address => bool) private permissionedOperators;
mapping(address => string) public clientToPolicyID;
bool public allowRegistrations;
event SetPolicy(address indexed client, string indexed policyID);
event DeployedPolicy(string indexed policyID, string policy);
event OperatorRegistered(address indexed operator);
event OperatorRemoved(address indexed operator);
event StrategyAdded(address indexed strategy);
event StrategyRemoved(address indexed strategy);
event OperatorsStakesUpdated(address[][] indexed operatorsPerQuorum, bytes indexed quorumNumbers);
event AVSDirectoryUpdated(address indexed avsDirectory);
event ThresholdStakeUpdated(uint256 indexed thresholdStake);
event DelegationManagerUpdated(address indexed delegationManager);
event StakeRegistryUpdated(address indexed stakeRegistry);
event OperatorSigningKeyRotated(
address indexed operator, address indexed oldSigningKey, address indexed newSigningKey
);
event PermissionedOperatorsAdded(address[] operators);
event PermissionedOperatorsRemoved(address[] operators);
event TaskValidated(
address indexed msgSender,
address indexed target,
uint256 indexed value,
string policyID,
string taskId,
uint256 quorumThresholdCount,
uint256 expireByTime,
address[] signerAddresses
);
modifier onlyPermissionedOperator() {
if (!permissionedOperators[msg.sender]) {
revert ServiceManager__Unauthorized();
}
_;
}
function initialize(
address _owner,
address _aggregator,
address _delegationManager,
address _stakeRegistry,
address _avsDirectory,
uint256 _thresholdStake
) external initializer {
_transferOwnership(_owner);
delegationManager = _delegationManager;
stakeRegistry = _stakeRegistry;
avsDirectory = _avsDirectory;
thresholdStake = _thresholdStake;
}
/**
* @notice Adds permissioned operators to the set for the AVS
* @param _operators is the address[] to be permissioned for registration on the AVS
* @dev only callable by the owner
*/
function addPermissionedOperators(
address[] calldata _operators
) external onlyOwner {
for (uint256 i = 0; i < _operators.length; i++) {
permissionedOperators[_operators[i]] = true;
}
emit PermissionedOperatorsAdded(_operators);
}
/**
* @notice Removes permissioned operators from the set for the AVS
* @param _operators is the address[] to have permission revoked for registration on the AVS
* @dev only callable by the owner
*/
function removePermissionedOperators(
address[] calldata _operators
) external onlyOwner {
for (uint256 i = 0; i < _operators.length; i++) {
permissionedOperators[_operators[i]] = false;
}
emit PermissionedOperatorsRemoved(_operators);
}
/**
* @notice Enables the rotation of Predicate Signing Key for an operator
* @param _oldSigningKey address of the old signing key to remove
* @param _newSigningKey address of the new signing key to add
*/
function rotatePredicateSigningKey(address _oldSigningKey, address _newSigningKey) external {
require(
operators[msg.sender].status == OperatorStatus.REGISTERED,
"Predicate.rotatePredicateSigningKey: operator is not registered"
);
require(
msg.sender == signingKeyToOperator[_oldSigningKey],
"Predicate.rotatePredicateSigningKey: operator can only change it's own signing key"
);
require(
signingKeyToOperator[_newSigningKey] == address(0),
"Predicate.rotatePredicateSigningKey: new signing key already registered"
);
delete signingKeyToOperator[_oldSigningKey];
signingKeyToOperator[_newSigningKey] = msg.sender;
emit OperatorSigningKeyRotated(msg.sender, _oldSigningKey, _newSigningKey);
}
/**
* @notice Registers an operator with owner override
* @param _operator the address of the operator to register
* @param _operatorSigningKey the address of the operator signing key
* @dev only callable by the owner
*/
function registerOperatorOverride(address _operator, address _operatorSigningKey) external onlyOwner {
require(
signingKeyToOperator[_operatorSigningKey] == address(0),
"Predicate.registerOperatorToAVS: operator already registered"
);
require(
signingKeyToOperator[_operatorSigningKey] == address(0),
"Predicate.rotatePredicateSigningKey: new signing key already registered"
);
operators[_operator] = OperatorInfo(0, OperatorStatus.REGISTERED);
signingKeyToOperator[_operatorSigningKey] = _operator;
emit OperatorRegistered(_operator);
}
function setAllowRegistrations(bool _allowRegistrations) external onlyOwner {
allowRegistrations = _allowRegistrations;
}
/**
* @notice Registers a new operator
* @param _operatorSigningKey address of the operator signing key
* @param _operatorSignature signature used for validation
*/
function registerOperatorToAVS(
address _operatorSigningKey,
SignatureWithSaltAndExpiry memory _operatorSignature
) external onlyPermissionedOperator {
require(allowRegistrations, "Predicate.registerOperatorToAVS: registrations are not allowed");
require(
signingKeyToOperator[_operatorSigningKey] == address(0),
"Predicate.registerOperatorToAVS: operator already registered"
);
require(
signingKeyToOperator[_operatorSigningKey] == address(0),
"Predicate.rotatePredicateSigningKey: new signing key already registered"
);
uint256 totalStake;
for (uint256 i; i != strategies.length;) {
totalStake += IDelegationManager(delegationManager).operatorShares(msg.sender, IStrategy(strategies[i]));
unchecked {
++i;
}
}
if (totalStake >= thresholdStake) {
operators[msg.sender] = OperatorInfo(totalStake, OperatorStatus.REGISTERED);
signingKeyToOperator[_operatorSigningKey] = msg.sender;
ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSig = ISignatureUtils.SignatureWithSaltAndExpiry(
_operatorSignature.signature, _operatorSignature.salt, _operatorSignature.expiry
);
IAVSDirectory(avsDirectory).registerOperatorToAVS(msg.sender, _operatorSig);
emit OperatorRegistered(msg.sender);
}
}
/**
* @notice Removes an operator
* @param _operator the address of the operator to be removed
*/
function deregisterOperatorFromAVS(
address _operator
) external onlyOwner {
require(
operators[_operator].status != OperatorStatus.NEVER_REGISTERED,
"Predicate.deregisterOperatorFromAVS: operator is not registered"
);
operators[_operator] = OperatorInfo(0, OperatorStatus.DEREGISTERED);
IAVSDirectory(avsDirectory).deregisterOperatorFromAVS(_operator);
emit OperatorRemoved(_operator);
}
/**
* @notice Deploys a policy for which clients can use
* @param _policyID is a unique identifier
* @param _policy is set of formatted rules
* @param _quorumThreshold is the number of signatures required to validate a task
*/
function deployPolicy(
string memory _policyID,
string memory _policy,
uint256 _quorumThreshold
) external onlyOwner {
require(bytes(idToPolicy[_policyID]).length == 0, "Predicate.deployPolicy: policy exists");
require(_quorumThreshold > 0, "Predicate.deployPolicy: quorum threshold must be greater than zero");
require(bytes(_policy).length > 0, "Predicate.deployPolicy: policy string cannot be empty");
idToPolicy[_policyID] = _policy;
policyIdToThreshold[_policyID] = _quorumThreshold;
deployedPolicyIDs.push(_policyID);
emit DeployedPolicy(_policyID, _policy);
}
/**
* @notice Gets array of deployed policies
* @return array of deployed policies
*/
function getDeployedPolicies() external view returns (string[] memory) {
return deployedPolicyIDs;
}
/**
* @notice Sets a policy for the calling contract (msg.sender)
* @dev Associates a client contract with a specific policy ID. The policy must be previously registered.
* @param _policyID Identifier of a registered policy to associate with the caller
*/
function setPolicy(
string memory _policyID
) external {
require(bytes(_policyID).length > 0, "Predicate.setPolicy: policy ID cannot be empty");
require(policyIdToThreshold[_policyID] > 0, "Predicate.setPolicy: policy ID not registered");
clientToPolicyID[msg.sender] = _policyID;
emit SetPolicy(msg.sender, _policyID);
}
/**
* @notice Overrides the policy for a specific client address
* @param _policyID is the unique identifier for the policy
* @param _clientAddress is the address of the client for which the policy is being overridden
*/
function overrideClientPolicyID(string memory _policyID, address _clientAddress) external onlyOwner {
require(bytes(_policyID).length > 0, "Predicate.setPolicy: policy ID cannot be empty");
require(policyIdToThreshold[_policyID] > 0, "Predicate.setPolicy: policy ID not registered");
clientToPolicyID[_clientAddress] = _policyID;
emit SetPolicy(_clientAddress, _policyID);
}
/**
* @notice Performs the hashing of an STM task
* @param _task parameters of the task
* @return the keccak256 digest of the task
*/
function hashTaskWithExpiry(
Task calldata _task
) public pure returns (bytes32) {
return keccak256(
abi.encode(
_task.taskId,
_task.msgSender,
_task.target,
_task.value,
_task.encodedSigAndArgs,
_task.policyID,
_task.quorumThresholdCount,
_task.expireByTime
)
);
}
/**
* @notice Computes a secure task hash with validation-time context
* @param _task The task parameters to hash
* @return bytes32 The keccak256 digest including validation context
*/
function hashTaskSafe(
Task calldata _task
) public view returns (bytes32) {
return keccak256(
abi.encode(
_task.taskId,
_task.msgSender,
msg.sender,
_task.value,
_task.encodedSigAndArgs,
_task.policyID,
_task.quorumThresholdCount,
_task.expireByTime
)
);
}
/**
* @notice Validates signatures using the OpenZeppelin ECDSA library for the Predicate Single Transaction Model
* @param _task the params of the task
* @param signerAddresses the addresses of the operators
* @param signatures the signatures of the operators
*/
function validateSignatures(
Task calldata _task,
address[] memory signerAddresses,
bytes[] memory signatures
) external returns (bool isVerified) {
require(_task.quorumThresholdCount != 0, "Predicate.validateSignatures: quorum threshold count cannot be zero");
require(
signerAddresses.length == signatures.length,
"Predicate.validateSignatures: Mismatch between signers and signatures"
);
require(block.timestamp <= _task.expireByTime, "Predicate.validateSignatures: transaction expired");
require(!spentTaskIds[_task.taskId], "Predicate.validateSignatures: task ID already spent");
uint256 numSignaturesRequired = policyIdToThreshold[_task.policyID];
require(
numSignaturesRequired != 0 && _task.quorumThresholdCount == numSignaturesRequired,
"Predicate.PredicateVerified: deployed policy quorum threshold differs from task quorum threshold"
);
bytes32 messageHash = hashTaskSafe(_task);
for (uint256 i = 0; i < numSignaturesRequired;) {
if (i > 0 && uint160(signerAddresses[i]) <= uint160(signerAddresses[i - 1])) {
revert("Predicate.validateSignatures: Signer addresses must be unique and sorted");
}
address recoveredSigner = ECDSA.recover(messageHash, signatures[i]);
require(recoveredSigner == signerAddresses[i], "Predicate.validateSignatures: Invalid signature");
address operator = signingKeyToOperator[recoveredSigner];
require(
operators[operator].status == OperatorStatus.REGISTERED,
"Predicate.validateSignatures: Signer is not a registered operator"
);
unchecked {
++i;
}
}
emit TaskValidated(
_task.msgSender,
_task.target,
_task.value,
_task.policyID,
_task.taskId,
_task.quorumThresholdCount,
_task.expireByTime,
signerAddresses
);
spentTaskIds[_task.taskId] = true;
return true;
}
// ============ EigenLayer ============ //
/**
* @notice Sets the delegationManager contract address
* @param _delegationManager is the delegationManager on the eigenlayer contracts
* @dev only callable by the owner
*/
function setDelegationManager(
address _delegationManager
) external onlyOwner {
delegationManager = _delegationManager;
emit DelegationManagerUpdated(delegationManager);
}
/**
* @notice Sets the stakeRegistry contract address
* @param _stakeRegistry is the stakeRegistry on the eigenlayer contracts
* @dev only callable by the owner
*/
function setStakeRegistry(
address _stakeRegistry
) external onlyOwner {
stakeRegistry = _stakeRegistry;
emit StakeRegistryUpdated(stakeRegistry);
}
/**
* @notice Sets the avsDirectory contract address
* @param _avsDirectory is the avsDirectory on the eigenlayer contracts
* @dev only callable by the owner
*/
function setAVSDirectory(
address _avsDirectory
) external onlyOwner {
avsDirectory = _avsDirectory;
emit AVSDirectoryUpdated(avsDirectory);
}
/**
* @notice Sets threshold stake.
* @dev Has modifiers: onlyOwner.
* @param _thresholdStake The threshold stake (uint256).
*/
function setThresholdStake(
uint256 _thresholdStake
) external onlyOwner {
thresholdStake = _thresholdStake;
emit ThresholdStakeUpdated(thresholdStake);
}
/**
* @notice Sets the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
* @dev only callable by the owner
*/
function setMetadataURI(
string memory _metadataURI
) external onlyOwner {
IAVSDirectory(avsDirectory).updateAVSMetadataURI(_metadataURI);
}
/**
* @notice Adds a new strategy
* @param _strategy address of the strategy to add
* @param quorumNumber uint8 denoting the quorum number
* @param index uint256 denoting the index for the strategy
*/
function addStrategy(address _strategy, uint8 quorumNumber, uint256 index) external onlyOwner {
IStakeRegistry.StrategyParams memory strategyParams =
IStakeRegistry(stakeRegistry).strategyParamsByIndex(quorumNumber, index);
if (address(strategyParams.strategy) != _strategy) {
revert ServiceManager__InvalidStrategy();
}
strategies.push(_strategy);
emit StrategyAdded(_strategy);
}
/**
* @notice Removes a strategy
* @param _strategy address of the strategy to be removed
*/
function removeStrategy(
address _strategy
) external onlyOwner {
for (uint256 i = 0; i != strategies.length;) {
if (strategies[i] == _strategy) {
strategies[i] = strategies[strategies.length - 1];
strategies.pop();
emit StrategyRemoved(_strategy);
break;
}
unchecked {
++i;
}
}
}
/**
* @notice Returns the list of strategies that the AVS supports for restaking
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on uniqueness of each element in the returned array.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies() external view returns (address[] memory) {
return strategies;
}
/**
* @notice Returns the list of strategies that the operator has potentially restaked on the AVS
* @param operator The address of the operator to get restaked strategies for
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness
* of each element in the returned array. The off-chain service should do that validation separately
*/
function getOperatorRestakedStrategies(
address operator
) external view returns (address[] memory) {
address[] memory restakedStrategies = new address[](strategies.length);
uint256 index = 0;
for (uint256 i = 0; i < strategies.length; i++) {
if (IDelegationManager(delegationManager).operatorShares(operator, IStrategy(strategies[i])) > 0) {
restakedStrategies[index] = strategies[i];
index++;
}
}
return restakedStrategies;
}
/**
* @notice Updates the stakes of all operators for each of the specified quorums in the StakeRegistry. Each quorum also
* has their quorumUpdateBlockNumber updated. which is meant to keep track of when operators were last all updated at once.
* @param operatorsPerQuorum is an array of arrays of operators to update for each quorum. Note that each nested array
* of operators must be sorted in ascending address order to ensure that all operators in the quorum are updated
* @param quorumNumbers is an array of quorum numbers to update
* @dev This method is used to update the stakes of all operators in a quorum at once, rather than individually. Performs
* sanitization checks on the input array lengths, quorumNumbers existing, and that quorumNumbers are ordered. Function must
* also not be paused by the PAUSED_UPDATE_OPERATOR flag.
*/
function updateOperatorsForQuorum(address[][] calldata operatorsPerQuorum, bytes calldata quorumNumbers) external {
if (operatorsPerQuorum.length != quorumNumbers.length) {
revert ServiceManager__ArrayLengthMismatch();
}
address[] memory currQuorumOperators;
address currOperatorAddress;
OperatorInfo storage currOperator;
for (uint256 i; i != quorumNumbers.length;) {
currQuorumOperators = operatorsPerQuorum[i];
for (uint256 j; j < currQuorumOperators.length;) {
currOperatorAddress = currQuorumOperators[j];
currOperator = operators[currOperatorAddress];
if (currOperator.status == OperatorStatus.NEVER_REGISTERED) {
revert ServiceManager__InvalidOperator();
}
uint256 totalStake;
for (uint256 k; k != strategies.length;) {
totalStake += IDelegationManager(delegationManager).operatorShares(
currOperatorAddress, IStrategy(strategies[k])
);
unchecked {
++k;
}
}
currOperator.totalStake = totalStake;
currOperator.status =
totalStake < thresholdStake ? OperatorStatus.DEREGISTERED : OperatorStatus.REGISTERED;
unchecked {
++j;
}
}
unchecked {
++i;
}
}
emit OperatorsStakesUpdated(operatorsPerQuorum, quorumNumbers);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
struct Ownable2StepStorage {
address _pendingOwner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
assembly {
$.slot := Ownable2StepStorageLocation
}
}
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function __Ownable2Step_init() internal onlyInitializing {
}
function __Ownable2Step_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
return $._pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*
* Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
$._pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
delete $._pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// 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 address to receive the rewards that the operator earns via serving applications built on EigenLayer.
address 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 a queued withdrawal is *migrated* from the StrategyManager to the DelegationManager
event WithdrawalMigrated(bytes32 oldWithdrawalRoot, bytes32 newWithdrawalRoot);
/// @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 This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
* @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.
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
*/
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 earnings receiver address for an operator
*/
function earningsReceiver(address operator) external view returns (address);
/**
* @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 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);
function migrateQueuedWithdrawals(IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import "./ISignatureUtils.sol";
interface IAVSDirectory is ISignatureUtils {
/// @notice Enum representing the status of an operator's registration with an AVS
enum OperatorAVSRegistrationStatus {
UNREGISTERED, // Operator not registered to AVS
REGISTERED // Operator registered to AVS
}
/**
* @notice Emitted when @param avs 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 AVSMetadataURIUpdated(address indexed avs, string metadataURI);
/// @notice Emitted when an operator's registration status for an AVS is updated
event OperatorAVSRegistrationStatusUpdated(address indexed operator, address indexed avs, OperatorAVSRegistrationStatus status);
/**
* @notice Called by an avs to register an operator with the avs.
* @param operator The address of the operator to register.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operator,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external;
/**
* @notice Called by an avs to deregister an operator with the avs.
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(address operator) external;
/**
* @notice Called by an AVS to emit an `AVSMetadataURIUpdated` event indicating the information has updated.
* @param metadataURI The URI for metadata associated with an AVS
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `AVSMetadataURIUpdated` event
*/
function updateAVSMetadataURI(string calldata metadataURI) external;
/**
* @notice Returns whether or not the salt has already been used by the operator.
* @dev Salts is used in the `registerOperatorToAVS` function.
*/
function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool);
/**
* @notice Calculates the digest hash to be signed by an operator to register with an AVS
* @param operator The account registering as an operator
* @param avs The AVS the operator is registering to
* @param salt A unique and single use value associated with the approver signature.
* @param expiry Time after which the approver's signature becomes invalid
*/
function calculateOperatorAVSRegistrationDigestHash(
address operator,
address avs,
bytes32 salt,
uint256 expiry
) external view returns (bytes32);
/// @notice The EIP-712 typehash for the Registration struct used by the contract
function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32);
}// 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 "@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: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
/**
* @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums.
* @author Layr Labs, Inc.
*/
interface IStakeRegistry {
/**
* @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;
}
/// @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);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
/// @notice Struct that bundles together a task's parameters for validation
struct Task {
// the unique identifier for the task
string taskId;
// the address of the sender of the task
address msgSender;
// the address of the target contract for the task
address target;
// the value to send with the task
uint256 value;
// the encoded signature and arguments for the task
bytes encodedSigAndArgs;
// the policy ID associated with the task
string policyID;
// the number of signatures required to authorize the task
uint32 quorumThresholdCount;
// the timestamp by which the task must be executed
uint256 expireByTime;
}
/// @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;
}
/**
* @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer
* @author Predicate Labs, Inc
*/
interface IPredicateManager {
/**
* @notice Sets the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
*/
function setMetadataURI(
string memory _metadataURI
) external;
/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS
* @param operatorSigningKey The address of the operator's signing key.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToAVS(
address operatorSigningKey,
SignatureWithSaltAndExpiry memory operatorSignature
) external;
/**
* @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS
* @param operator The address of the operator to deregister.
*/
function deregisterOperatorFromAVS(
address operator
) external;
/**
* @notice Returns the list of strategies that the operator has potentially restaked on the AVS
* @param operator The address of the operator to get restaked strategies for
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness
* of each element in the returned array. The off-chain service should do that validation separately
*/
function getOperatorRestakedStrategies(
address operator
) external view returns (address[] memory);
/**
* @notice Returns the list of strategies that the AVS supports for restaking
* @dev This function is intended to be called off-chain
* @dev No guarantee is made on uniqueness of each element in the returned array.
* The off-chain service should do that validation separately
*/
function getRestakeableStrategies() external view returns (address[] memory);
/**
* @notice Sets a policy ID for the sender, defining execution rules or parameters for tasks
* @param policyID string pointing to the policy details
* @dev Only callable by client contracts or EOAs to associate a policy with their address
* @dev Emits a SetPolicy event upon successful association
*/
function setPolicy(
string memory policyID
) external;
/**
* @notice Deploys a policy with ID with execution rules or parameters for tasks
* @param _policyID string pointing to the policy details
* @param _policy string containing the policy details
* @param _quorumThreshold The number of signatures required to authorize a task
* @dev Only callable by service manager deployer
* @dev Emits a DeployedPolicy event upon successful deployment
*/
function deployPolicy(string memory _policyID, string memory _policy, uint256 _quorumThreshold) external;
/**
* @notice Gets array of deployed policies
*/
function getDeployedPolicies() external view returns (string[] memory);
/**
* @notice Verifies if a task is authorized by the required number of operators
* @param _task Parameters of the task including sender, target, function signature, arguments, quorum count, and expiry block
* @param signerAddresses Array of addresses of the operators who signed the task
* @param signatures Array of signatures from the operators authorizing the task
* @return isVerified Boolean indicating if the task has been verified by the required number of operators
* @dev This function checks the signatures against the hash of the task parameters to ensure task authenticity and authorization
*/
function validateSignatures(
Task memory _task,
address[] memory signerAddresses,
bytes[] memory signatures
) external returns (bool isVerified);
/**
* @notice Adds a new strategy to the Service Manager
* @dev Only callable by the contract owner. Adds a strategy that operators can stake on.
* @param _strategy The address of the strategy contract to add
* @param quorumNumber The quorum number associated with the strategy
* @param index The index of the strategy within the quorum
* @dev Emits a StrategyAdded event upon successful addition of the strategy
* @dev Reverts if the strategy does not exist or is already added
*/
function addStrategy(address _strategy, uint8 quorumNumber, uint256 index) external;
/**
* @notice Removes an existing strategy from the Service Manager
* @dev Only callable by the contract owner. Removes a strategy that operators are currently able to stake on.
* @param _strategy The address of the strategy contract to remove
* @dev Emits a StrategyRemoved event upon successful removal of the strategy
* @dev Reverts if the strategy is not currently added or if the address is invalid
*/
function removeStrategy(
address _strategy
) external;
/**
* @notice Enables the rotation of Predicate Signing Key for an operator
* @param _oldSigningKey address of the old signing key to remove
* @param _newSigningKey address of the new signing key to add
*/
function rotatePredicateSigningKey(address _oldSigningKey, address _newSigningKey) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// 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` 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);
// LIMITED BACKWARDS-COMPATIBILITY FOR DEPRECATED FUNCTIONALITY
// packed struct for queued withdrawals; helps deal with stack-too-deep errors
struct DeprecatedStruct_WithdrawerAndNonce {
address withdrawer;
uint96 nonce;
}
/**
* 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. `startQueuedWithdrawalWaitingPeriod` or `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 DeprecatedStruct_QueuedWithdrawal {
IStrategy[] strategies;
uint256[] shares;
address staker;
DeprecatedStruct_WithdrawerAndNonce withdrawerAndNonce;
uint32 withdrawalStartBlock;
address delegatedAddress;
}
function migrateQueuedWithdrawal(DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal) external returns (bool, bytes32);
function calculateWithdrawalRoot(DeprecatedStruct_QueuedWithdrawal memory queuedWithdrawal) external pure returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// 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 (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @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.
*
* {UpgradeableBeacon} 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.
*
* _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 != 0 && proof.length % 32 == 0,
"Merkle.processInclusionProofKeccak: proof length should be a non-zero 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": [
"forge-std/=lib/forge-std/src/",
"solmate/=lib/solmate/src/",
"openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"eigenlayer-contracts/=lib/eigenlayer-contracts/",
"utils/=lib/utils/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin-upgrades-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
"@openzeppelin-upgrades/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable/",
"@openzeppelin-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"eigenlayer-middleware/=lib/eigenlayer-middleware/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"openzeppelin/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ServiceManager__ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"ServiceManager__InvalidOperator","type":"error"},{"inputs":[],"name":"ServiceManager__InvalidStrategy","type":"error"},{"inputs":[],"name":"ServiceManager__Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"avsDirectory","type":"address"}],"name":"AVSDirectoryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegationManager","type":"address"}],"name":"DelegationManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"policyID","type":"string"},{"indexed":false,"internalType":"string","name":"policy","type":"string"}],"name":"DeployedPolicy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"oldSigningKey","type":"address"},{"indexed":true,"internalType":"address","name":"newSigningKey","type":"address"}],"name":"OperatorSigningKeyRotated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address[][]","name":"operatorsPerQuorum","type":"address[][]"},{"indexed":true,"internalType":"bytes","name":"quorumNumbers","type":"bytes"}],"name":"OperatorsStakesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"operators","type":"address[]"}],"name":"PermissionedOperatorsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"operators","type":"address[]"}],"name":"PermissionedOperatorsRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"client","type":"address"},{"indexed":true,"internalType":"string","name":"policyID","type":"string"}],"name":"SetPolicy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakeRegistry","type":"address"}],"name":"StakeRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"}],"name":"StrategyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":true,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"string","name":"policyID","type":"string"},{"indexed":false,"internalType":"string","name":"taskId","type":"string"},{"indexed":false,"internalType":"uint256","name":"quorumThresholdCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireByTime","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"signerAddresses","type":"address[]"}],"name":"TaskValidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"thresholdStake","type":"uint256"}],"name":"ThresholdStakeUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_operators","type":"address[]"}],"name":"addPermissionedOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"},{"internalType":"uint8","name":"quorumNumber","type":"uint8"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"addStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"aggregator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowRegistrations","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"avsDirectory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"clientToPolicy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"clientToPolicyID","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegationManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_policyID","type":"string"},{"internalType":"string","name":"_policy","type":"string"},{"internalType":"uint256","name":"_quorumThreshold","type":"uint256"}],"name":"deployPolicy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deployedPolicyIDs","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"deregisterOperatorFromAVS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDeployedPolicies","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getOperatorRestakedStrategies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRestakeableStrategies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"taskId","type":"string"},{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"encodedSigAndArgs","type":"bytes"},{"internalType":"string","name":"policyID","type":"string"},{"internalType":"uint32","name":"quorumThresholdCount","type":"uint32"},{"internalType":"uint256","name":"expireByTime","type":"uint256"}],"internalType":"struct Task","name":"_task","type":"tuple"}],"name":"hashTaskSafe","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"taskId","type":"string"},{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"encodedSigAndArgs","type":"bytes"},{"internalType":"string","name":"policyID","type":"string"},{"internalType":"uint32","name":"quorumThresholdCount","type":"uint32"},{"internalType":"uint256","name":"expireByTime","type":"uint256"}],"internalType":"struct Task","name":"_task","type":"tuple"}],"name":"hashTaskWithExpiry","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"idToPolicy","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"idToSocialGraph","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_aggregator","type":"address"},{"internalType":"address","name":"_delegationManager","type":"address"},{"internalType":"address","name":"_stakeRegistry","type":"address"},{"internalType":"address","name":"_avsDirectory","type":"address"},{"internalType":"uint256","name":"_thresholdStake","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"uint256","name":"totalStake","type":"uint256"},{"internalType":"enum ServiceManager.OperatorStatus","name":"status","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_policyID","type":"string"},{"internalType":"address","name":"_clientAddress","type":"address"}],"name":"overrideClientPolicyID","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"policyIdToThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_operatorSigningKey","type":"address"}],"name":"registerOperatorOverride","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operatorSigningKey","type":"address"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct SignatureWithSaltAndExpiry","name":"_operatorSignature","type":"tuple"}],"name":"registerOperatorToAVS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_operators","type":"address[]"}],"name":"removePermissionedOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategy","type":"address"}],"name":"removeStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oldSigningKey","type":"address"},{"internalType":"address","name":"_newSigningKey","type":"address"}],"name":"rotatePredicateSigningKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_avsDirectory","type":"address"}],"name":"setAVSDirectory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowRegistrations","type":"bool"}],"name":"setAllowRegistrations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegationManager","type":"address"}],"name":"setDelegationManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_metadataURI","type":"string"}],"name":"setMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_policyID","type":"string"}],"name":"setPolicy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakeRegistry","type":"address"}],"name":"setStakeRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_thresholdStake","type":"uint256"}],"name":"setThresholdStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signingKeyToOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"socialGraphIDs","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"spentTaskIds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"strategies","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"thresholdStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[][]","name":"operatorsPerQuorum","type":"address[][]"},{"internalType":"bytes","name":"quorumNumbers","type":"bytes"}],"name":"updateOperatorsForQuorum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"taskId","type":"string"},{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"encodedSigAndArgs","type":"bytes"},{"internalType":"string","name":"policyID","type":"string"},{"internalType":"uint32","name":"quorumThresholdCount","type":"uint32"},{"internalType":"uint256","name":"expireByTime","type":"uint256"}],"internalType":"struct Task","name":"_task","type":"tuple"},{"internalType":"address[]","name":"signerAddresses","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"}],"name":"validateSignatures","outputs":[{"internalType":"bool","name":"isVerified","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234601c57600e6020565b615fd661002b8239615fd690f35b6026565b60405190565b5f80fdfe60806040526004361015610013575b611dcb565b61001d5f3561030b565b80621ba1eb146103065780630b3ce015146103015780630c68603b146102fc5780630ff26fd1146102f757806313e7c9d8146102f2578063140a16bc146102ed578063175188e8146102e857806318cea58d146102e35780631a8d0de2146102de57806320af7390146102d9578063245a7bfc146102d457806333cfb7b7146102cf57806338d11d3c146102ca578063403dc365146102c55780635140a548146102c05780635890e59c146102bb5780635b9a64dc146102b65780635d43707b146102b157806366f17e73146102ac57806368304835146102a75780636b3aa72e146102a25780636b4c991b1461029d5780636d224f8d14610298578063715018a614610293578063749dccc71461028e578063750521f514610289578063786bf3c31461028457806379ba50971461027f5780638376f7f81461027a578063862621ef146102755780638ad96602146102705780638da5cb5b1461026b578063949528fc1461026657806395b6ef0c146102615780639926ee7d1461025c578063a364f4da14610257578063c0443c5f14610252578063d574ea3d1461024d578063d9d4e99f14610248578063ddb49ce114610243578063ddf6a51b1461023e578063df93506514610239578063e30c397814610234578063e3b05f2f1461022f578063e481af9d1461022a578063ea4d3c9b146102255763f2fde38b0361000e57611d98565b611d63565b611d1f565b611cec565b611cb7565b611c82565b611c21565b611bd3565b611b3d565b611ae3565b611a3a565b61199c565b611968565b611854565b6117bb565b611786565b611753565b611702565b6116ce565b61169b565b611667565b6115bf565b61158a565b61153e565b61150a565b6114d7565b6114a2565b61145e565b61141b565b611362565b6112ed565b611273565b61122d565b611117565b610f34565b610ea5565b610dd1565b610d8d565b610d4b565b610d15565b610a82565b610a4c565b61093b565b610813565b610707565b6104ca565b61038f565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b60018060a01b031690565b61033790610323565b90565b6103438161032e565b0361034a57565b5f80fd5b9050359061035b8261033a565b565b91906040838203126103855780610379610382925f860161034e565b9360200161034e565b90565b61031b565b5f0190565b346103be576103a86103a236600461035d565b9061210a565b6103b0610311565b806103ba8161038a565b0390f35b610317565b5f9103126103cd57565b61031b565b5190565b60209181520190565b60200190565b5190565b60209181520190565b90825f9392825e0152565b601f801991011690565b61042661042f6020936104349361041d816103e5565b938480936103e9565b958691016103f2565b6103fd565b0190565b9061044291610407565b90565b60200190565b9061045f610458836103d2565b80926103d6565b9081610470602083028401946103df565b925f915b83831061048357505050505090565b909192939460206104a561049f83856001950387528951610438565b97610445565b9301930191939290610474565b6104c79160208201915f81840391015261044b565b90565b346104fa576104da3660046103c3565b6104f66104e56122c2565b6104ed610311565b918291826104b2565b0390f35b610317565b5f80fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b90610525906103fd565b810190811067ffffffffffffffff82111761053f57604052565b610507565b90610557610550610311565b928361051b565b565b67ffffffffffffffff8111610577576105736020916103fd565b0190565b610507565b90825f939282370152565b9092919261059c61059782610559565b610544565b938185526020850190828401116105b8576105b69261057c565b565b610503565b9080601f830112156105db578160206105d893359101610587565b90565b6104ff565b90602082820312610610575f82013567ffffffffffffffff811161060b5761060892016105bd565b90565b61031f565b61031b565b905090565b61063f6106369260209261062d816103e5565b94858093610615565b938491016103f2565b0190565b90565b90565b61065561065a91610643565b610646565b9052565b61066e610675916020949361061a565b8092610649565b0190565b61068d610684610311565b9283928361065e565b03902090565b61069c91610679565b90565b1c90565b90565b6106b69060086106bb930261069f565b6106a3565b90565b906106c991546106a6565b90565b6106e2906106dd600e915f92610693565b6106be565b90565b6106ee90610643565b9052565b9190610705905f602085019401906106e5565b565b346107375761073361072261071d3660046105e0565b6106cc565b61072a610311565b918291826106f2565b0390f35b610317565b9060208282031261075557610752915f0161034e565b90565b61031b565b90565b61077161076c61077692610323565b61075a565b610323565b90565b6107829061075d565b90565b61078e90610779565b90565b9061079b90610785565b5f5260205260405f2090565b60018060a01b031690565b6107c29060086107c7930261069f565b6107a7565b90565b906107d591546107b2565b90565b6107ee906107e96001915f92610791565b6107ca565b90565b6107fa9061032e565b9052565b9190610811905f602085019401906107f1565b565b346108435761083f61082e61082936600461073c565b6107d8565b610836610311565b918291826107fe565b0390f35b610317565b9061085290610785565b5f5260205260405f2090565b5f1c90565b61086f6108749161085e565b6106a3565b90565b6108819054610863565b90565b60ff1690565b61089661089b9161085e565b610884565b90565b6108a8905461088a565b90565b6108b5905f610848565b906108cd60016108c65f8501610877565b930161089e565b90565b634e487b7160e01b5f52602160045260245ffd5b600311156108ee57565b6108d0565b906108fd826108e4565b565b610908906108f3565b90565b610914906108ff565b9052565b91602061093992949361093260408201965f8301906106e5565b019061090b565b565b3461096c5761095361094e36600461073c565b6108ab565b9061096861095f610311565b92839283610918565b0390f35b610317565b9190916040818403126109b15761098a835f830161034e565b92602082013567ffffffffffffffff81116109ac576109a992016105bd565b90565b61031f565b61031b565b906109c090610785565b5f5260205260405f2090565b6109d591610679565b90565b60ff1690565b6109ee9060086109f3930261069f565b6109d8565b90565b90610a0191546109de565b90565b610a1d610a2292610a186002935f946109b6565b6109cc565b6109f6565b90565b151590565b610a3390610a25565b9052565b9190610a4a905f60208501940190610a2a565b565b34610a7d57610a79610a68610a62366004610971565b90610a04565b610a70610311565b91829182610a37565b0390f35b610317565b34610ab057610a9a610a9536600461073c565b6124f0565b610aa2610311565b80610aac8161038a565b0390f35b610317565b5f80fd5b9081610100910312610ac85790565b610ab5565b67ffffffffffffffff8111610ae55760208091020190565b610507565b5f80fd5b90929192610b03610afe82610acd565b610544565b9381855260208086019202830192818411610b4057915b838310610b275750505050565b60208091610b35848661034e565b815201920191610b1a565b610aea565b9080601f83011215610b6357816020610b6093359101610aee565b90565b6104ff565b67ffffffffffffffff8111610b805760208091020190565b610507565b67ffffffffffffffff8111610ba357610b9f6020916103fd565b0190565b610507565b90929192610bbd610bb882610b85565b610544565b93818552602085019082840111610bd957610bd79261057c565b565b610503565b9080601f83011215610bfc57816020610bf993359101610ba8565b90565b6104ff565b929190610c15610c1082610b68565b610544565b9381855260208086019202810191838311610c6c5781905b838210610c3b575050505050565b813567ffffffffffffffff8111610c6757602091610c5c8784938701610bde565b815201910190610c2d565b6104ff565b610aea565b9080601f83011215610c8f57816020610c8c93359101610c01565b90565b6104ff565b91606083830312610d10575f83013567ffffffffffffffff8111610d0b5782610cbe918501610ab9565b92602081013567ffffffffffffffff8111610d065783610cdf918301610b45565b92604082013567ffffffffffffffff8111610d0157610cfe9201610c71565b90565b61031f565b61031f565b61031f565b61031b565b34610d4657610d42610d31610d2b366004610c94565b91612d7c565b610d39610311565b91829182610a37565b0390f35b610317565b34610d7957610d63610d5e36600461073c565b613151565b610d6b610311565b80610d758161038a565b0390f35b610317565b610d8a600d5f906106be565b90565b34610dbd57610d9d3660046103c3565b610db9610da8610d7e565b610db0610311565b918291826106f2565b0390f35b610317565b610dce60095f906107ca565b90565b34610e0157610de13660046103c3565b610dfd610dec610dc2565b610df4610311565b918291826107fe565b0390f35b610317565b5190565b60209181520190565b60200190565b610e229061032e565b9052565b90610e3381602093610e19565b0190565b60200190565b90610e5a610e54610e4d84610e06565b8093610e0a565b92610e13565b905f5b818110610e6a5750505090565b909192610e83610e7d6001928651610e26565b94610e37565b9101919091610e5d565b610ea29160208201915f818403910152610e3d565b90565b34610ed557610ed1610ec0610ebb36600461073c565b61328d565b610ec8610311565b91829182610e8d565b0390f35b610317565b90602082820312610f0a575f82013567ffffffffffffffff8111610f0557610f029201610ab9565b90565b61031f565b61031b565b90565b610f1b90610f0f565b9052565b9190610f32905f60208501940190610f12565b565b34610f6457610f60610f4f610f4a366004610eda565b61350f565b610f57610311565b91829182610f1f565b0390f35b610317565b90610f7390610785565b5f5260205260405f2090565b634e487b7160e01b5f525f60045260245ffd5b634e487b7160e01b5f52602260045260245ffd5b9060016002830492168015610fc6575b6020831014610fc157565b610f92565b91607f1691610fb6565b5f5260205f2090565b905f9291805490610ff3610fec83610fa6565b80946103e9565b916001811690815f1461104a575060011461100e575b505050565b61101b9192939450610fd0565b915f925b81841061103257505001905f8080611009565b6001816020929593955484860152019101929061101f565b92949550505060ff19168252151560200201905f8080611009565b9061106f91610fd9565b90565b9061109261108b92611082610311565b93848092611065565b038361051b565b565b905f106110a7576110a490611072565b90565b610f7f565b6110c2906110bd6010915f92610f69565b611094565b90565b60209181520190565b6110ed6110f66020936110fb936110e4816103e5565b938480936110c5565b958691016103f2565b6103fd565b0190565b6111149160208201915f8184039101526110ce565b90565b346111475761114361113261112d36600461073c565b6110ac565b61113a610311565b918291826110ff565b0390f35b610317565b5f80fd5b909182601f8301121561118a5781359167ffffffffffffffff831161118557602001926020830284011161118057565b610aea565b61114c565b6104ff565b909182601f830112156111c95781359167ffffffffffffffff83116111c45760200192600183028401116111bf57565b610aea565b61114c565b6104ff565b9091604082840312611228575f82013567ffffffffffffffff811161122357836111f9918401611150565b929093602082013567ffffffffffffffff811161121e5761121a920161118f565b9091565b61031f565b61031f565b61031b565b3461125f576112496112403660046111ce565b92919091613892565b611251610311565b8061125b8161038a565b0390f35b610317565b61127060115f906109f6565b90565b346112a3576112833660046103c3565b61129f61128e611264565b611296610311565b91829182610a37565b0390f35b610317565b91906040838203126112e8575f8301359067ffffffffffffffff82116112e3576112d7816112e09386016105bd565b9360200161034e565b90565b61031f565b61031b565b3461131c576113066113003660046112a8565b90613fcb565b61130e610311565b806113188161038a565b0390f35b610317565b61132a81610a25565b0361133157565b5f80fd5b9050359061134282611321565b565b9060208282031261135d5761135a915f01611335565b90565b61031b565b346113905761137a611375366004611344565b613ff7565b611382610311565b8061138c8161038a565b0390f35b610317565b60ff1690565b6113a481611395565b036113ab57565b5f80fd5b905035906113bc8261139b565b565b6113c781610643565b036113ce57565b5f80fd5b905035906113df826113be565b565b9091606082840312611416576114136113fc845f850161034e565b9361140a81602086016113af565b936040016113d2565b90565b61031b565b3461144a5761143461142e3660046113e1565b9161429a565b61143c610311565b806114468161038a565b0390f35b610317565b61145b600b5f906107ca565b90565b3461148e5761146e3660046103c3565b61148a61147961144f565b611481610311565b918291826107fe565b0390f35b610317565b61149f600c5f906107ca565b90565b346114d2576114b23660046103c3565b6114ce6114bd611493565b6114c5610311565b918291826107fe565b0390f35b610317565b34611505576114ef6114ea3660046105e0565b6142a7565b6114f7610311565b806115018161038a565b0390f35b610317565b346115395761152361151d36600461035d565b906145a6565b61152b610311565b806115358161038a565b0390f35b610317565b3461156c5761154e3660046103c3565b6115566145d7565b61155e610311565b806115688161038a565b0390f35b610317565b611587906115826004915f926109cc565b6109f6565b90565b346115ba576115b66115a56115a03660046105e0565b611571565b6115ad610311565b91829182610a37565b0390f35b610317565b346115ed576115d76115d23660046105e0565b6146c0565b6115df610311565b806115e98161038a565b0390f35b610317565b909182601f8301121561162c5781359167ffffffffffffffff831161162757602001926020830284011161162257565b610aea565b61114c565b6104ff565b90602082820312611662575f82013567ffffffffffffffff811161165d5761165992016115f2565b9091565b61031f565b61031b565b346116965761168061167a366004611631565b9061481c565b611688610311565b806116928161038a565b0390f35b610317565b346116c9576116ab3660046103c3565b6116b3614828565b6116bb610311565b806116c58161038a565b0390f35b610317565b346116fd576116e76116e1366004611631565b9061492e565b6116ef610311565b806116f98161038a565b0390f35b610317565b346117305761171a61171536600461073c565b6149a4565b611722610311565b8061172c8161038a565b0390f35b610317565b9060208282031261174e5761174b915f016113d2565b90565b61031b565b346117815761176b611766366004611735565b614a19565b611773610311565b8061177d8161038a565b0390f35b610317565b346117b6576117963660046103c3565b6117b26117a1614a24565b6117a9610311565b918291826107fe565b0390f35b610317565b346117eb576117e76117d66117d1366004610eda565b614a42565b6117de610311565b91829182610f1f565b0390f35b610317565b909160c08284031261184f57611808835f840161034e565b92611816816020850161034e565b92611824826040830161034e565b9261184c611835846060850161034e565b93611843816080860161034e565b9360a0016113d2565b90565b61031b565b34611889576118736118673660046117f0565b94939093929192614dff565b61187b610311565b806118858161038a565b0390f35b610317565b5f80fd5b5f80fd5b61189f81610f0f565b036118a657565b5f80fd5b905035906118b782611896565b565b91909160608184031261191e576118d06060610544565b925f8201359167ffffffffffffffff8311611919576118f482611912948301610bde565b5f86015261190582602083016118aa565b60208601526040016113d2565b6040830152565b611892565b61188e565b9190916040818403126119635761193c835f830161034e565b92602082013567ffffffffffffffff811161195e5761195b92016118b9565b90565b61031f565b61031b565b346119975761198161197b366004611923565b90615303565b611989610311565b806119938161038a565b0390f35b610317565b346119ca576119b46119af36600461073c565b615512565b6119bc610311565b806119c68161038a565b0390f35b610317565b9091606082840312611a35575f82013567ffffffffffffffff8111611a3057836119fa9184016105bd565b9260208301359067ffffffffffffffff8211611a2b57611a1f81611a289386016105bd565b936040016113d2565b90565b61031f565b61031f565b61031b565b34611a6957611a53611a4d3660046119cf565b916158e6565b611a5b610311565b80611a658161038a565b0390f35b610317565b634e487b7160e01b5f52603260045260245ffd5b5490565b5f5260205f2090565b611a9881611a82565b821015611ab257611aaa600191611a86565b910201905f90565b611a6e565b6008611ac281611a82565b821015611adf57611adc91611ad691611a8f565b906107ca565b90565b5f80fd5b34611b1357611b0f611afe611af9366004611735565b611ab7565b611b06610311565b918291826107fe565b0390f35b610317565b611b2191610679565b90565b611b3a90611b356003915f92611b18565b611094565b90565b34611b6d57611b69611b58611b533660046105e0565b611b24565b611b60610311565b918291826110ff565b0390f35b610317565b5490565b5f5260205f2090565b611b8881611b72565b821015611ba257611b9a600191611b76565b910201905f90565b611a6e565b6006611bb281611b72565b821015611bcf57611bcc91611bc691611b7f565b90611094565b90565b5f80fd5b34611c0357611bff611bee611be9366004611735565b611ba7565b611bf6610311565b918291826110ff565b0390f35b610317565b611c1e90611c196005915f92611b18565b611094565b90565b34611c5157611c4d611c3c611c373660046105e0565b611c08565b611c44610311565b918291826110ff565b0390f35b610317565b6007611c6181611b72565b821015611c7e57611c7b91611c7591611b7f565b90611094565b90565b5f80fd5b34611cb257611cae611c9d611c98366004611735565b611c56565b611ca5610311565b918291826110ff565b0390f35b610317565b34611ce757611cc73660046103c3565b611ce3611cd26158f3565b611cda610311565b918291826107fe565b0390f35b610317565b34611d1a57611d04611cff36600461073c565b61597b565b611d0c610311565b80611d168161038a565b0390f35b610317565b34611d4f57611d2f3660046103c3565b611d4b611d3a615a34565b611d42610311565b91829182610e8d565b0390f35b610317565b611d60600a5f906107ca565b90565b34611d9357611d733660046103c3565b611d8f611d7e611d54565b611d86610311565b918291826107fe565b0390f35b610317565b34611dc657611db0611dab36600461073c565b615ac5565b611db8610311565b80611dc28161038a565b0390f35b610317565b5f80fd5b60207f4b65793a206f70657261746f72206973206e6f74207265676973746572656400917f5072656469636174652e726f746174655072656469636174655369676e696e675f8201520152565b611e29603f6040926110c5565b611e3281611dcf565b0190565b611e4b9060208101905f818303910152611e1c565b90565b15611e5557565b611e5d610311565b62461bcd60e51b815280611e7360048201611e36565b0390fd5b611e83611e889161085e565b6107a7565b90565b611e959054611e77565b90565b60407f2773206f776e207369676e696e67206b65790000000000000000000000000000917f5072656469636174652e726f746174655072656469636174655369676e696e675f8201527f4b65793a206f70657261746f722063616e206f6e6c79206368616e676520697460208201520152565b611f1860526060926110c5565b611f2181611e98565b0190565b611f3a9060208101905f818303910152611f0b565b90565b15611f4457565b611f4c610311565b62461bcd60e51b815280611f6260048201611f25565b0390fd5b90565b611f7d611f78611f8292611f66565b61075a565b610323565b90565b611f8e90611f69565b90565b60407f6973746572656400000000000000000000000000000000000000000000000000917f5072656469636174652e726f746174655072656469636174655369676e696e675f8201527f4b65793a206e6577207369676e696e67206b657920616c72656164792072656760208201520152565b61201160476060926110c5565b61201a81611f91565b0190565b6120339060208101905f818303910152612004565b90565b1561203d57565b612045610311565b62461bcd60e51b81528061205b6004820161201e565b0390fd5b1b90565b9190600861208391029161207d60018060a01b038461205f565b9261205f565b9181191691161790565b90565b91906120a66120a16120ae93610785565b61208d565b908354612063565b9055565b5f90565b6120c8916120c26120b2565b91612090565b565b5f1b90565b906120e060018060a01b03916120ca565b9181191691161790565b906120ff6120fa61210692610785565b61208d565b82546120cf565b9055565b9061213d612124600161211e5f3390610848565b0161089e565b61213761213160016108f3565b916108f3565b14611e4e565b61216c3361216661216061215b61215660018890610791565b611e8b565b61032e565b9161032e565b14611f3d565b6121a361218361217e60018490610791565b611e8b565b61219d6121976121925f611f85565b61032e565b9161032e565b14612036565b6121b85f6121b360018590610791565b6120b6565b6121cd336121c860018490610791565b6120ea565b339161220b6122056121ff7f6c01cc00138da448a5e602c2eea1e9551a5bf0d2ae983466089ec720d7c0b74895610785565b92610785565b92610785565b92612214610311565b8061221e8161038a565b0390a4565b606090565b67ffffffffffffffff81116122405760208091020190565b610507565b9061225761225283612228565b610544565b918252565b61226590611072565b90565b9061227282611b72565b61227b81612245565b926122896020850191611b76565b5f915b8383106122995750505050565b6001602081926122a88561225c565b81520192019201919061228c565b6122bf90612268565b90565b6122ca612223565b506122d560066122b6565b90565b6122e9906122e4615ad0565b6123e9565b565b6122ff6122fa61230492611f66565b61075a565b610643565b90565b90565b61231e61231961232392612307565b61075a565b610643565b90565b634e487b7160e01b5f52601160045260245ffd5b61234961234f91939293610643565b92610643565b820391821161235a57565b612326565b90565b634e487b7160e01b5f52603160045260245ffd5b5490565b5f5260205f2090565b61238c81612376565b8210156123a65761239e60019161237a565b910201905f90565b611a6e565b6123b481612376565b80156123d55760019003906123d26123cc8383612383565b906120b6565b55565b612362565b60016123e69101610643565b90565b6123f25f6122eb565b5b8061240f6124096124046008611a82565b610643565b91610643565b146124e95761242961242360088390611a8f565b906107ca565b61243b6124358461032e565b9161032e565b1461244e57612449906123da565b6123f3565b6124949061248e612486612480600861247a61246a6008611a82565b612474600161230a565b9061233a565b90611a8f565b906107ca565b916008611a8f565b90612090565b6124a66124a1600861235f565b6123ab565b6124d07f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea491610785565b906124d9610311565b806124e38161038a565b0390a25b565b50506124e7565b6124f9906122d8565b565b5f90565b63ffffffff1690565b612511816124ff565b0361251857565b5f80fd5b3561252681612508565b90565b61253d61253861254292611f66565b61075a565b6124ff565b90565b60407f65726f0000000000000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2071755f8201527f6f72756d207468726573686f6c6420636f756e742063616e6e6f74206265207a60208201520152565b6125c560436060926110c5565b6125ce81612545565b0190565b6125e79060208101905f8183039101526125b8565b90565b156125f157565b6125f9610311565b62461bcd60e51b81528061260f600482016125d2565b0390fd5b5190565b60407f7475726573000000000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a204d695f8201527f736d61746368206265747765656e207369676e65727320616e64207369676e6160208201520152565b61269760456060926110c5565b6126a081612617565b0190565b6126b99060208101905f81830391015261268a565b90565b156126c357565b6126cb610311565b62461bcd60e51b8152806126e1600482016126a4565b0390fd5b356126ef816113be565b90565b60207f616e73616374696f6e2065787069726564000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2074725f8201520152565b61274c60316040926110c5565b612755816126f2565b0190565b61276e9060208101905f81830391015261273f565b90565b1561277857565b612780610311565b62461bcd60e51b81528061279660048201612759565b0390fd5b5f80fd5b5f80fd5b5f80fd5b9035906001602003813603038212156127e8570180359067ffffffffffffffff82116127e3576020019160018202360383136127de57565b6127a2565b61279e565b61279a565b9091826127fd8161280493610615565b809361057c565b0190565b6128199060209493612820936127ed565b8092610649565b0190565b909161283b90612832610311565b93849384612808565b03902090565b909161284c92612824565b90565b61285b6128609161085e565b6109d8565b90565b61286d905461284f565b90565b60207f736b20494420616c7265616479207370656e7400000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2074615f8201520152565b6128ca60336040926110c5565b6128d381612870565b0190565b6128ec9060208101905f8183039101526128bd565b90565b156128f657565b6128fe610311565b62461bcd60e51b815280612914600482016128d7565b0390fd5b909161292392612824565b90565b61293a61293561293f926124ff565b61075a565b610643565b90565b60407f66666572732066726f6d207461736b2071756f72756d207468726573686f6c64917f5072656469636174652e50726564696361746556657269666965643a206465705f8201527f6c6f79656420706f6c6963792071756f72756d207468726573686f6c6420646960208201520152565b6129c1606080926110c5565b6129ca81612942565b0190565b6129e39060208101905f8183039101526129b5565b90565b156129ed57565b6129f5610311565b62461bcd60e51b815280612a0b600482016129ce565b0390fd5b90612a1982610e06565b811015612a2a576020809102010190565b611a6e565b612a39905161032e565b90565b612a459061075d565b90565b60407f6420736f72746564000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2053695f8201527f676e657220616464726573736573206d75737420626520756e6971756520616e60208201520152565b612ac860486060926110c5565b612ad181612a48565b0190565b612aea9060208101905f818303910152612abb565b90565b90612af782612613565b811015612b08576020809102010190565b611a6e565b60207f76616c6964207369676e61747572650000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a20496e5f8201520152565b612b67602f6040926110c5565b612b7081612b0d565b0190565b612b899060208101905f818303910152612b5a565b90565b15612b9357565b612b9b610311565b62461bcd60e51b815280612bb160048201612b74565b0390fd5b60407f7200000000000000000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2053695f8201527f676e6572206973206e6f7420612072656769737465726564206f70657261746f60208201520152565b612c3560416060926110c5565b612c3e81612bb5565b0190565b612c579060208101905f818303910152612c28565b90565b15612c6157565b612c69610311565b62461bcd60e51b815280612c7f60048201612c42565b0390fd5b35612c8d8161033a565b90565b612ca4612c9f612ca992610643565b61075a565b610643565b90565b9190612cc681612cbf81612ccb956110c5565b809561057c565b6103fd565b0190565b612cd890612926565b9052565b93612d2793612d1291612d04612d1d94612d349b999a9660a08a01918a83035f8c0152612cac565b918783036020890152612cac565b956040850190612ccf565b60608301906106e5565b6080818403910152610e3d565b90565b90612d4360ff916120ca565b9181191691161790565b612d5690610a25565b90565b90565b90612d71612d6c612d7892612d4d565b612d59565b8254612d37565b9055565b9291612d866124fb565b50612daf612d9660c0860161251c565b612da8612da25f612529565b916124ff565b14156125ea565b612ddb612dbb83610e06565b612dd5612dcf612dca85612613565b610643565b91610643565b146126bc565b612e0342612dfc612df6612df160e089016126e5565b610643565b91610643565b1115612771565b612e34612e2f612e29612e246004612e1e895f8101906127a6565b91612841565b612863565b15610a25565b6128ef565b612e55612e50600e612e4a8760a08101906127a6565b91612918565b610877565b9283612e69612e635f6122eb565b91610643565b1415806130bd575b612e7a906129e6565b612e838561350f565b94612e8d5f6122eb565b5b80612ea1612e9b88610643565b91610643565b1015612fdc5780612eba612eb45f6122eb565b91610643565b1180612f7a575b612f5857612f5390612f4e612f356001612f2f612f29612f24612ef08e612ee98d8a90612aed565b5190615b1e565b612f1e8d612f18612f12612f0d612f088d8795612a0f565b612a2f565b61032e565b9161032e565b14612b8c565b84610791565b611e8b565b5f610848565b0161089e565b612f48612f4260016108f3565b916108f3565b14612c5a565b6123da565b612e8e565b612f60610311565b62461bcd60e51b815280612f7660048201612ad5565b0390fd5b50612f96612f91612f8c878490612a0f565b612a2f565b612a3c565b612fd5612fcf612fca612fc5612fc08a612fba88612fb4600161230a565b9061233a565b90612a0f565b612a2f565b612a3c565b610323565b91610323565b1115612ec1565b5092915093506130b89250612ff360208301612c83565b612fff60408401612c83565b61300b606085016126e5565b9161301a8560a08101906127a6565b9061309561302b885f8101906127a6565b909761303960c08b0161251c565b61304560e08c016126e5565b919261308361307d6130777fb4c99a6d727df238766e771095ad097c0bf9c2aef920b56b65c0ac529f36f2db9a610785565b9a610785565b9a612c90565b9a61308c610311565b97889788612cdc565b0390a46130b36001916130ad6004915f8101906127a6565b91612841565b612d5c565b600190565b50612e7a6130cd60c0870161251c565b6130df6130d987610643565b91612926565b149050612e71565b6130f8906130f3615ad0565b6130fa565b565b61310590600a6120ea565b61310f600a611e8b565b6131397fa1b8fcd417a2bb56a91d1fc6708faf8283b5730e55821393e70303e544aeec9291610785565b90613142610311565b8061314c8161038a565b0390a2565b61315a906130e7565b565b606090565b9061317361316e83610acd565b610544565b918252565b369037565b906131a261318a83613161565b926020806131988693610acd565b9201910390613178565b565b6131ad9061075d565b90565b6131b9906131a4565b90565b6131c590610779565b90565b6131d19061075d565b90565b6131dd906131c8565b90565b5f80fd5b60e01b90565b905051906131f7826113be565b565b906020828203126132125761320f915f016131ea565b90565b61031b565b61322090610779565b90565b61322c90613217565b9052565b91602061325192949361324a60408201965f8301906107f1565b0190613223565b565b61325b610311565b3d5f823e3d90fd5b9061326d9061032e565b9052565b61327a90610643565b5f1981146132885760010190565b612326565b9061329661315c565b506132a96132a46008611a82565b61317d565b6132b25f6122eb565b6132bb5f6122eb565b5b806132d86132d26132cd6008611a82565b610643565b91610643565b10156133f0576132f86132f36132ee600a611e8b565b6131b0565b6131bc565b602063778e55f391879061333a61332261331d61331760088990611a8f565b906107ca565b6131d4565b9461334561332e610311565b968795869485946131e4565b845260048401613230565b03915afa9081156133eb575f916133bd575b5061336a6133645f6122eb565b91610643565b1161337e575b613379906123da565b6132bc565b906133b5613379916133b061339e61339860088790611a8f565b906107ca565b6133ab8791849092612a0f565b613263565b613271565b919050613370565b6133de915060203d81116133e4575b6133d6818361051b565b8101906131f9565b5f613357565b503d6133cc565b613253565b505090915090565b5f90565b90359060016020038136030382121561343e570180359067ffffffffffffffff82116134395760200191600182023603831361343457565b6127a2565b61279e565b61279a565b60209181520190565b91906134668161345f8161346b95613443565b809561057c565b6103fd565b0190565b613478906124ff565b9052565b966134fc966134d66134f1976135039d9f9e9c9660e09c986134c26134cc9260208f996134ba906134e39c6101008d01918d5f818503910152612cac565b9901906107f1565b60408d01906107f1565b60608b01906106e5565b88830360808a015261344c565b9185830360a0870152612cac565b9660c083019061346f565b01906106e5565b565b60200190565b5190565b6135a66135979161351e6133f8565b5061352c815f8101906127a6565b93909161353b60208201612c83565b9033613549606083016126e5565b906135588360808101906133fc565b906135678560a08101906127a6565b94909361358260e061357b60c08a0161251c565b98016126e5565b9761358b610311565b9c8d9b60208d0161347c565b6020820181038252038261051b565b6135b86135b28261350b565b91613505565b2090565b5090565b5090565b5f90565b90359060016020038136030382121561360a570180359067ffffffffffffffff82116136055760200191602082023603831361360057565b6127a2565b61279e565b61279a565b9082101561362a57602061362692028101906135c8565b9091565b611a6e565b61363a913691610aee565b90565b90565b61364f61365591939293610643565b92610643565b820180921161366057565b612326565b906136715f19916120ca565b9181191691161790565b90565b9061369361368e61369a92612c90565b61367b565b8254613665565b9055565b6136a7906108f3565b90565b90565b906136c26136bd6136c99261369e565b6136aa565b8254612d37565b9055565b905090565b90565b905090565b90565b6136e69061032e565b9052565b906136f7816020936136dd565b0190565b5061370a90602081019061034e565b90565b60200190565b9161372182613727926136d5565b926136da565b90815f905b82821061373a575050505090565b9091929361375c61375660019261375188866136fb565b6136ea565b9561370d565b92019092919261372c565b906137729291613713565b90565b5f80fd5b5f80fd5b5f80fd5b90356001602003823603038112156137c257016020813591019167ffffffffffffffff82116137bd5760208202360383136137b857565b613779565b613775565b61377d565b60200190565b916137db826137e1926136cd565b926136d2565b90815f905b8282106137f4575050505090565b9091929361381761381160019261380b8886613781565b90613767565b956137c7565b9201909291926137e6565b909161382d926137cd565b90565b61384461383b610311565b92839283613822565b03902090565b905090565b90918261385f816138669361384a565b809361057c565b0190565b90916138759261384f565b90565b61388c613883610311565b9283928361386a565b03902090565b919390926138a18385906135bc565b6138bd6138b76138b28885906135c0565b610643565b91610643565b03613b875791925f949194506138d161315c565b506138da6120b2565b506138e36135c4565b935b846139026138fc6138f78688906135c0565b610643565b91610643565b14613b335794909361392261391c8795949587849161360f565b9061362f565b9561392b6135c4565b935b8461394861394261393d8b610e06565b610643565b91610643565b1015613b195761396161395c898790612a0f565b612a2f565b956139756139705f8990610848565b61363d565b9761398260018a0161089e565b61399461398e5f6108f3565b916108f3565b14613afd576139a16135c4565b936139aa6135c4565b945b856139c86139c26139bd6008611a82565b610643565b91610643565b14613a93576139e76139e26139dd600a611e8b565b6131b0565b6131bc565b602063778e55f3918c90613a28613a10613a0b613a058d6008611a8f565b906107ca565b6131d4565b94613a33613a1c610311565b968795869485946131e4565b845260048401613230565b03915afa8015613a8e57613a5892613a52925f92613a5e575b50613640565b956123da565b946139ac565b613a8091925060203d8111613a87575b613a78818361051b565b8101906131f9565b905f613a4c565b503d613a6e565b613253565b613ae6949950613ae19297955099909599979297613ab3815f840161367e565b613ace613ac8613ac3600d610877565b610643565b91610643565b105f14613af45760016002915b016136ad565b6123da565b94959190929594939461392d565b60018091613adb565b5f63ba6435f160e01b815280613b156004820161038a565b0390fd5b929350939550613b28906123da565b9491949390926138e5565b613b6f92919450613b6990949193917ff3fcd2dadc5f6ebf9c1e9310a9e986a14079afc7ecf26784853ea9a3ac90721b95613830565b92613878565b91613b78610311565b80613b828161038a565b0390a3565b5f63371821d760e21b815280613b9f6004820161038a565b0390fd5b90613bb591613bb0615ad0565b613f0e565b565b90565b60207f616e6e6f7420626520656d707479000000000000000000000000000000000000917f5072656469636174652e736574506f6c6963793a20706f6c69637920494420635f8201520152565b613c14602e6040926110c5565b613c1d81613bba565b0190565b613c369060208101905f818303910152613c07565b90565b15613c4057565b613c48610311565b62461bcd60e51b815280613c5e60048201613c21565b0390fd5b60207f6f74207265676973746572656400000000000000000000000000000000000000917f5072656469636174652e736574506f6c6963793a20706f6c696379204944206e5f8201520152565b613cbc602d6040926110c5565b613cc581613c62565b0190565b613cde9060208101905f818303910152613caf565b90565b15613ce857565b613cf0610311565b62461bcd60e51b815280613d0660048201613cc9565b0390fd5b601f602091010490565b91906008613d2f910291613d295f198461205f565b9261205f565b9181191691161790565b9190613d4f613d4a613d5793612c90565b61367b565b908354613d14565b9055565b613d6d91613d676135c4565b91613d39565b565b5b818110613d7b575050565b80613d885f600193613d5b565b01613d70565b9190601f8111613d9e575b505050565b613daa613dcf93610fd0565b906020613db684613d0a565b83019310613dd7575b613dc890613d0a565b0190613d6f565b5f8080613d99565b9150613dc881929050613dbf565b90613df5905f199060080261069f565b191690565b81613e0491613de5565b906002021790565b90613e16816103e5565b9067ffffffffffffffff8211613ed657613e3a82613e348554610fa6565b85613d8e565b602090601f8311600114613e6e57918091613e5d935f92613e62575b5050613dfa565b90555b565b90915001515f80613e56565b601f19831691613e7d85610fd0565b925f5b818110613ebe57509160029391856001969410613ea4575b50505002019055613e60565b613eb4910151601f841690613de5565b90555f8080613e98565b91936020600181928787015181550195019201613e80565b610507565b90613ee591613e0c565b565b613ef09161061a565b90565b613f0890613eff610311565b91829182613ee7565b03902090565b90613f3b613f23613f1e84613bb7565b61350b565b613f35613f2f5f6122eb565b91610643565b11613c39565b613f6a613f52613f4d600e8590610693565b610877565b613f64613f5e5f6122eb565b91610643565b11613ce1565b613f7f82613f7a60108490610f69565b613edb565b90613fb3613fad7ffbc30d1514eac402cb2045f1dd80ec75fbc997db6f719421b6d7490f4bfb779d93610785565b91613ef3565b91613fbc610311565b80613fc68161038a565b0390a3565b90613fd591613ba3565b565b613fe890613fe3615ad0565b613fea565b565b613ff5906011612d5c565b565b61400090613fd7565b565b906140159291614010615ad0565b61416d565b565b6140209061075d565b90565b61402c90614017565b90565b61403890610779565b90565b6140449061032e565b90565b6140508161403b565b0361405757565b5f80fd5b9050519061406882614047565b565b6bffffffffffffffffffffffff1690565b6140848161406a565b0361408b57565b5f80fd5b9050519061409c8261407b565b565b91906040838203126140d8576140d1906140b86040610544565b936140c5825f830161405b565b5f86015260200161408f565b6020830152565b61188e565b906040828203126140f6576140f3915f0161409e565b90565b61031b565b61410490611395565b9052565b91602061412992949361412260408201965f8301906140fb565b01906106e5565b565b614135905161403b565b90565b9081549168010000000000000000831015614168578261416091600161416695018155612383565b90612090565b565b610507565b9160409061418b614186614181600b611e8b565b614023565b61402f565b6141ad63adc804da9492946141b86141a1610311565b968795869485946131e4565b845260048401614108565b03915afa8015614295575f6141d9916141de938291614267575b500161412b565b613217565b6141f06141ea8361032e565b9161032e565b0361424b57614209614202600861235f565b8290614138565b6142337f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f191610785565b9061423c610311565b806142468161038a565b0390a2565b5f63a4e34a6960e01b8152806142636004820161038a565b0390fd5b614288915060403d811161428e575b614280818361051b565b8101906140dd565b5f6141d2565b503d614276565b613253565b906142a59291614002565b565b6142d36142bb6142b683613bb7565b61350b565b6142cd6142c75f6122eb565b91610643565b11613c39565b6143026142ea6142e5600e8490610693565b610877565b6142fc6142f65f6122eb565b91610643565b11613ce1565b6143178161431260103390610f69565b613edb565b339061434c6143467ffbc30d1514eac402cb2045f1dd80ec75fbc997db6f719421b6d7490f4bfb779d93610785565b91613ef3565b91614355610311565b8061435f8161038a565b0390a3565b9061437691614371615ad0565b6144a2565b565b60207f206f70657261746f7220616c7265616479207265676973746572656400000000917f5072656469636174652e72656769737465724f70657261746f72546f4156533a5f8201520152565b6143d2603c6040926110c5565b6143db81614378565b0190565b6143f49060208101905f8183039101526143c5565b90565b156143fe57565b614406610311565b62461bcd60e51b81528061441c600482016143df565b0390fd5b61442a6040610544565b90565b9061443790610643565b9052565b90614445906108f3565b9052565b6144539051610643565b90565b61446090516108f3565b90565b9061448e60206001614494946144865f82016144805f8801614449565b9061367e565b019201614456565b906136ad565b565b906144a091614463565b565b90614564906144de6144be6144b960018490610791565b611e8b565b6144d86144d26144cd5f611f85565b61032e565b9161032e565b146143f7565b6145156144f56144f060018490610791565b611e8b565b61450f6145096145045f611f85565b61032e565b9161032e565b14612036565b6145535f614543600161453a61453261452c614420565b946122eb565b5f850161442d565b6020830161443b565b61454e5f8690610848565b614496565b61455f83916001610791565b6120ea565b61458e7f4d0eb1f4bac8744fd2be119845e23b3befc88094b42bcda1204c65694a00f9e591610785565b90614597610311565b806145a18161038a565b0390a2565b906145b091614364565b565b6145ba615ad0565b6145c26145c4565b565b6145d56145d05f611f85565b615b40565b565b6145df6145b2565b565b6145f2906145ed615ad0565b614627565b565b6145fd9061075d565b90565b614609906145f4565b90565b61461590610779565b90565b5f91031261462257565b61031b565b61464161463c614637600c611e8b565b614600565b61460c565b9063a98fb35590823b156146bb576146789261466d5f8094614661610311565b968795869485936131e4565b8352600483016110ff565b03925af180156146b65761468a575b50565b6146a9905f3d81116146af575b6146a1818361051b565b810190614618565b5f614687565b503d614697565b613253565b6131e0565b6146c9906145e1565b565b906146dd916146d8615ad0565b61477c565b565b5090565b91908110156146f3576020020190565b611a6e565b9061470290610785565b5f5260205260405f2090565b9161471c8261472292610e0a565b926136da565b90815f905b828210614735575050505090565b9091929361475761475160019261474c88866136fb565b610e26565b9561370d565b920190929192614727565b90916147799260208301925f81850391015261470e565b90565b6147855f6122eb565b5b806147a361479d6147988587906146df565b610643565b91610643565b10156147df576147da906147d55f6147d0600f6147ca6147c5888a88916146e3565b612c83565b906146f8565b612d5c565b6123da565b614786565b50907f05440a044a44c57aadc302acd45d200977ad0c2da22575dc91c0e07a760ce78c9161481761480e610311565b92839283614762565b0390a1565b90614826916146cb565b565b614830615b5e565b6148386158f3565b61484a6148448361032e565b9161032e565b0361485a5761485890615b40565b565b614875905f91829163118cdaa760e01b8352600483016107fe565b0390fd5b9061488b91614886615ad0565b61488d565b565b6148965f6122eb565b5b806148b46148ae6148a98587906146df565b610643565b91610643565b10156148f1576148ec906148e760016148e2600f6148dc6148d7888a88916146e3565b612c83565b906146f8565b612d5c565b6123da565b614897565b50907f33a805a6149751d70281b1283390e2798db286558e2b64bbe594c79141f736cc91614929614920610311565b92839283614762565b0390a1565b9061493891614879565b565b61494b90614946615ad0565b61494d565b565b61495890600c6120ea565b614962600c611e8b565b61498c7f2a623245c0f1c5741f2a4c247a58842872f1fdf8a31e66d031dd1cd1532e89a791610785565b90614995610311565b8061499f8161038a565b0390a2565b6149ad9061493a565b565b6149c0906149bb615ad0565b6149c2565b565b6149cd90600d61367e565b6149d7600d610877565b614a017f76f50103a7b1e136f23c29045235143c62e36705e8253019cb1253d9062bc6d491612c90565b90614a0a610311565b80614a148161038a565b0390a2565b614a22906149af565b565b614a2c6120b2565b50614a3f5f614a39615b6b565b01611e8b565b90565b614ae4614ad591614a516133f8565b50614a5f815f8101906127a6565b939091614a6e60208201612c83565b90614a7b60408201612c83565b614a87606083016126e5565b90614a968360808101906133fc565b90614aa58560a08101906127a6565b949093614ac060e0614ab960c08a0161251c565b98016126e5565b97614ac9610311565b9c8d9b60208d0161347c565b6020820181038252038261051b565b614af6614af08261350b565b91613505565b2090565b60401c90565b614b0c614b1191614afa565b6109d8565b90565b614b1e9054614b00565b90565b67ffffffffffffffff1690565b614b3a614b3f9161085e565b614b21565b90565b614b4c9054614b2e565b90565b67ffffffffffffffff1690565b614b70614b6b614b7592611f66565b61075a565b614b4f565b90565b614b8c614b87614b9192612307565b61075a565b614b4f565b90565b614b9d90610779565b90565b90614bb367ffffffffffffffff916120ca565b9181191691161790565b614bd1614bcc614bd692614b4f565b61075a565b614b4f565b90565b90565b90614bf1614bec614bf892614bbd565b614bd9565b8254614ba0565b9055565b60401b90565b90614c1668ff000000000000000091614bfc565b9181191691161790565b90614c35614c30614c3c92612d4d565b612d59565b8254614c02565b9055565b614c4990614b78565b9052565b9190614c60905f60208501940190614c40565b565b92949194939093614c71615b8f565b95614c86614c805f8901614b14565b15610a25565b95614c925f8901614b42565b80614ca5614c9f5f614b5c565b91614b4f565b1480614dbf575b90614cc0614cba6001614b78565b91614b4f565b1480614d97575b614cd2909115610a25565b9081614d86575b50614d6a57614d0295614cf7614cef6001614b78565b5f8b01614bdc565b87614d58575b614dc6565b614d0a575b50565b614d17905f809101614c20565b6001614d4f7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291614d46610311565b91829182614c4d565b0390a15f614d07565b614d6560015f8b01614c20565b614cfd565b5f63f92ee8a960e01b815280614d826004820161038a565b0390fd5b614d91915015610a25565b5f614cd9565b50614cd2614da430614b94565b3b614db7614db15f6122eb565b91610643565b149050614cc7565b5087614cac565b614def915092614de8614dfd969593614de1614df696615b40565b600a6120ea565b600b6120ea565b600c6120ea565b600d61367e565b565b90614e0d9594939291614c62565b565b90614e2d614e27614e22600f33906146f8565b612863565b15610a25565b614e3c57614e3a91614fe4565b565b5f6332c5b57b60e11b815280614e546004820161038a565b0390fd5b60207f20726567697374726174696f6e7320617265206e6f7420616c6c6f7765640000917f5072656469636174652e72656769737465724f70657261746f72546f4156533a5f8201520152565b614eb2603e6040926110c5565b614ebb81614e58565b0190565b614ed49060208101905f818303910152614ea5565b90565b15614ede57565b614ee6610311565b62461bcd60e51b815280614efc60048201614ebf565b0390fd5b614f0a9051610f0f565b90565b614f176060610544565b90565b52565b90614f2790610f0f565b9052565b60209181520190565b614f53614f5c602093614f6193614f4a8161350b565b93848093614f2b565b958691016103f2565b6103fd565b0190565b614f6e90610f0f565b9052565b614f7b90610643565b9052565b90614fbb90604080614f9e606084015f8701518582035f870152614f34565b94614fb160208201516020860190614f65565b0151910190614f72565b90565b91614fe192614fd460408201935f8301906107f1565b6020818403910152614f7f565b90565b9091614ff8614ff36011612863565b614ed7565b61502f61500f61500a60018590610791565b611e8b565b61502961502361501e5f611f85565b61032e565b9161032e565b146143f7565b61506661504661504160018590610791565b611e8b565b61506061505a6150555f611f85565b61032e565b9161032e565b14612036565b61506e6135c4565b926150776135c4565b935b8461509561508f61508a6008611a82565b610643565b91610643565b14615161576150b46150af6150aa600a611e8b565b6131b0565b6131bc565b602063778e55f39133906150f66150de6150d96150d360088d90611a8f565b906107ca565b6131d4565b946151016150ea610311565b968795869485946131e4565b845260048401613230565b03915afa801561515c5761512692615120925f9261512c575b50613640565b946123da565b93615079565b61514e91925060203d8111615155575b615146818361051b565b8101906131f9565b905f61511a565b503d61513c565b613253565b919350918161518161517b615176600d610877565b610643565b91610643565b101561518d575b505050565b6151c66151d7926151b660016151ad6151a4614420565b935f850161442d565b6020830161443b565b6151c15f3390610848565b614496565b6151d233916001610791565b6120ea565b6152215f820151916152186151fa60406151f360208501614f00565b9301614449565b9161520f615206614f0d565b955f8701614f1a565b60208501614f1d565b6040830161442d565b61523b615236615231600c611e8b565b614600565b61460c565b90639926ee7d90339092803b156152fe576152695f809461527461525d610311565b978896879586946131e4565b845260048401614fbe565b03925af180156152f9576152cd575b50336152af7f4d0eb1f4bac8744fd2be119845e23b3befc88094b42bcda1204c65694a00f9e591610785565b906152b8610311565b806152c28161038a565b0390a25f8080615188565b6152ec905f3d81116152f2575b6152e4818361051b565b810190614618565b5f615283565b503d6152da565b613253565b6131e0565b9061530d91614e0f565b565b6153209061531b615ad0565b6153ca565b565b60207f4156533a206f70657261746f72206973206e6f74207265676973746572656400917f5072656469636174652e646572656769737465724f70657261746f7246726f6d5f8201520152565b61537c603f6040926110c5565b61538581615322565b0190565b61539e9060208101905f81830391015261536f565b90565b156153a857565b6153b0610311565b62461bcd60e51b8152806153c660048201615389565b0390fd5b6153fc6153e360016153dd5f8590610848565b0161089e565b6153f56153ef5f6108f3565b916108f3565b14156153a1565b61543a5f61542a6002615421615419615413614420565b946122eb565b5f850161442d565b6020830161443b565b6154355f8490610848565b614496565b61545461544f61544a600c611e8b565b614600565b61460c565b63a364f4da82823b1561550d5761548a9261547f5f8094615473610311565b968795869485936131e4565b8352600483016107fe565b03925af18015615508576154dc575b506154c47f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d91610785565b906154cd610311565b806154d78161038a565b0390a2565b6154fb905f3d8111615501575b6154f3818361051b565b810190614618565b5f615499565b503d6154e9565b613253565b6131e0565b61551b9061530f565b565b90615530929161552b615ad0565b6157e3565b565b90565b61553f9054610fa6565b90565b60207f7869737473000000000000000000000000000000000000000000000000000000917f5072656469636174652e6465706c6f79506f6c6963793a20706f6c69637920655f8201520152565b61559c60256040926110c5565b6155a581615542565b0190565b6155be9060208101905f81830391015261558f565b90565b156155c857565b6155d0610311565b62461bcd60e51b8152806155e6600482016155a9565b0390fd5b60407f726f000000000000000000000000000000000000000000000000000000000000917f5072656469636174652e6465706c6f79506f6c6963793a2071756f72756d20745f8201527f68726573686f6c64206d7573742062652067726561746572207468616e207a6560208201520152565b61566a60426060926110c5565b615673816155ea565b0190565b61568c9060208101905f81830391015261565d565b90565b1561569657565b61569e610311565b62461bcd60e51b8152806156b460048201615677565b0390fd5b60207f7472696e672063616e6e6f7420626520656d7074790000000000000000000000917f5072656469636174652e6465706c6f79506f6c6963793a20706f6c69637920735f8201520152565b61571260356040926110c5565b61571b816156b8565b0190565b6157349060208101905f818303910152615705565b90565b1561573e57565b615746610311565b62461bcd60e51b81528061575c6004820161571f565b0390fd5b90565b5f5260205f2090565b5490565b6157798161576c565b8210156157935761578b600191615763565b910201905f90565b611a6e565b91906157a9576157a791613e0c565b565b610f7f565b90815491680100000000000000008310156157de57826157d69160016157dc95018155615770565b90615798565b565b610507565b909161588e906158206158086158036157fe60038790611b18565b615532565b615535565b61581a6158145f6122eb565b91610643565b146155c1565b61583c816158366158305f6122eb565b91610643565b1161568f565b61586861585061584b86613bb7565b61350b565b61586261585c5f6122eb565b91610643565b11615737565b61587d8461587860038690611b18565b613edb565b615889600e8490610693565b61367e565b6158a261589b6006615760565b82906157ae565b6158e16158cf7fb6487025b06543ad686bdaa829e2b07863fd163cacd2e0f031340308ec09584e92613ef3565b926158d8610311565b918291826110ff565b0390a2565b906158f1929161551d565b565b6158fb6120b2565b5061590e5f615908615bb3565b01611e8b565b90565b6159229061591d615ad0565b615924565b565b61592f90600b6120ea565b615939600b611e8b565b6159637fcb08c26360210256eb3fd98640509ba95a8a716bc3aa4aadc738e333102de73791610785565b9061596c610311565b806159768161038a565b0390a2565b61598490615911565b565b60209181520190565b6159999054611e77565b90565b60010190565b906159bf6159b96159b284611a82565b8093615986565b92611a86565b905f5b8181106159cf5750505090565b9091926159ef6159e96001926159e48761598f565b610e26565b9461599c565b91019190916159c2565b90615a03916159a2565b90565b90615a26615a1f92615a16610311565b938480926159f9565b038361051b565b565b615a3190615a06565b90565b615a3c61315c565b50615a476008615a28565b90565b615a5b90615a56615ad0565b615a5d565b565b615a71615a68615bb3565b5f8391016120ea565b615a79614a24565b90615aad615aa77f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270093610785565b91610785565b91615ab6610311565b80615ac08161038a565b0390a3565b615ace90615a4a565b565b615ad8614a24565b615af1615aeb615ae6615b5e565b61032e565b9161032e565b03615af857565b615b1a615b03615b5e565b5f91829163118cdaa760e01b8352600483016107fe565b0390fd5b615b3d91615b3491615b2e6120b2565b50615c1a565b90929192615cee565b90565b615b5c90615b575f80615b51615bb3565b016120b6565b615dbf565b565b615b666120b2565b503390565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b5f90565b90565b615bf2615bed615bf792615bdb565b61075a565b610643565b90565b615c0e615c09615c1392610643565b6120ca565b610f0f565b90565b5f90565b919091615c256120b2565b50615c2e615bd7565b50615c376133f8565b50615c418361350b565b615c54615c4e6041615bde565b91610643565b145f14615c9b57615c949192615c686133f8565b50615c716133f8565b50615c7a615c16565b506020810151606060408301519201515f1a909192615ea4565b9192909190565b50615ca55f611f85565b90615cb9615cb460029461350b565b615bfa565b91929190565b60041115615cc957565b6108d0565b90615cd882615cbf565b565b615ce6615ceb9161085e565b612c90565b90565b80615d01615cfb5f615cce565b91615cce565b145f14615d0c575050565b80615d20615d1a6001615cce565b91615cce565b145f14615d43575f63f645eedf60e01b815280615d3f6004820161038a565b0390fd5b80615d57615d516002615cce565b91615cce565b145f14615d8557615d81615d6a83615cda565b5f91829163fce698f760e01b8352600483016106f2565b0390fd5b615d98615d926003615cce565b91615cce565b14615da05750565b615dbb905f9182916335e2f38360e21b835260048301610f1f565b0390fd5b615dc7615b6b565b615ddf615dd55f8301611e8b565b915f8491016120ea565b90615e13615e0d7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e093610785565b91610785565b91615e1c610311565b80615e268161038a565b0390a3565b90565b615e42615e3d615e4792615e2b565b61075a565b610643565b90565b615e7f615e8694615e75606094989795615e6b608086019a5f870190610f12565b60208501906140fb565b6040830190610f12565b0190610f12565b565b615e9c615e97615ea192611f66565b6120ca565b610f0f565b90565b939293615eaf6120b2565b50615eb8615bd7565b50615ec16133f8565b50615ecb85615cda565b615efd615ef77f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0615e2e565b91610643565b11615f8a5790615f20602094955f94939293615f17610311565b94859485615e4a565b838052039060015afa15615f8557615f385f516120ca565b80615f53615f4d615f485f611f85565b61032e565b9161032e565b14615f69575f91615f635f615e88565b91929190565b50615f735f611f85565b600191615f7f5f615e88565b91929190565b613253565b505050615f965f611f85565b906003929192919056fea26469706673582212209cf7804c8fa6e0185c0cf437200aeb210de65b262e033f9a8c2053bc5d498fd864736f6c634300081c0033
Deployed Bytecode
0x60806040526004361015610013575b611dcb565b61001d5f3561030b565b80621ba1eb146103065780630b3ce015146103015780630c68603b146102fc5780630ff26fd1146102f757806313e7c9d8146102f2578063140a16bc146102ed578063175188e8146102e857806318cea58d146102e35780631a8d0de2146102de57806320af7390146102d9578063245a7bfc146102d457806333cfb7b7146102cf57806338d11d3c146102ca578063403dc365146102c55780635140a548146102c05780635890e59c146102bb5780635b9a64dc146102b65780635d43707b146102b157806366f17e73146102ac57806368304835146102a75780636b3aa72e146102a25780636b4c991b1461029d5780636d224f8d14610298578063715018a614610293578063749dccc71461028e578063750521f514610289578063786bf3c31461028457806379ba50971461027f5780638376f7f81461027a578063862621ef146102755780638ad96602146102705780638da5cb5b1461026b578063949528fc1461026657806395b6ef0c146102615780639926ee7d1461025c578063a364f4da14610257578063c0443c5f14610252578063d574ea3d1461024d578063d9d4e99f14610248578063ddb49ce114610243578063ddf6a51b1461023e578063df93506514610239578063e30c397814610234578063e3b05f2f1461022f578063e481af9d1461022a578063ea4d3c9b146102255763f2fde38b0361000e57611d98565b611d63565b611d1f565b611cec565b611cb7565b611c82565b611c21565b611bd3565b611b3d565b611ae3565b611a3a565b61199c565b611968565b611854565b6117bb565b611786565b611753565b611702565b6116ce565b61169b565b611667565b6115bf565b61158a565b61153e565b61150a565b6114d7565b6114a2565b61145e565b61141b565b611362565b6112ed565b611273565b61122d565b611117565b610f34565b610ea5565b610dd1565b610d8d565b610d4b565b610d15565b610a82565b610a4c565b61093b565b610813565b610707565b6104ca565b61038f565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b60018060a01b031690565b61033790610323565b90565b6103438161032e565b0361034a57565b5f80fd5b9050359061035b8261033a565b565b91906040838203126103855780610379610382925f860161034e565b9360200161034e565b90565b61031b565b5f0190565b346103be576103a86103a236600461035d565b9061210a565b6103b0610311565b806103ba8161038a565b0390f35b610317565b5f9103126103cd57565b61031b565b5190565b60209181520190565b60200190565b5190565b60209181520190565b90825f9392825e0152565b601f801991011690565b61042661042f6020936104349361041d816103e5565b938480936103e9565b958691016103f2565b6103fd565b0190565b9061044291610407565b90565b60200190565b9061045f610458836103d2565b80926103d6565b9081610470602083028401946103df565b925f915b83831061048357505050505090565b909192939460206104a561049f83856001950387528951610438565b97610445565b9301930191939290610474565b6104c79160208201915f81840391015261044b565b90565b346104fa576104da3660046103c3565b6104f66104e56122c2565b6104ed610311565b918291826104b2565b0390f35b610317565b5f80fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b90610525906103fd565b810190811067ffffffffffffffff82111761053f57604052565b610507565b90610557610550610311565b928361051b565b565b67ffffffffffffffff8111610577576105736020916103fd565b0190565b610507565b90825f939282370152565b9092919261059c61059782610559565b610544565b938185526020850190828401116105b8576105b69261057c565b565b610503565b9080601f830112156105db578160206105d893359101610587565b90565b6104ff565b90602082820312610610575f82013567ffffffffffffffff811161060b5761060892016105bd565b90565b61031f565b61031b565b905090565b61063f6106369260209261062d816103e5565b94858093610615565b938491016103f2565b0190565b90565b90565b61065561065a91610643565b610646565b9052565b61066e610675916020949361061a565b8092610649565b0190565b61068d610684610311565b9283928361065e565b03902090565b61069c91610679565b90565b1c90565b90565b6106b69060086106bb930261069f565b6106a3565b90565b906106c991546106a6565b90565b6106e2906106dd600e915f92610693565b6106be565b90565b6106ee90610643565b9052565b9190610705905f602085019401906106e5565b565b346107375761073361072261071d3660046105e0565b6106cc565b61072a610311565b918291826106f2565b0390f35b610317565b9060208282031261075557610752915f0161034e565b90565b61031b565b90565b61077161076c61077692610323565b61075a565b610323565b90565b6107829061075d565b90565b61078e90610779565b90565b9061079b90610785565b5f5260205260405f2090565b60018060a01b031690565b6107c29060086107c7930261069f565b6107a7565b90565b906107d591546107b2565b90565b6107ee906107e96001915f92610791565b6107ca565b90565b6107fa9061032e565b9052565b9190610811905f602085019401906107f1565b565b346108435761083f61082e61082936600461073c565b6107d8565b610836610311565b918291826107fe565b0390f35b610317565b9061085290610785565b5f5260205260405f2090565b5f1c90565b61086f6108749161085e565b6106a3565b90565b6108819054610863565b90565b60ff1690565b61089661089b9161085e565b610884565b90565b6108a8905461088a565b90565b6108b5905f610848565b906108cd60016108c65f8501610877565b930161089e565b90565b634e487b7160e01b5f52602160045260245ffd5b600311156108ee57565b6108d0565b906108fd826108e4565b565b610908906108f3565b90565b610914906108ff565b9052565b91602061093992949361093260408201965f8301906106e5565b019061090b565b565b3461096c5761095361094e36600461073c565b6108ab565b9061096861095f610311565b92839283610918565b0390f35b610317565b9190916040818403126109b15761098a835f830161034e565b92602082013567ffffffffffffffff81116109ac576109a992016105bd565b90565b61031f565b61031b565b906109c090610785565b5f5260205260405f2090565b6109d591610679565b90565b60ff1690565b6109ee9060086109f3930261069f565b6109d8565b90565b90610a0191546109de565b90565b610a1d610a2292610a186002935f946109b6565b6109cc565b6109f6565b90565b151590565b610a3390610a25565b9052565b9190610a4a905f60208501940190610a2a565b565b34610a7d57610a79610a68610a62366004610971565b90610a04565b610a70610311565b91829182610a37565b0390f35b610317565b34610ab057610a9a610a9536600461073c565b6124f0565b610aa2610311565b80610aac8161038a565b0390f35b610317565b5f80fd5b9081610100910312610ac85790565b610ab5565b67ffffffffffffffff8111610ae55760208091020190565b610507565b5f80fd5b90929192610b03610afe82610acd565b610544565b9381855260208086019202830192818411610b4057915b838310610b275750505050565b60208091610b35848661034e565b815201920191610b1a565b610aea565b9080601f83011215610b6357816020610b6093359101610aee565b90565b6104ff565b67ffffffffffffffff8111610b805760208091020190565b610507565b67ffffffffffffffff8111610ba357610b9f6020916103fd565b0190565b610507565b90929192610bbd610bb882610b85565b610544565b93818552602085019082840111610bd957610bd79261057c565b565b610503565b9080601f83011215610bfc57816020610bf993359101610ba8565b90565b6104ff565b929190610c15610c1082610b68565b610544565b9381855260208086019202810191838311610c6c5781905b838210610c3b575050505050565b813567ffffffffffffffff8111610c6757602091610c5c8784938701610bde565b815201910190610c2d565b6104ff565b610aea565b9080601f83011215610c8f57816020610c8c93359101610c01565b90565b6104ff565b91606083830312610d10575f83013567ffffffffffffffff8111610d0b5782610cbe918501610ab9565b92602081013567ffffffffffffffff8111610d065783610cdf918301610b45565b92604082013567ffffffffffffffff8111610d0157610cfe9201610c71565b90565b61031f565b61031f565b61031f565b61031b565b34610d4657610d42610d31610d2b366004610c94565b91612d7c565b610d39610311565b91829182610a37565b0390f35b610317565b34610d7957610d63610d5e36600461073c565b613151565b610d6b610311565b80610d758161038a565b0390f35b610317565b610d8a600d5f906106be565b90565b34610dbd57610d9d3660046103c3565b610db9610da8610d7e565b610db0610311565b918291826106f2565b0390f35b610317565b610dce60095f906107ca565b90565b34610e0157610de13660046103c3565b610dfd610dec610dc2565b610df4610311565b918291826107fe565b0390f35b610317565b5190565b60209181520190565b60200190565b610e229061032e565b9052565b90610e3381602093610e19565b0190565b60200190565b90610e5a610e54610e4d84610e06565b8093610e0a565b92610e13565b905f5b818110610e6a5750505090565b909192610e83610e7d6001928651610e26565b94610e37565b9101919091610e5d565b610ea29160208201915f818403910152610e3d565b90565b34610ed557610ed1610ec0610ebb36600461073c565b61328d565b610ec8610311565b91829182610e8d565b0390f35b610317565b90602082820312610f0a575f82013567ffffffffffffffff8111610f0557610f029201610ab9565b90565b61031f565b61031b565b90565b610f1b90610f0f565b9052565b9190610f32905f60208501940190610f12565b565b34610f6457610f60610f4f610f4a366004610eda565b61350f565b610f57610311565b91829182610f1f565b0390f35b610317565b90610f7390610785565b5f5260205260405f2090565b634e487b7160e01b5f525f60045260245ffd5b634e487b7160e01b5f52602260045260245ffd5b9060016002830492168015610fc6575b6020831014610fc157565b610f92565b91607f1691610fb6565b5f5260205f2090565b905f9291805490610ff3610fec83610fa6565b80946103e9565b916001811690815f1461104a575060011461100e575b505050565b61101b9192939450610fd0565b915f925b81841061103257505001905f8080611009565b6001816020929593955484860152019101929061101f565b92949550505060ff19168252151560200201905f8080611009565b9061106f91610fd9565b90565b9061109261108b92611082610311565b93848092611065565b038361051b565b565b905f106110a7576110a490611072565b90565b610f7f565b6110c2906110bd6010915f92610f69565b611094565b90565b60209181520190565b6110ed6110f66020936110fb936110e4816103e5565b938480936110c5565b958691016103f2565b6103fd565b0190565b6111149160208201915f8184039101526110ce565b90565b346111475761114361113261112d36600461073c565b6110ac565b61113a610311565b918291826110ff565b0390f35b610317565b5f80fd5b909182601f8301121561118a5781359167ffffffffffffffff831161118557602001926020830284011161118057565b610aea565b61114c565b6104ff565b909182601f830112156111c95781359167ffffffffffffffff83116111c45760200192600183028401116111bf57565b610aea565b61114c565b6104ff565b9091604082840312611228575f82013567ffffffffffffffff811161122357836111f9918401611150565b929093602082013567ffffffffffffffff811161121e5761121a920161118f565b9091565b61031f565b61031f565b61031b565b3461125f576112496112403660046111ce565b92919091613892565b611251610311565b8061125b8161038a565b0390f35b610317565b61127060115f906109f6565b90565b346112a3576112833660046103c3565b61129f61128e611264565b611296610311565b91829182610a37565b0390f35b610317565b91906040838203126112e8575f8301359067ffffffffffffffff82116112e3576112d7816112e09386016105bd565b9360200161034e565b90565b61031f565b61031b565b3461131c576113066113003660046112a8565b90613fcb565b61130e610311565b806113188161038a565b0390f35b610317565b61132a81610a25565b0361133157565b5f80fd5b9050359061134282611321565b565b9060208282031261135d5761135a915f01611335565b90565b61031b565b346113905761137a611375366004611344565b613ff7565b611382610311565b8061138c8161038a565b0390f35b610317565b60ff1690565b6113a481611395565b036113ab57565b5f80fd5b905035906113bc8261139b565b565b6113c781610643565b036113ce57565b5f80fd5b905035906113df826113be565b565b9091606082840312611416576114136113fc845f850161034e565b9361140a81602086016113af565b936040016113d2565b90565b61031b565b3461144a5761143461142e3660046113e1565b9161429a565b61143c610311565b806114468161038a565b0390f35b610317565b61145b600b5f906107ca565b90565b3461148e5761146e3660046103c3565b61148a61147961144f565b611481610311565b918291826107fe565b0390f35b610317565b61149f600c5f906107ca565b90565b346114d2576114b23660046103c3565b6114ce6114bd611493565b6114c5610311565b918291826107fe565b0390f35b610317565b34611505576114ef6114ea3660046105e0565b6142a7565b6114f7610311565b806115018161038a565b0390f35b610317565b346115395761152361151d36600461035d565b906145a6565b61152b610311565b806115358161038a565b0390f35b610317565b3461156c5761154e3660046103c3565b6115566145d7565b61155e610311565b806115688161038a565b0390f35b610317565b611587906115826004915f926109cc565b6109f6565b90565b346115ba576115b66115a56115a03660046105e0565b611571565b6115ad610311565b91829182610a37565b0390f35b610317565b346115ed576115d76115d23660046105e0565b6146c0565b6115df610311565b806115e98161038a565b0390f35b610317565b909182601f8301121561162c5781359167ffffffffffffffff831161162757602001926020830284011161162257565b610aea565b61114c565b6104ff565b90602082820312611662575f82013567ffffffffffffffff811161165d5761165992016115f2565b9091565b61031f565b61031b565b346116965761168061167a366004611631565b9061481c565b611688610311565b806116928161038a565b0390f35b610317565b346116c9576116ab3660046103c3565b6116b3614828565b6116bb610311565b806116c58161038a565b0390f35b610317565b346116fd576116e76116e1366004611631565b9061492e565b6116ef610311565b806116f98161038a565b0390f35b610317565b346117305761171a61171536600461073c565b6149a4565b611722610311565b8061172c8161038a565b0390f35b610317565b9060208282031261174e5761174b915f016113d2565b90565b61031b565b346117815761176b611766366004611735565b614a19565b611773610311565b8061177d8161038a565b0390f35b610317565b346117b6576117963660046103c3565b6117b26117a1614a24565b6117a9610311565b918291826107fe565b0390f35b610317565b346117eb576117e76117d66117d1366004610eda565b614a42565b6117de610311565b91829182610f1f565b0390f35b610317565b909160c08284031261184f57611808835f840161034e565b92611816816020850161034e565b92611824826040830161034e565b9261184c611835846060850161034e565b93611843816080860161034e565b9360a0016113d2565b90565b61031b565b34611889576118736118673660046117f0565b94939093929192614dff565b61187b610311565b806118858161038a565b0390f35b610317565b5f80fd5b5f80fd5b61189f81610f0f565b036118a657565b5f80fd5b905035906118b782611896565b565b91909160608184031261191e576118d06060610544565b925f8201359167ffffffffffffffff8311611919576118f482611912948301610bde565b5f86015261190582602083016118aa565b60208601526040016113d2565b6040830152565b611892565b61188e565b9190916040818403126119635761193c835f830161034e565b92602082013567ffffffffffffffff811161195e5761195b92016118b9565b90565b61031f565b61031b565b346119975761198161197b366004611923565b90615303565b611989610311565b806119938161038a565b0390f35b610317565b346119ca576119b46119af36600461073c565b615512565b6119bc610311565b806119c68161038a565b0390f35b610317565b9091606082840312611a35575f82013567ffffffffffffffff8111611a3057836119fa9184016105bd565b9260208301359067ffffffffffffffff8211611a2b57611a1f81611a289386016105bd565b936040016113d2565b90565b61031f565b61031f565b61031b565b34611a6957611a53611a4d3660046119cf565b916158e6565b611a5b610311565b80611a658161038a565b0390f35b610317565b634e487b7160e01b5f52603260045260245ffd5b5490565b5f5260205f2090565b611a9881611a82565b821015611ab257611aaa600191611a86565b910201905f90565b611a6e565b6008611ac281611a82565b821015611adf57611adc91611ad691611a8f565b906107ca565b90565b5f80fd5b34611b1357611b0f611afe611af9366004611735565b611ab7565b611b06610311565b918291826107fe565b0390f35b610317565b611b2191610679565b90565b611b3a90611b356003915f92611b18565b611094565b90565b34611b6d57611b69611b58611b533660046105e0565b611b24565b611b60610311565b918291826110ff565b0390f35b610317565b5490565b5f5260205f2090565b611b8881611b72565b821015611ba257611b9a600191611b76565b910201905f90565b611a6e565b6006611bb281611b72565b821015611bcf57611bcc91611bc691611b7f565b90611094565b90565b5f80fd5b34611c0357611bff611bee611be9366004611735565b611ba7565b611bf6610311565b918291826110ff565b0390f35b610317565b611c1e90611c196005915f92611b18565b611094565b90565b34611c5157611c4d611c3c611c373660046105e0565b611c08565b611c44610311565b918291826110ff565b0390f35b610317565b6007611c6181611b72565b821015611c7e57611c7b91611c7591611b7f565b90611094565b90565b5f80fd5b34611cb257611cae611c9d611c98366004611735565b611c56565b611ca5610311565b918291826110ff565b0390f35b610317565b34611ce757611cc73660046103c3565b611ce3611cd26158f3565b611cda610311565b918291826107fe565b0390f35b610317565b34611d1a57611d04611cff36600461073c565b61597b565b611d0c610311565b80611d168161038a565b0390f35b610317565b34611d4f57611d2f3660046103c3565b611d4b611d3a615a34565b611d42610311565b91829182610e8d565b0390f35b610317565b611d60600a5f906107ca565b90565b34611d9357611d733660046103c3565b611d8f611d7e611d54565b611d86610311565b918291826107fe565b0390f35b610317565b34611dc657611db0611dab36600461073c565b615ac5565b611db8610311565b80611dc28161038a565b0390f35b610317565b5f80fd5b60207f4b65793a206f70657261746f72206973206e6f74207265676973746572656400917f5072656469636174652e726f746174655072656469636174655369676e696e675f8201520152565b611e29603f6040926110c5565b611e3281611dcf565b0190565b611e4b9060208101905f818303910152611e1c565b90565b15611e5557565b611e5d610311565b62461bcd60e51b815280611e7360048201611e36565b0390fd5b611e83611e889161085e565b6107a7565b90565b611e959054611e77565b90565b60407f2773206f776e207369676e696e67206b65790000000000000000000000000000917f5072656469636174652e726f746174655072656469636174655369676e696e675f8201527f4b65793a206f70657261746f722063616e206f6e6c79206368616e676520697460208201520152565b611f1860526060926110c5565b611f2181611e98565b0190565b611f3a9060208101905f818303910152611f0b565b90565b15611f4457565b611f4c610311565b62461bcd60e51b815280611f6260048201611f25565b0390fd5b90565b611f7d611f78611f8292611f66565b61075a565b610323565b90565b611f8e90611f69565b90565b60407f6973746572656400000000000000000000000000000000000000000000000000917f5072656469636174652e726f746174655072656469636174655369676e696e675f8201527f4b65793a206e6577207369676e696e67206b657920616c72656164792072656760208201520152565b61201160476060926110c5565b61201a81611f91565b0190565b6120339060208101905f818303910152612004565b90565b1561203d57565b612045610311565b62461bcd60e51b81528061205b6004820161201e565b0390fd5b1b90565b9190600861208391029161207d60018060a01b038461205f565b9261205f565b9181191691161790565b90565b91906120a66120a16120ae93610785565b61208d565b908354612063565b9055565b5f90565b6120c8916120c26120b2565b91612090565b565b5f1b90565b906120e060018060a01b03916120ca565b9181191691161790565b906120ff6120fa61210692610785565b61208d565b82546120cf565b9055565b9061213d612124600161211e5f3390610848565b0161089e565b61213761213160016108f3565b916108f3565b14611e4e565b61216c3361216661216061215b61215660018890610791565b611e8b565b61032e565b9161032e565b14611f3d565b6121a361218361217e60018490610791565b611e8b565b61219d6121976121925f611f85565b61032e565b9161032e565b14612036565b6121b85f6121b360018590610791565b6120b6565b6121cd336121c860018490610791565b6120ea565b339161220b6122056121ff7f6c01cc00138da448a5e602c2eea1e9551a5bf0d2ae983466089ec720d7c0b74895610785565b92610785565b92610785565b92612214610311565b8061221e8161038a565b0390a4565b606090565b67ffffffffffffffff81116122405760208091020190565b610507565b9061225761225283612228565b610544565b918252565b61226590611072565b90565b9061227282611b72565b61227b81612245565b926122896020850191611b76565b5f915b8383106122995750505050565b6001602081926122a88561225c565b81520192019201919061228c565b6122bf90612268565b90565b6122ca612223565b506122d560066122b6565b90565b6122e9906122e4615ad0565b6123e9565b565b6122ff6122fa61230492611f66565b61075a565b610643565b90565b90565b61231e61231961232392612307565b61075a565b610643565b90565b634e487b7160e01b5f52601160045260245ffd5b61234961234f91939293610643565b92610643565b820391821161235a57565b612326565b90565b634e487b7160e01b5f52603160045260245ffd5b5490565b5f5260205f2090565b61238c81612376565b8210156123a65761239e60019161237a565b910201905f90565b611a6e565b6123b481612376565b80156123d55760019003906123d26123cc8383612383565b906120b6565b55565b612362565b60016123e69101610643565b90565b6123f25f6122eb565b5b8061240f6124096124046008611a82565b610643565b91610643565b146124e95761242961242360088390611a8f565b906107ca565b61243b6124358461032e565b9161032e565b1461244e57612449906123da565b6123f3565b6124949061248e612486612480600861247a61246a6008611a82565b612474600161230a565b9061233a565b90611a8f565b906107ca565b916008611a8f565b90612090565b6124a66124a1600861235f565b6123ab565b6124d07f09a1db4b80c32706328728508c941a6b954f31eb5affd32f236c1fd405f8fea491610785565b906124d9610311565b806124e38161038a565b0390a25b565b50506124e7565b6124f9906122d8565b565b5f90565b63ffffffff1690565b612511816124ff565b0361251857565b5f80fd5b3561252681612508565b90565b61253d61253861254292611f66565b61075a565b6124ff565b90565b60407f65726f0000000000000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2071755f8201527f6f72756d207468726573686f6c6420636f756e742063616e6e6f74206265207a60208201520152565b6125c560436060926110c5565b6125ce81612545565b0190565b6125e79060208101905f8183039101526125b8565b90565b156125f157565b6125f9610311565b62461bcd60e51b81528061260f600482016125d2565b0390fd5b5190565b60407f7475726573000000000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a204d695f8201527f736d61746368206265747765656e207369676e65727320616e64207369676e6160208201520152565b61269760456060926110c5565b6126a081612617565b0190565b6126b99060208101905f81830391015261268a565b90565b156126c357565b6126cb610311565b62461bcd60e51b8152806126e1600482016126a4565b0390fd5b356126ef816113be565b90565b60207f616e73616374696f6e2065787069726564000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2074725f8201520152565b61274c60316040926110c5565b612755816126f2565b0190565b61276e9060208101905f81830391015261273f565b90565b1561277857565b612780610311565b62461bcd60e51b81528061279660048201612759565b0390fd5b5f80fd5b5f80fd5b5f80fd5b9035906001602003813603038212156127e8570180359067ffffffffffffffff82116127e3576020019160018202360383136127de57565b6127a2565b61279e565b61279a565b9091826127fd8161280493610615565b809361057c565b0190565b6128199060209493612820936127ed565b8092610649565b0190565b909161283b90612832610311565b93849384612808565b03902090565b909161284c92612824565b90565b61285b6128609161085e565b6109d8565b90565b61286d905461284f565b90565b60207f736b20494420616c7265616479207370656e7400000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2074615f8201520152565b6128ca60336040926110c5565b6128d381612870565b0190565b6128ec9060208101905f8183039101526128bd565b90565b156128f657565b6128fe610311565b62461bcd60e51b815280612914600482016128d7565b0390fd5b909161292392612824565b90565b61293a61293561293f926124ff565b61075a565b610643565b90565b60407f66666572732066726f6d207461736b2071756f72756d207468726573686f6c64917f5072656469636174652e50726564696361746556657269666965643a206465705f8201527f6c6f79656420706f6c6963792071756f72756d207468726573686f6c6420646960208201520152565b6129c1606080926110c5565b6129ca81612942565b0190565b6129e39060208101905f8183039101526129b5565b90565b156129ed57565b6129f5610311565b62461bcd60e51b815280612a0b600482016129ce565b0390fd5b90612a1982610e06565b811015612a2a576020809102010190565b611a6e565b612a39905161032e565b90565b612a459061075d565b90565b60407f6420736f72746564000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2053695f8201527f676e657220616464726573736573206d75737420626520756e6971756520616e60208201520152565b612ac860486060926110c5565b612ad181612a48565b0190565b612aea9060208101905f818303910152612abb565b90565b90612af782612613565b811015612b08576020809102010190565b611a6e565b60207f76616c6964207369676e61747572650000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a20496e5f8201520152565b612b67602f6040926110c5565b612b7081612b0d565b0190565b612b899060208101905f818303910152612b5a565b90565b15612b9357565b612b9b610311565b62461bcd60e51b815280612bb160048201612b74565b0390fd5b60407f7200000000000000000000000000000000000000000000000000000000000000917f5072656469636174652e76616c69646174655369676e6174757265733a2053695f8201527f676e6572206973206e6f7420612072656769737465726564206f70657261746f60208201520152565b612c3560416060926110c5565b612c3e81612bb5565b0190565b612c579060208101905f818303910152612c28565b90565b15612c6157565b612c69610311565b62461bcd60e51b815280612c7f60048201612c42565b0390fd5b35612c8d8161033a565b90565b612ca4612c9f612ca992610643565b61075a565b610643565b90565b9190612cc681612cbf81612ccb956110c5565b809561057c565b6103fd565b0190565b612cd890612926565b9052565b93612d2793612d1291612d04612d1d94612d349b999a9660a08a01918a83035f8c0152612cac565b918783036020890152612cac565b956040850190612ccf565b60608301906106e5565b6080818403910152610e3d565b90565b90612d4360ff916120ca565b9181191691161790565b612d5690610a25565b90565b90565b90612d71612d6c612d7892612d4d565b612d59565b8254612d37565b9055565b9291612d866124fb565b50612daf612d9660c0860161251c565b612da8612da25f612529565b916124ff565b14156125ea565b612ddb612dbb83610e06565b612dd5612dcf612dca85612613565b610643565b91610643565b146126bc565b612e0342612dfc612df6612df160e089016126e5565b610643565b91610643565b1115612771565b612e34612e2f612e29612e246004612e1e895f8101906127a6565b91612841565b612863565b15610a25565b6128ef565b612e55612e50600e612e4a8760a08101906127a6565b91612918565b610877565b9283612e69612e635f6122eb565b91610643565b1415806130bd575b612e7a906129e6565b612e838561350f565b94612e8d5f6122eb565b5b80612ea1612e9b88610643565b91610643565b1015612fdc5780612eba612eb45f6122eb565b91610643565b1180612f7a575b612f5857612f5390612f4e612f356001612f2f612f29612f24612ef08e612ee98d8a90612aed565b5190615b1e565b612f1e8d612f18612f12612f0d612f088d8795612a0f565b612a2f565b61032e565b9161032e565b14612b8c565b84610791565b611e8b565b5f610848565b0161089e565b612f48612f4260016108f3565b916108f3565b14612c5a565b6123da565b612e8e565b612f60610311565b62461bcd60e51b815280612f7660048201612ad5565b0390fd5b50612f96612f91612f8c878490612a0f565b612a2f565b612a3c565b612fd5612fcf612fca612fc5612fc08a612fba88612fb4600161230a565b9061233a565b90612a0f565b612a2f565b612a3c565b610323565b91610323565b1115612ec1565b5092915093506130b89250612ff360208301612c83565b612fff60408401612c83565b61300b606085016126e5565b9161301a8560a08101906127a6565b9061309561302b885f8101906127a6565b909761303960c08b0161251c565b61304560e08c016126e5565b919261308361307d6130777fb4c99a6d727df238766e771095ad097c0bf9c2aef920b56b65c0ac529f36f2db9a610785565b9a610785565b9a612c90565b9a61308c610311565b97889788612cdc565b0390a46130b36001916130ad6004915f8101906127a6565b91612841565b612d5c565b600190565b50612e7a6130cd60c0870161251c565b6130df6130d987610643565b91612926565b149050612e71565b6130f8906130f3615ad0565b6130fa565b565b61310590600a6120ea565b61310f600a611e8b565b6131397fa1b8fcd417a2bb56a91d1fc6708faf8283b5730e55821393e70303e544aeec9291610785565b90613142610311565b8061314c8161038a565b0390a2565b61315a906130e7565b565b606090565b9061317361316e83610acd565b610544565b918252565b369037565b906131a261318a83613161565b926020806131988693610acd565b9201910390613178565b565b6131ad9061075d565b90565b6131b9906131a4565b90565b6131c590610779565b90565b6131d19061075d565b90565b6131dd906131c8565b90565b5f80fd5b60e01b90565b905051906131f7826113be565b565b906020828203126132125761320f915f016131ea565b90565b61031b565b61322090610779565b90565b61322c90613217565b9052565b91602061325192949361324a60408201965f8301906107f1565b0190613223565b565b61325b610311565b3d5f823e3d90fd5b9061326d9061032e565b9052565b61327a90610643565b5f1981146132885760010190565b612326565b9061329661315c565b506132a96132a46008611a82565b61317d565b6132b25f6122eb565b6132bb5f6122eb565b5b806132d86132d26132cd6008611a82565b610643565b91610643565b10156133f0576132f86132f36132ee600a611e8b565b6131b0565b6131bc565b602063778e55f391879061333a61332261331d61331760088990611a8f565b906107ca565b6131d4565b9461334561332e610311565b968795869485946131e4565b845260048401613230565b03915afa9081156133eb575f916133bd575b5061336a6133645f6122eb565b91610643565b1161337e575b613379906123da565b6132bc565b906133b5613379916133b061339e61339860088790611a8f565b906107ca565b6133ab8791849092612a0f565b613263565b613271565b919050613370565b6133de915060203d81116133e4575b6133d6818361051b565b8101906131f9565b5f613357565b503d6133cc565b613253565b505090915090565b5f90565b90359060016020038136030382121561343e570180359067ffffffffffffffff82116134395760200191600182023603831361343457565b6127a2565b61279e565b61279a565b60209181520190565b91906134668161345f8161346b95613443565b809561057c565b6103fd565b0190565b613478906124ff565b9052565b966134fc966134d66134f1976135039d9f9e9c9660e09c986134c26134cc9260208f996134ba906134e39c6101008d01918d5f818503910152612cac565b9901906107f1565b60408d01906107f1565b60608b01906106e5565b88830360808a015261344c565b9185830360a0870152612cac565b9660c083019061346f565b01906106e5565b565b60200190565b5190565b6135a66135979161351e6133f8565b5061352c815f8101906127a6565b93909161353b60208201612c83565b9033613549606083016126e5565b906135588360808101906133fc565b906135678560a08101906127a6565b94909361358260e061357b60c08a0161251c565b98016126e5565b9761358b610311565b9c8d9b60208d0161347c565b6020820181038252038261051b565b6135b86135b28261350b565b91613505565b2090565b5090565b5090565b5f90565b90359060016020038136030382121561360a570180359067ffffffffffffffff82116136055760200191602082023603831361360057565b6127a2565b61279e565b61279a565b9082101561362a57602061362692028101906135c8565b9091565b611a6e565b61363a913691610aee565b90565b90565b61364f61365591939293610643565b92610643565b820180921161366057565b612326565b906136715f19916120ca565b9181191691161790565b90565b9061369361368e61369a92612c90565b61367b565b8254613665565b9055565b6136a7906108f3565b90565b90565b906136c26136bd6136c99261369e565b6136aa565b8254612d37565b9055565b905090565b90565b905090565b90565b6136e69061032e565b9052565b906136f7816020936136dd565b0190565b5061370a90602081019061034e565b90565b60200190565b9161372182613727926136d5565b926136da565b90815f905b82821061373a575050505090565b9091929361375c61375660019261375188866136fb565b6136ea565b9561370d565b92019092919261372c565b906137729291613713565b90565b5f80fd5b5f80fd5b5f80fd5b90356001602003823603038112156137c257016020813591019167ffffffffffffffff82116137bd5760208202360383136137b857565b613779565b613775565b61377d565b60200190565b916137db826137e1926136cd565b926136d2565b90815f905b8282106137f4575050505090565b9091929361381761381160019261380b8886613781565b90613767565b956137c7565b9201909291926137e6565b909161382d926137cd565b90565b61384461383b610311565b92839283613822565b03902090565b905090565b90918261385f816138669361384a565b809361057c565b0190565b90916138759261384f565b90565b61388c613883610311565b9283928361386a565b03902090565b919390926138a18385906135bc565b6138bd6138b76138b28885906135c0565b610643565b91610643565b03613b875791925f949194506138d161315c565b506138da6120b2565b506138e36135c4565b935b846139026138fc6138f78688906135c0565b610643565b91610643565b14613b335794909361392261391c8795949587849161360f565b9061362f565b9561392b6135c4565b935b8461394861394261393d8b610e06565b610643565b91610643565b1015613b195761396161395c898790612a0f565b612a2f565b956139756139705f8990610848565b61363d565b9761398260018a0161089e565b61399461398e5f6108f3565b916108f3565b14613afd576139a16135c4565b936139aa6135c4565b945b856139c86139c26139bd6008611a82565b610643565b91610643565b14613a93576139e76139e26139dd600a611e8b565b6131b0565b6131bc565b602063778e55f3918c90613a28613a10613a0b613a058d6008611a8f565b906107ca565b6131d4565b94613a33613a1c610311565b968795869485946131e4565b845260048401613230565b03915afa8015613a8e57613a5892613a52925f92613a5e575b50613640565b956123da565b946139ac565b613a8091925060203d8111613a87575b613a78818361051b565b8101906131f9565b905f613a4c565b503d613a6e565b613253565b613ae6949950613ae19297955099909599979297613ab3815f840161367e565b613ace613ac8613ac3600d610877565b610643565b91610643565b105f14613af45760016002915b016136ad565b6123da565b94959190929594939461392d565b60018091613adb565b5f63ba6435f160e01b815280613b156004820161038a565b0390fd5b929350939550613b28906123da565b9491949390926138e5565b613b6f92919450613b6990949193917ff3fcd2dadc5f6ebf9c1e9310a9e986a14079afc7ecf26784853ea9a3ac90721b95613830565b92613878565b91613b78610311565b80613b828161038a565b0390a3565b5f63371821d760e21b815280613b9f6004820161038a565b0390fd5b90613bb591613bb0615ad0565b613f0e565b565b90565b60207f616e6e6f7420626520656d707479000000000000000000000000000000000000917f5072656469636174652e736574506f6c6963793a20706f6c69637920494420635f8201520152565b613c14602e6040926110c5565b613c1d81613bba565b0190565b613c369060208101905f818303910152613c07565b90565b15613c4057565b613c48610311565b62461bcd60e51b815280613c5e60048201613c21565b0390fd5b60207f6f74207265676973746572656400000000000000000000000000000000000000917f5072656469636174652e736574506f6c6963793a20706f6c696379204944206e5f8201520152565b613cbc602d6040926110c5565b613cc581613c62565b0190565b613cde9060208101905f818303910152613caf565b90565b15613ce857565b613cf0610311565b62461bcd60e51b815280613d0660048201613cc9565b0390fd5b601f602091010490565b91906008613d2f910291613d295f198461205f565b9261205f565b9181191691161790565b9190613d4f613d4a613d5793612c90565b61367b565b908354613d14565b9055565b613d6d91613d676135c4565b91613d39565b565b5b818110613d7b575050565b80613d885f600193613d5b565b01613d70565b9190601f8111613d9e575b505050565b613daa613dcf93610fd0565b906020613db684613d0a565b83019310613dd7575b613dc890613d0a565b0190613d6f565b5f8080613d99565b9150613dc881929050613dbf565b90613df5905f199060080261069f565b191690565b81613e0491613de5565b906002021790565b90613e16816103e5565b9067ffffffffffffffff8211613ed657613e3a82613e348554610fa6565b85613d8e565b602090601f8311600114613e6e57918091613e5d935f92613e62575b5050613dfa565b90555b565b90915001515f80613e56565b601f19831691613e7d85610fd0565b925f5b818110613ebe57509160029391856001969410613ea4575b50505002019055613e60565b613eb4910151601f841690613de5565b90555f8080613e98565b91936020600181928787015181550195019201613e80565b610507565b90613ee591613e0c565b565b613ef09161061a565b90565b613f0890613eff610311565b91829182613ee7565b03902090565b90613f3b613f23613f1e84613bb7565b61350b565b613f35613f2f5f6122eb565b91610643565b11613c39565b613f6a613f52613f4d600e8590610693565b610877565b613f64613f5e5f6122eb565b91610643565b11613ce1565b613f7f82613f7a60108490610f69565b613edb565b90613fb3613fad7ffbc30d1514eac402cb2045f1dd80ec75fbc997db6f719421b6d7490f4bfb779d93610785565b91613ef3565b91613fbc610311565b80613fc68161038a565b0390a3565b90613fd591613ba3565b565b613fe890613fe3615ad0565b613fea565b565b613ff5906011612d5c565b565b61400090613fd7565b565b906140159291614010615ad0565b61416d565b565b6140209061075d565b90565b61402c90614017565b90565b61403890610779565b90565b6140449061032e565b90565b6140508161403b565b0361405757565b5f80fd5b9050519061406882614047565b565b6bffffffffffffffffffffffff1690565b6140848161406a565b0361408b57565b5f80fd5b9050519061409c8261407b565b565b91906040838203126140d8576140d1906140b86040610544565b936140c5825f830161405b565b5f86015260200161408f565b6020830152565b61188e565b906040828203126140f6576140f3915f0161409e565b90565b61031b565b61410490611395565b9052565b91602061412992949361412260408201965f8301906140fb565b01906106e5565b565b614135905161403b565b90565b9081549168010000000000000000831015614168578261416091600161416695018155612383565b90612090565b565b610507565b9160409061418b614186614181600b611e8b565b614023565b61402f565b6141ad63adc804da9492946141b86141a1610311565b968795869485946131e4565b845260048401614108565b03915afa8015614295575f6141d9916141de938291614267575b500161412b565b613217565b6141f06141ea8361032e565b9161032e565b0361424b57614209614202600861235f565b8290614138565b6142337f3f008fd510eae7a9e7bee13513d7b83bef8003d488b5a3d0b0da4de71d6846f191610785565b9061423c610311565b806142468161038a565b0390a2565b5f63a4e34a6960e01b8152806142636004820161038a565b0390fd5b614288915060403d811161428e575b614280818361051b565b8101906140dd565b5f6141d2565b503d614276565b613253565b906142a59291614002565b565b6142d36142bb6142b683613bb7565b61350b565b6142cd6142c75f6122eb565b91610643565b11613c39565b6143026142ea6142e5600e8490610693565b610877565b6142fc6142f65f6122eb565b91610643565b11613ce1565b6143178161431260103390610f69565b613edb565b339061434c6143467ffbc30d1514eac402cb2045f1dd80ec75fbc997db6f719421b6d7490f4bfb779d93610785565b91613ef3565b91614355610311565b8061435f8161038a565b0390a3565b9061437691614371615ad0565b6144a2565b565b60207f206f70657261746f7220616c7265616479207265676973746572656400000000917f5072656469636174652e72656769737465724f70657261746f72546f4156533a5f8201520152565b6143d2603c6040926110c5565b6143db81614378565b0190565b6143f49060208101905f8183039101526143c5565b90565b156143fe57565b614406610311565b62461bcd60e51b81528061441c600482016143df565b0390fd5b61442a6040610544565b90565b9061443790610643565b9052565b90614445906108f3565b9052565b6144539051610643565b90565b61446090516108f3565b90565b9061448e60206001614494946144865f82016144805f8801614449565b9061367e565b019201614456565b906136ad565b565b906144a091614463565b565b90614564906144de6144be6144b960018490610791565b611e8b565b6144d86144d26144cd5f611f85565b61032e565b9161032e565b146143f7565b6145156144f56144f060018490610791565b611e8b565b61450f6145096145045f611f85565b61032e565b9161032e565b14612036565b6145535f614543600161453a61453261452c614420565b946122eb565b5f850161442d565b6020830161443b565b61454e5f8690610848565b614496565b61455f83916001610791565b6120ea565b61458e7f4d0eb1f4bac8744fd2be119845e23b3befc88094b42bcda1204c65694a00f9e591610785565b90614597610311565b806145a18161038a565b0390a2565b906145b091614364565b565b6145ba615ad0565b6145c26145c4565b565b6145d56145d05f611f85565b615b40565b565b6145df6145b2565b565b6145f2906145ed615ad0565b614627565b565b6145fd9061075d565b90565b614609906145f4565b90565b61461590610779565b90565b5f91031261462257565b61031b565b61464161463c614637600c611e8b565b614600565b61460c565b9063a98fb35590823b156146bb576146789261466d5f8094614661610311565b968795869485936131e4565b8352600483016110ff565b03925af180156146b65761468a575b50565b6146a9905f3d81116146af575b6146a1818361051b565b810190614618565b5f614687565b503d614697565b613253565b6131e0565b6146c9906145e1565b565b906146dd916146d8615ad0565b61477c565b565b5090565b91908110156146f3576020020190565b611a6e565b9061470290610785565b5f5260205260405f2090565b9161471c8261472292610e0a565b926136da565b90815f905b828210614735575050505090565b9091929361475761475160019261474c88866136fb565b610e26565b9561370d565b920190929192614727565b90916147799260208301925f81850391015261470e565b90565b6147855f6122eb565b5b806147a361479d6147988587906146df565b610643565b91610643565b10156147df576147da906147d55f6147d0600f6147ca6147c5888a88916146e3565b612c83565b906146f8565b612d5c565b6123da565b614786565b50907f05440a044a44c57aadc302acd45d200977ad0c2da22575dc91c0e07a760ce78c9161481761480e610311565b92839283614762565b0390a1565b90614826916146cb565b565b614830615b5e565b6148386158f3565b61484a6148448361032e565b9161032e565b0361485a5761485890615b40565b565b614875905f91829163118cdaa760e01b8352600483016107fe565b0390fd5b9061488b91614886615ad0565b61488d565b565b6148965f6122eb565b5b806148b46148ae6148a98587906146df565b610643565b91610643565b10156148f1576148ec906148e760016148e2600f6148dc6148d7888a88916146e3565b612c83565b906146f8565b612d5c565b6123da565b614897565b50907f33a805a6149751d70281b1283390e2798db286558e2b64bbe594c79141f736cc91614929614920610311565b92839283614762565b0390a1565b9061493891614879565b565b61494b90614946615ad0565b61494d565b565b61495890600c6120ea565b614962600c611e8b565b61498c7f2a623245c0f1c5741f2a4c247a58842872f1fdf8a31e66d031dd1cd1532e89a791610785565b90614995610311565b8061499f8161038a565b0390a2565b6149ad9061493a565b565b6149c0906149bb615ad0565b6149c2565b565b6149cd90600d61367e565b6149d7600d610877565b614a017f76f50103a7b1e136f23c29045235143c62e36705e8253019cb1253d9062bc6d491612c90565b90614a0a610311565b80614a148161038a565b0390a2565b614a22906149af565b565b614a2c6120b2565b50614a3f5f614a39615b6b565b01611e8b565b90565b614ae4614ad591614a516133f8565b50614a5f815f8101906127a6565b939091614a6e60208201612c83565b90614a7b60408201612c83565b614a87606083016126e5565b90614a968360808101906133fc565b90614aa58560a08101906127a6565b949093614ac060e0614ab960c08a0161251c565b98016126e5565b97614ac9610311565b9c8d9b60208d0161347c565b6020820181038252038261051b565b614af6614af08261350b565b91613505565b2090565b60401c90565b614b0c614b1191614afa565b6109d8565b90565b614b1e9054614b00565b90565b67ffffffffffffffff1690565b614b3a614b3f9161085e565b614b21565b90565b614b4c9054614b2e565b90565b67ffffffffffffffff1690565b614b70614b6b614b7592611f66565b61075a565b614b4f565b90565b614b8c614b87614b9192612307565b61075a565b614b4f565b90565b614b9d90610779565b90565b90614bb367ffffffffffffffff916120ca565b9181191691161790565b614bd1614bcc614bd692614b4f565b61075a565b614b4f565b90565b90565b90614bf1614bec614bf892614bbd565b614bd9565b8254614ba0565b9055565b60401b90565b90614c1668ff000000000000000091614bfc565b9181191691161790565b90614c35614c30614c3c92612d4d565b612d59565b8254614c02565b9055565b614c4990614b78565b9052565b9190614c60905f60208501940190614c40565b565b92949194939093614c71615b8f565b95614c86614c805f8901614b14565b15610a25565b95614c925f8901614b42565b80614ca5614c9f5f614b5c565b91614b4f565b1480614dbf575b90614cc0614cba6001614b78565b91614b4f565b1480614d97575b614cd2909115610a25565b9081614d86575b50614d6a57614d0295614cf7614cef6001614b78565b5f8b01614bdc565b87614d58575b614dc6565b614d0a575b50565b614d17905f809101614c20565b6001614d4f7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291614d46610311565b91829182614c4d565b0390a15f614d07565b614d6560015f8b01614c20565b614cfd565b5f63f92ee8a960e01b815280614d826004820161038a565b0390fd5b614d91915015610a25565b5f614cd9565b50614cd2614da430614b94565b3b614db7614db15f6122eb565b91610643565b149050614cc7565b5087614cac565b614def915092614de8614dfd969593614de1614df696615b40565b600a6120ea565b600b6120ea565b600c6120ea565b600d61367e565b565b90614e0d9594939291614c62565b565b90614e2d614e27614e22600f33906146f8565b612863565b15610a25565b614e3c57614e3a91614fe4565b565b5f6332c5b57b60e11b815280614e546004820161038a565b0390fd5b60207f20726567697374726174696f6e7320617265206e6f7420616c6c6f7765640000917f5072656469636174652e72656769737465724f70657261746f72546f4156533a5f8201520152565b614eb2603e6040926110c5565b614ebb81614e58565b0190565b614ed49060208101905f818303910152614ea5565b90565b15614ede57565b614ee6610311565b62461bcd60e51b815280614efc60048201614ebf565b0390fd5b614f0a9051610f0f565b90565b614f176060610544565b90565b52565b90614f2790610f0f565b9052565b60209181520190565b614f53614f5c602093614f6193614f4a8161350b565b93848093614f2b565b958691016103f2565b6103fd565b0190565b614f6e90610f0f565b9052565b614f7b90610643565b9052565b90614fbb90604080614f9e606084015f8701518582035f870152614f34565b94614fb160208201516020860190614f65565b0151910190614f72565b90565b91614fe192614fd460408201935f8301906107f1565b6020818403910152614f7f565b90565b9091614ff8614ff36011612863565b614ed7565b61502f61500f61500a60018590610791565b611e8b565b61502961502361501e5f611f85565b61032e565b9161032e565b146143f7565b61506661504661504160018590610791565b611e8b565b61506061505a6150555f611f85565b61032e565b9161032e565b14612036565b61506e6135c4565b926150776135c4565b935b8461509561508f61508a6008611a82565b610643565b91610643565b14615161576150b46150af6150aa600a611e8b565b6131b0565b6131bc565b602063778e55f39133906150f66150de6150d96150d360088d90611a8f565b906107ca565b6131d4565b946151016150ea610311565b968795869485946131e4565b845260048401613230565b03915afa801561515c5761512692615120925f9261512c575b50613640565b946123da565b93615079565b61514e91925060203d8111615155575b615146818361051b565b8101906131f9565b905f61511a565b503d61513c565b613253565b919350918161518161517b615176600d610877565b610643565b91610643565b101561518d575b505050565b6151c66151d7926151b660016151ad6151a4614420565b935f850161442d565b6020830161443b565b6151c15f3390610848565b614496565b6151d233916001610791565b6120ea565b6152215f820151916152186151fa60406151f360208501614f00565b9301614449565b9161520f615206614f0d565b955f8701614f1a565b60208501614f1d565b6040830161442d565b61523b615236615231600c611e8b565b614600565b61460c565b90639926ee7d90339092803b156152fe576152695f809461527461525d610311565b978896879586946131e4565b845260048401614fbe565b03925af180156152f9576152cd575b50336152af7f4d0eb1f4bac8744fd2be119845e23b3befc88094b42bcda1204c65694a00f9e591610785565b906152b8610311565b806152c28161038a565b0390a25f8080615188565b6152ec905f3d81116152f2575b6152e4818361051b565b810190614618565b5f615283565b503d6152da565b613253565b6131e0565b9061530d91614e0f565b565b6153209061531b615ad0565b6153ca565b565b60207f4156533a206f70657261746f72206973206e6f74207265676973746572656400917f5072656469636174652e646572656769737465724f70657261746f7246726f6d5f8201520152565b61537c603f6040926110c5565b61538581615322565b0190565b61539e9060208101905f81830391015261536f565b90565b156153a857565b6153b0610311565b62461bcd60e51b8152806153c660048201615389565b0390fd5b6153fc6153e360016153dd5f8590610848565b0161089e565b6153f56153ef5f6108f3565b916108f3565b14156153a1565b61543a5f61542a6002615421615419615413614420565b946122eb565b5f850161442d565b6020830161443b565b6154355f8490610848565b614496565b61545461544f61544a600c611e8b565b614600565b61460c565b63a364f4da82823b1561550d5761548a9261547f5f8094615473610311565b968795869485936131e4565b8352600483016107fe565b03925af18015615508576154dc575b506154c47f80c0b871b97b595b16a7741c1b06fed0c6f6f558639f18ccbce50724325dc40d91610785565b906154cd610311565b806154d78161038a565b0390a2565b6154fb905f3d8111615501575b6154f3818361051b565b810190614618565b5f615499565b503d6154e9565b613253565b6131e0565b61551b9061530f565b565b90615530929161552b615ad0565b6157e3565b565b90565b61553f9054610fa6565b90565b60207f7869737473000000000000000000000000000000000000000000000000000000917f5072656469636174652e6465706c6f79506f6c6963793a20706f6c69637920655f8201520152565b61559c60256040926110c5565b6155a581615542565b0190565b6155be9060208101905f81830391015261558f565b90565b156155c857565b6155d0610311565b62461bcd60e51b8152806155e6600482016155a9565b0390fd5b60407f726f000000000000000000000000000000000000000000000000000000000000917f5072656469636174652e6465706c6f79506f6c6963793a2071756f72756d20745f8201527f68726573686f6c64206d7573742062652067726561746572207468616e207a6560208201520152565b61566a60426060926110c5565b615673816155ea565b0190565b61568c9060208101905f81830391015261565d565b90565b1561569657565b61569e610311565b62461bcd60e51b8152806156b460048201615677565b0390fd5b60207f7472696e672063616e6e6f7420626520656d7074790000000000000000000000917f5072656469636174652e6465706c6f79506f6c6963793a20706f6c69637920735f8201520152565b61571260356040926110c5565b61571b816156b8565b0190565b6157349060208101905f818303910152615705565b90565b1561573e57565b615746610311565b62461bcd60e51b81528061575c6004820161571f565b0390fd5b90565b5f5260205f2090565b5490565b6157798161576c565b8210156157935761578b600191615763565b910201905f90565b611a6e565b91906157a9576157a791613e0c565b565b610f7f565b90815491680100000000000000008310156157de57826157d69160016157dc95018155615770565b90615798565b565b610507565b909161588e906158206158086158036157fe60038790611b18565b615532565b615535565b61581a6158145f6122eb565b91610643565b146155c1565b61583c816158366158305f6122eb565b91610643565b1161568f565b61586861585061584b86613bb7565b61350b565b61586261585c5f6122eb565b91610643565b11615737565b61587d8461587860038690611b18565b613edb565b615889600e8490610693565b61367e565b6158a261589b6006615760565b82906157ae565b6158e16158cf7fb6487025b06543ad686bdaa829e2b07863fd163cacd2e0f031340308ec09584e92613ef3565b926158d8610311565b918291826110ff565b0390a2565b906158f1929161551d565b565b6158fb6120b2565b5061590e5f615908615bb3565b01611e8b565b90565b6159229061591d615ad0565b615924565b565b61592f90600b6120ea565b615939600b611e8b565b6159637fcb08c26360210256eb3fd98640509ba95a8a716bc3aa4aadc738e333102de73791610785565b9061596c610311565b806159768161038a565b0390a2565b61598490615911565b565b60209181520190565b6159999054611e77565b90565b60010190565b906159bf6159b96159b284611a82565b8093615986565b92611a86565b905f5b8181106159cf5750505090565b9091926159ef6159e96001926159e48761598f565b610e26565b9461599c565b91019190916159c2565b90615a03916159a2565b90565b90615a26615a1f92615a16610311565b938480926159f9565b038361051b565b565b615a3190615a06565b90565b615a3c61315c565b50615a476008615a28565b90565b615a5b90615a56615ad0565b615a5d565b565b615a71615a68615bb3565b5f8391016120ea565b615a79614a24565b90615aad615aa77f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270093610785565b91610785565b91615ab6610311565b80615ac08161038a565b0390a3565b615ace90615a4a565b565b615ad8614a24565b615af1615aeb615ae6615b5e565b61032e565b9161032e565b03615af857565b615b1a615b03615b5e565b5f91829163118cdaa760e01b8352600483016107fe565b0390fd5b615b3d91615b3491615b2e6120b2565b50615c1a565b90929192615cee565b90565b615b5c90615b575f80615b51615bb3565b016120b6565b615dbf565b565b615b666120b2565b503390565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b5f90565b90565b615bf2615bed615bf792615bdb565b61075a565b610643565b90565b615c0e615c09615c1392610643565b6120ca565b610f0f565b90565b5f90565b919091615c256120b2565b50615c2e615bd7565b50615c376133f8565b50615c418361350b565b615c54615c4e6041615bde565b91610643565b145f14615c9b57615c949192615c686133f8565b50615c716133f8565b50615c7a615c16565b506020810151606060408301519201515f1a909192615ea4565b9192909190565b50615ca55f611f85565b90615cb9615cb460029461350b565b615bfa565b91929190565b60041115615cc957565b6108d0565b90615cd882615cbf565b565b615ce6615ceb9161085e565b612c90565b90565b80615d01615cfb5f615cce565b91615cce565b145f14615d0c575050565b80615d20615d1a6001615cce565b91615cce565b145f14615d43575f63f645eedf60e01b815280615d3f6004820161038a565b0390fd5b80615d57615d516002615cce565b91615cce565b145f14615d8557615d81615d6a83615cda565b5f91829163fce698f760e01b8352600483016106f2565b0390fd5b615d98615d926003615cce565b91615cce565b14615da05750565b615dbb905f9182916335e2f38360e21b835260048301610f1f565b0390fd5b615dc7615b6b565b615ddf615dd55f8301611e8b565b915f8491016120ea565b90615e13615e0d7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e093610785565b91610785565b91615e1c610311565b80615e268161038a565b0390a3565b90565b615e42615e3d615e4792615e2b565b61075a565b610643565b90565b615e7f615e8694615e75606094989795615e6b608086019a5f870190610f12565b60208501906140fb565b6040830190610f12565b0190610f12565b565b615e9c615e97615ea192611f66565b6120ca565b610f0f565b90565b939293615eaf6120b2565b50615eb8615bd7565b50615ec16133f8565b50615ecb85615cda565b615efd615ef77f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0615e2e565b91610643565b11615f8a5790615f20602094955f94939293615f17610311565b94859485615e4a565b838052039060015afa15615f8557615f385f516120ca565b80615f53615f4d615f485f611f85565b61032e565b9161032e565b14615f69575f91615f635f615e88565b91929190565b50615f735f611f85565b600191615f7f5f615e88565b91929190565b613253565b505050615f965f611f85565b906003929192919056fea26469706673582212209cf7804c8fa6e0185c0cf437200aeb210de65b262e033f9a8c2053bc5d498fd864736f6c634300081c0033
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.