Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
StakingNode
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/// SPDX-License-Identifier: BSD 3-Clause License pragma solidity ^0.8.24; import {ReentrancyGuardUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; import {BeaconChainProofs} from "lib/eigenlayer-contracts/src/contracts/libraries/BeaconChainProofs.sol"; import {IDelegationManager } from "lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IEigenPodManager } from "lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {IEigenPod } from "lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPod.sol"; import {ISignatureUtils} from "lib/eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IStrategy} from "lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IBeacon} from "lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol"; import {IEigenPodManager} from "lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {IStakingNodesManager} from "src/interfaces/IStakingNodesManager.sol"; import {IStakingNode} from "src/interfaces/IStakingNode.sol"; import {RewardsType} from "src/interfaces/IRewardsDistributor.sol"; import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; import {ONE_GWEI, DEFAULT_VALIDATOR_STAKE} from "src/Constants.sol"; interface StakingNodeEvents { event EigenPodCreated(address indexed nodeAddress, address indexed podAddress); event Delegated(address indexed operator, bytes32 approverSalt); event Undelegated(address indexed operator); event NonBeaconChainETHWithdrawalsProcessed(uint256 claimedAmount); event ETHReceived(address sender, uint256 value); event WithdrawnNonBeaconChainETH(uint256 amount, uint256 remainingBalance); event AllocatedStakedETH(uint256 currentUnverifiedStakedETH, uint256 newAmount); event DeallocatedStakedETH(uint256 amount, uint256 currentWithdrawnValidatorPrincipal); event ValidatorRestaked(uint40 indexed validatorIndex, uint64 oracleTimestamp, uint256 effectiveBalanceGwei); event VerifyWithdrawalCredentialsCompleted(uint40 indexed validatorIndex, uint64 oracleTimestamp, uint256 effectiveBalanceGwei); event WithdrawalProcessed( uint40 indexed validatorIndex, uint256 effectiveBalance, bytes32 withdrawalCredentials, uint256 withdrawalAmount, uint64 oracleTimestamp ); event QueuedWithdrawals(uint256 sharesAmount, bytes32[] fullWithdrawalRoots); event CompletedQueuedWithdrawals(IDelegationManager.Withdrawal[] withdrawals, uint256 totalWithdrawalAmount); } /** * @title StakingNode * @dev Implements staking node functionality for the YieldNest protocol, enabling ETH staking, delegation, and rewards management. * Each StakingNode owns exactl one EigenPod which acts as a delegation unit, as it can be associated with exactly one operator. */ contract StakingNode is IStakingNode, StakingNodeEvents, ReentrancyGuardUpgradeable { using BeaconChainProofs for *; //-------------------------------------------------------------------------------------- //---------------------------------- ERRORS ------------------------------------------ //-------------------------------------------------------------------------------------- error NotStakingNodesOperator(); error ETHDepositorNotDelayedWithdrawalRouterOrEigenPod(); error ClaimAmountTooLow(uint256 expected, uint256 actual); error ZeroAddress(); error NotStakingNodesManager(); error NotStakingNodesDelegator(); error NoBalanceToProcess(); error MismatchInExpectedETHBalanceAfterWithdrawals(uint256 actualWithdrawalAmount, uint256 totalWithdrawalAmount); error TransferFailed(); error InsufficientWithdrawnValidatorPrincipal(uint256 amount, uint256 withdrawnValidatorPrincipal); error NotStakingNodesWithdrawer(); //-------------------------------------------------------------------------------------- //---------------------------------- CONSTANTS --------------------------------------- //-------------------------------------------------------------------------------------- IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); //-------------------------------------------------------------------------------------- //---------------------------------- VARIABLES --------------------------------------- //-------------------------------------------------------------------------------------- IStakingNodesManager public stakingNodesManager; IEigenPod public eigenPod; uint256 public nodeId; /** @dev Monitors the ETH balance that was committed to validators allocated to this StakingNode */ uint256 private _unused_former_allocatedETH; /** @dev Accounts for withdrawn ETH balance that can be withdrawn by the StakingNodesManager contract */ uint256 public withdrawnValidatorPrincipal; /** * @dev Accounts for ETH staked with validators whose withdrawal address is this Node's eigenPod. * that is not yet verified with verifyWithdrawalCredentials. * Increases when calling allocateETH, and decreases when verifying with verifyWithdrawalCredentials */ uint256 public unverifiedStakedETH; /** * @dev Amount of shares queued for withdrawal (no longer active in staking). 1 share == 1 ETH. * Increases when calling queueWithdrawals, and decreases when calling completeQueuedWithdrawals. */ uint256 public queuedSharesAmount; /** @dev Allows only a whitelisted address to configure the contract */ modifier onlyOperator() { if(!stakingNodesManager.isStakingNodesOperator(msg.sender)) revert NotStakingNodesOperator(); _; } modifier onlyDelegator() { if (!stakingNodesManager.isStakingNodesDelegator(msg.sender)) revert NotStakingNodesDelegator(); _; } modifier onlyStakingNodesManager() { if(msg.sender != address(stakingNodesManager)) revert NotStakingNodesManager(); _; } modifier onlyStakingNodesWithdrawer() { if (!stakingNodesManager.isStakingNodesWithdrawer(msg.sender)) revert NotStakingNodesWithdrawer(); _; } //-------------------------------------------------------------------------------------- //---------------------------------- INITIALIZATION ---------------------------------- //-------------------------------------------------------------------------------------- receive() external payable { // Consensus Layer rewards and the validator principal will be sent this way. // if (msg.sender != address(stakingNodesManager.delayedWithdrawalRouter()) // && msg.sender != address(eigenPod)) { // revert ETHDepositorNotDelayedWithdrawalRouterOrEigenPod(); // } emit ETHReceived(msg.sender, msg.value); } constructor() { _disableInitializers(); } function initialize(Init memory init) external notZeroAddress(address(init.stakingNodesManager)) initializer { __ReentrancyGuard_init(); stakingNodesManager = init.stakingNodesManager; nodeId = init.nodeId; } function initializeV2(uint256 initialUnverifiedStakedETH) external onlyStakingNodesManager reinitializer(2) { unverifiedStakedETH = initialUnverifiedStakedETH; } //-------------------------------------------------------------------------------------- //---------------------------------- EIGENPOD CREATION ------------------------------ //-------------------------------------------------------------------------------------- /** * @notice Creates an EigenPod if it does not already exist for this StakingNode. * @dev If it does not exist, it proceeds to create a new EigenPod via EigenPodManager * @return The address of the EigenPod associated with this StakingNode. */ function createEigenPod() public nonReentrant returns (IEigenPod) { if (address(eigenPod) != address(0)) return eigenPod; // already have pod IEigenPodManager eigenPodManager = IEigenPodManager(IStakingNodesManager(stakingNodesManager).eigenPodManager()); eigenPodManager.createPod(); eigenPod = eigenPodManager.getPod(address(this)); emit EigenPodCreated(address(this), address(eigenPod)); return eigenPod; } //-------------------------------------------------------------------------------------- //---------------------------------- EXPEDITED WITHDRAWAL --------------------------- //-------------------------------------------------------------------------------------- /** * @notice Processes withdrawals by verifying the node's balance and transferring ETH to the StakingNodesManager. * @dev This function checks if the node's current balance matches the expected balance and then transfers the ETH to the StakingNodesManager. */ function processDelayedWithdrawals() public nonReentrant onlyOperator { // Delayed withdrawals that do not count as validator principal are handled as rewards uint256 balance = address(this).balance - withdrawnValidatorPrincipal; if (balance == 0) { revert NoBalanceToProcess(); } stakingNodesManager.processRewards{value: balance}(nodeId, RewardsType.ConsensusLayer); emit NonBeaconChainETHWithdrawalsProcessed(balance); } //-------------------------------------------------------------------------------------- //---------------------------------- VERIFICATION AND DELEGATION -------------------- //-------------------------------------------------------------------------------------- /** * @dev Validates the withdrawal credentials for a withdrawal. * This activates the activation of the staked funds within EigenLayer. * @param beaconTimestamp The timestamp of the oracle that signed the block. * @param stateRootProof The state root proof. * @param validatorIndices The indices of the validators. * @param validatorFieldsProofs The validator fields proofs. * @param validatorFields The validator fields. */ function verifyWithdrawalCredentials( uint64 beaconTimestamp, BeaconChainProofs.StateRootProof calldata stateRootProof, uint40[] calldata validatorIndices, bytes[] calldata validatorFieldsProofs, bytes32[][] calldata validatorFields ) external onlyOperator { IEigenPod(address(eigenPod)).verifyWithdrawalCredentials( beaconTimestamp, stateRootProof, validatorIndices, validatorFieldsProofs, validatorFields ); for (uint256 i = 0; i < validatorIndices.length; i++) { // If the validator is already exited, the effectiveBalanceGwei is 0. // if the validator has not been exited, the effectiveBalanceGwei is whatever is staked // (32ETH in the absence of slasing, and less than that if slashed) uint256 effectiveBalanceGwei = validatorFields[i].getEffectiveBalanceGwei(); emit VerifyWithdrawalCredentialsCompleted(validatorIndices[i], beaconTimestamp, effectiveBalanceGwei); // If the effectiveBalanceGwei is not 0, then the full stake of the validator // is verified as part of this process and shares are credited to this StakingNode instance. // This assumes StakingNodesManager.sol always stakes the full 32 ETH in one go. // effectiveBalanceGwei *may* be less than DEFAULT_VALIDATOR_STAKE if the validator was slashed. unverifiedStakedETH -= DEFAULT_VALIDATOR_STAKE; emit ValidatorRestaked(validatorIndices[i], beaconTimestamp, effectiveBalanceGwei); } } /** * @dev Create a checkpoint used to prove the pod's active validator set. * This function can only be called by the Operator. * @param revertIfNoBalance Forces a revert if the pod ETH balance is 0. */ function startCheckpoint(bool revertIfNoBalance) external onlyOperator { eigenPod.startCheckpoint(revertIfNoBalance); } //-------------------------------------------------------------------------------------- //---------------------------------- DELEGATION ------------------------------------- //-------------------------------------------------------------------------------------- /** * @notice Delegates authority to an operator. * @dev Delegates the staking node's authority to an operator using a signature with expiry. * @param operator The address of the operator to whom the delegation is made. * @param approverSignatureAndExpiry The signature of the approver along with its expiry details. * @param approverSalt The unique salt used to prevent replay attacks. */ function delegate( address operator, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt ) public override onlyDelegator { IDelegationManager delegationManager = IDelegationManager(address(stakingNodesManager.delegationManager())); delegationManager.delegateTo(operator, approverSignatureAndExpiry, approverSalt); emit Delegated(operator, approverSalt); } /** * @notice Undelegates the authority previously delegated to an operator. * @dev This function revokes the delegation by calling the `undelegate` method on the `DelegationManager`. * It emits an `Undelegated` event with the address of the operator from whom the delegation is being removed. */ function undelegate() public onlyDelegator { IDelegationManager delegationManager = IDelegationManager(address(stakingNodesManager.delegationManager())); address operator = delegationManager.delegatedTo(address(this)); emit Undelegated(operator); delegationManager.undelegate(address(this)); } //-------------------------------------------------------------------------------------- //---------------------------------- WITHDRAWALS ------------------------------------- //-------------------------------------------------------------------------------------- /** * @dev Queues a validator Principal withdrawal for processing. DelegationManager calls EigenPodManager.decreasesShares * which decreases the `podOwner`'s shares by `shares`, down to a minimum of zero. * @param sharesAmount The amount of shares to be queued for withdrawals. * @return fullWithdrawalRoots An array of keccak256 hashes of each withdrawal created. */ function queueWithdrawals( uint256 sharesAmount ) external onlyStakingNodesWithdrawer returns (bytes32[] memory fullWithdrawalRoots) { IDelegationManager delegationManager = IDelegationManager(address(stakingNodesManager.delegationManager())); IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); IStrategy[] memory strategies = new IStrategy[](1); // Assumption: 1 Share of beaconChainETHStrategy = 1 ETH. uint256[] memory shares = new uint256[](1); strategies[0] = beaconChainETHStrategy; shares[0] = sharesAmount; // The delegationManager requires the withdrawer == msg.sender (the StakingNode in this case). params[0] = IDelegationManager.QueuedWithdrawalParams({ strategies: strategies, shares: shares, withdrawer: address(this) }); fullWithdrawalRoots = delegationManager.queueWithdrawals(params); // After running queueWithdrawals, eigenPodManager.podOwnerShares(address(this)) decreases by `sharesAmount`. // Therefore queuedSharesAmount increase by `sharesAmount`. queuedSharesAmount += sharesAmount; emit QueuedWithdrawals(sharesAmount, fullWithdrawalRoots); } /** * @dev Triggers the completion of particular queued withdrawals. * Withdrawals can only be completed if * max(delegationManager.minWithdrawalDelayBlocks(), delegationManager.strategyWithdrawalDelayBlocks(beaconChainETHStrategy)) * number of blocks have passed since withdrawal was queued. * @param withdrawals The Withdrawals to complete. This withdrawalRoot (keccak hash of the Withdrawal) must match the * the withdrawal created as part of the queueWithdrawals call. * @param middlewareTimesIndexes The middlewareTimesIndex parameter has to do * with the Slasher, which currently does nothing. As of M2, this parameter * has no bearing on anything and can be ignored */ function completeQueuedWithdrawals( IDelegationManager.Withdrawal[] memory withdrawals, uint256[] memory middlewareTimesIndexes ) external onlyStakingNodesWithdrawer { uint256 totalWithdrawalAmount = 0; bool[] memory receiveAsTokens = new bool[](withdrawals.length); IERC20[][] memory tokens = new IERC20[][](withdrawals.length); for (uint256 i = 0; i < withdrawals.length; i++) { // Set receiveAsTokens to true to receive ETH when completeQueuedWithdrawals runs. ///IMPORTANT: beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` // and `withdrawal.withdrawer != withdrawal.staker`, 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. receiveAsTokens[i] = true; // tokens array must match length of the withdrawals[i].strategies // but does not need actual values in the case of the beaconChainETHStrategy tokens[i] = new IERC20[](withdrawals[i].strategies.length); for (uint256 j = 0; j < withdrawals[i].shares.length; j++) { totalWithdrawalAmount += withdrawals[i].shares[j]; } } IDelegationManager delegationManager = IDelegationManager(address(stakingNodesManager.delegationManager())); uint256 initialETHBalance = address(this).balance; // NOTE: completeQueuedWithdrawals can only be called by withdrawal.withdrawer for each withdrawal // The Eigenlayer beaconChainETHStrategy queued withdrawal completion flow follows the following steps: // 1. The flow starts in the DelegationManager where queued withdrawals are managed. // 2. For beaconChainETHStrategy, the DelegationManager calls _withdrawSharesAsTokens interacts with the EigenPodManager.withdrawSharesAsTokens // 3. Finally, the EigenPodManager calls withdrawRestakedBeaconChainETH on the EigenPod of this StakingNode to finalize the withdrawal. // 4. the EigenPod decrements withdrawableRestakedExecutionLayerGwei and send the ETH to address(this) delegationManager.completeQueuedWithdrawals(withdrawals, tokens, middlewareTimesIndexes, receiveAsTokens); uint256 finalETHBalance = address(this).balance; uint256 actualWithdrawalAmount = finalETHBalance - initialETHBalance; if (actualWithdrawalAmount != totalWithdrawalAmount) { revert MismatchInExpectedETHBalanceAfterWithdrawals(actualWithdrawalAmount, totalWithdrawalAmount); } // Shares are no longer queued queuedSharesAmount -= actualWithdrawalAmount; // Withdraw validator principal resides in the StakingNode until StakingNodesManager retrieves it. withdrawnValidatorPrincipal += actualWithdrawalAmount; emit CompletedQueuedWithdrawals(withdrawals, totalWithdrawalAmount); } //-------------------------------------------------------------------------------------- //---------------------------------- ETH BALANCE ACCOUNTING -------------------------- //-------------------------------------------------------------------------------------- /** * @dev Record total staked ETH for this StakingNode */ function allocateStakedETH(uint256 amount) external payable onlyStakingNodesManager { emit AllocatedStakedETH(unverifiedStakedETH, amount); unverifiedStakedETH += amount; } /** * @notice Deallocates a specified amount of staked ETH from the withdrawn validator principal * and transfers it to the StakingNodesManager. * @dev This function can only be called by the StakingNodesManager. It emits a DeallocatedStakedETH * event upon successful deallocation. * @param amount The amount of ETH to deallocate and transfer. */ function deallocateStakedETH(uint256 amount) external payable onlyStakingNodesManager { if (amount > withdrawnValidatorPrincipal) { revert InsufficientWithdrawnValidatorPrincipal(amount, withdrawnValidatorPrincipal); } emit DeallocatedStakedETH(amount, withdrawnValidatorPrincipal); withdrawnValidatorPrincipal -= amount; (bool success, ) = address(stakingNodesManager).call{value: amount}(""); if (!success) { revert TransferFailed(); } } function getETHBalance() public view returns (uint256) { IEigenPodManager eigenPodManager = IEigenPodManager(IStakingNodesManager(stakingNodesManager).eigenPodManager()); // TODO: unverifiedStakedETH MUST be initialized to the correct value // ad deploy time // Example: If ALL validators have been verified it MUST be 0 // If NONE of the validators have been verified it MUST be equal to former allocatedETH int256 totalETHBalance = int256(withdrawnValidatorPrincipal + unverifiedStakedETH + queuedSharesAmount) + eigenPodManager.podOwnerShares(address(this)); if (totalETHBalance < 0) { return 0; } return uint256(totalETHBalance); } /** * @notice Retrieves the amount of unverified staked ETH held by this StakingNode. * @return The amount of unverified staked ETH in wei. */ function getUnverifiedStakedETH() public view returns (uint256) { return unverifiedStakedETH; } /** * @notice Retrieves the amount of shares currently queued for withdrawal. * @return The amount of queued shares. */ function getQueuedSharesAmount() public view returns (uint256) { return queuedSharesAmount; } /** * @notice Retrieves the amount of ETH that has been withdrawn from validators and is held by this StakingNode. * @return The amount of withdrawn validator principal in wei. */ function getWithdrawnValidatorPrincipal() public view returns (uint256) { return withdrawnValidatorPrincipal; } //-------------------------------------------------------------------------------------- //---------------------------------- BEACON IMPLEMENTATION --------------------------- //-------------------------------------------------------------------------------------- /** Beacons slot value is defined here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/afb20119b33072da041c97ea717d3ce4417b5e01/contracts/proxy/ERC1967/ERC1967Upgrade.sol#L142 */ function implementation() public view returns (address) { bytes32 slot = bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1); address implementationVariable; assembly { implementationVariable := sload(slot) } IBeacon beacon = IBeacon(implementationVariable); return beacon.implementation(); } /** * @notice Retrieve the version number of the highest/newest initialize * function that was executed. */ function getInitializedVersion() external view returns (uint64) { return _getInitializedVersion(); } //-------------------------------------------------------------------------------------- //---------------------------------- MODIFIERS --------------------------------------- //-------------------------------------------------------------------------------------- /** * @notice Ensure that the given address is not the zero address. * @param _address The address to check. */ modifier notZeroAddress(address _address) { if (_address == address(0)) { revert ZeroAddress(); } _; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard struct ReentrancyGuardStorage { uint256 _status; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { $.slot := ReentrancyGuardStorageLocation } } /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); $._status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // On the first call to nonReentrant, _status will be NOT_ENTERED if ($._status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail $._status = ENTERED; } function _nonReentrantAfter() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) $._status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); return $._status == ENTERED; } }
// 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 { /// @notice Heights of various merkle trees in the beacon chain /// - beaconBlockRoot /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT /// -- beaconStateRoot /// | HEIGHT: BEACON_STATE_TREE_HEIGHT /// validatorContainerRoot, balanceContainerRoot /// | | HEIGHT: BALANCE_TREE_HEIGHT /// | individual balances /// | HEIGHT: VALIDATOR_TREE_HEIGHT /// individual validators uint256 internal constant BEACON_BLOCK_HEADER_TREE_HEIGHT = 3; uint256 internal constant BEACON_STATE_TREE_HEIGHT = 5; uint256 internal constant BALANCE_TREE_HEIGHT = 38; uint256 internal constant VALIDATOR_TREE_HEIGHT = 40; /// @notice Index of the beaconStateRoot in the `BeaconBlockHeader` container /// /// BeaconBlockHeader = [..., state_root, ...] /// 0... 3 /// /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#beaconblockheader) uint256 internal constant STATE_ROOT_INDEX = 3; /// @notice Indices for fields in the `BeaconState` container /// /// BeaconState = [..., validators, balances, ...] /// 0... 11 12 /// /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate) uint256 internal constant VALIDATOR_CONTAINER_INDEX = 11; uint256 internal constant BALANCE_CONTAINER_INDEX = 12; /// @notice Number of fields in the `Validator` container /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator) uint256 internal constant VALIDATOR_FIELDS_LENGTH = 8; /// @notice Indices for fields in the `Validator` container 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_SLASHED_INDEX = 3; uint256 internal constant VALIDATOR_EXIT_EPOCH_INDEX = 6; /// @notice Slot/Epoch timings uint64 internal constant SECONDS_PER_SLOT = 12; uint64 internal constant SLOTS_PER_EPOCH = 32; uint64 internal constant SECONDS_PER_EPOCH = SLOTS_PER_EPOCH * SECONDS_PER_SLOT; /// @notice `FAR_FUTURE_EPOCH` is used as the default value for certain `Validator` /// fields when a `Validator` is first created on the beacon chain uint64 internal constant FAR_FUTURE_EPOCH = type(uint64).max; bytes8 internal constant UINT64_MASK = 0xffffffffffffffff; /// @notice Contains a beacon state root and a merkle proof verifying its inclusion under a beacon block root struct StateRootProof { bytes32 beaconStateRoot; bytes proof; } /// @notice Contains a validator's fields and a merkle proof of their inclusion under a beacon state root struct ValidatorProof { bytes32[] validatorFields; bytes proof; } /// @notice Contains a beacon balance container root and a proof of this root under a beacon block root struct BalanceContainerProof { bytes32 balanceContainerRoot; bytes proof; } /// @notice Contains a validator balance root and a proof of its inclusion under a balance container root struct BalanceProof { bytes32 pubkeyHash; bytes32 balanceRoot; bytes proof; } /******************************************************************************* VALIDATOR FIELDS -> BEACON STATE ROOT -> BEACON BLOCK ROOT *******************************************************************************/ /// @notice Verify a merkle proof of the beacon state root against a beacon block root /// @param beaconBlockRoot merkle root of the beacon block /// @param proof the beacon state root and merkle proof of its inclusion under `beaconBlockRoot` function verifyStateRoot( bytes32 beaconBlockRoot, StateRootProof calldata proof ) internal view { require( proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT), "BeaconChainProofs.verifyStateRoot: Proof has incorrect length" ); /// This merkle proof verifies the `beaconStateRoot` under the `beaconBlockRoot` /// - beaconBlockRoot /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT /// -- beaconStateRoot require( Merkle.verifyInclusionSha256({ proof: proof.proof, root: beaconBlockRoot, leaf: proof.beaconStateRoot, index: STATE_ROOT_INDEX }), "BeaconChainProofs.verifyStateRoot: Invalid state root merkle proof" ); } /// @notice Verify a merkle proof of a validator container against a `beaconStateRoot` /// @dev This proof starts at a validator's container root, proves through the validator container root, /// and continues proving to the root of the `BeaconState` /// @dev See https://eth2book.info/capella/part3/containers/dependencies/#validator for info on `Validator` containers /// @dev See https://eth2book.info/capella/part3/containers/state/#beaconstate for info on `BeaconState` containers /// @param beaconStateRoot merkle root of the `BeaconState` container /// @param validatorFields an individual validator's fields. These are merklized to form a `validatorRoot`, /// which is used as the leaf to prove against `beaconStateRoot` /// @param validatorFieldsProof a merkle proof of inclusion of `validatorFields` under `beaconStateRoot` /// @param validatorIndex the validator's unique index function verifyValidatorFields( bytes32 beaconStateRoot, bytes32[] calldata validatorFields, bytes calldata validatorFieldsProof, uint40 validatorIndex ) internal view { require( validatorFields.length == VALIDATOR_FIELDS_LENGTH, "BeaconChainProofs.verifyValidatorFields: Validator fields has incorrect length" ); /// Note: the reason we use `VALIDATOR_TREE_HEIGHT + 1` here is because the merklization process for /// this container includes hashing the root of the validator tree with the length of the validator list require( validatorFieldsProof.length == 32 * ((VALIDATOR_TREE_HEIGHT + 1) + BEACON_STATE_TREE_HEIGHT), "BeaconChainProofs.verifyValidatorFields: Proof has incorrect length" ); // Merkleize `validatorFields` to get the leaf to prove bytes32 validatorRoot = Merkle.merkleizeSha256(validatorFields); /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees: /// - beaconStateRoot /// | HEIGHT: BEACON_STATE_TREE_HEIGHT /// -- validatorContainerRoot /// | HEIGHT: VALIDATOR_TREE_HEIGHT + 1 /// ---- validatorRoot uint256 index = (VALIDATOR_CONTAINER_INDEX << (VALIDATOR_TREE_HEIGHT + 1)) | uint256(validatorIndex); require( Merkle.verifyInclusionSha256({ proof: validatorFieldsProof, root: beaconStateRoot, leaf: validatorRoot, index: index }), "BeaconChainProofs.verifyValidatorFields: Invalid merkle proof" ); } /******************************************************************************* VALIDATOR BALANCE -> BALANCE CONTAINER ROOT -> BEACON BLOCK ROOT *******************************************************************************/ /// @notice Verify a merkle proof of the beacon state's balances container against the beacon block root /// @dev This proof starts at the balance container root, proves through the beacon state root, and /// continues proving through the beacon block root. As a result, this proof will contain elements /// of a `StateRootProof` under the same block root, with the addition of proving the balances field /// within the beacon state. /// @dev This is used to make checkpoint proofs more efficient, as a checkpoint will verify multiple balances /// against the same balance container root. /// @param beaconBlockRoot merkle root of the beacon block /// @param proof a beacon balance container root and merkle proof of its inclusion under `beaconBlockRoot` function verifyBalanceContainer( bytes32 beaconBlockRoot, BalanceContainerProof calldata proof ) internal view { require( proof.proof.length == 32 * (BEACON_BLOCK_HEADER_TREE_HEIGHT + BEACON_STATE_TREE_HEIGHT), "BeaconChainProofs.verifyBalanceContainer: Proof has incorrect length" ); /// This proof combines two proofs, so its index accounts for the relative position of leaves in two trees: /// - beaconBlockRoot /// | HEIGHT: BEACON_BLOCK_HEADER_TREE_HEIGHT /// -- beaconStateRoot /// | HEIGHT: BEACON_STATE_TREE_HEIGHT /// ---- balancesContainerRoot uint256 index = (STATE_ROOT_INDEX << (BEACON_STATE_TREE_HEIGHT)) | BALANCE_CONTAINER_INDEX; require( Merkle.verifyInclusionSha256({ proof: proof.proof, root: beaconBlockRoot, leaf: proof.balanceContainerRoot, index: index }), "BeaconChainProofs.verifyBalanceContainer: invalid balance container proof" ); } /// @notice Verify a merkle proof of a validator's balance against the beacon state's `balanceContainerRoot` /// @param balanceContainerRoot the merkle root of all validators' current balances /// @param validatorIndex the index of the validator whose balance we are proving /// @param proof the validator's associated balance root and a merkle proof of inclusion under `balanceContainerRoot` /// @return validatorBalanceGwei the validator's current balance (in gwei) function verifyValidatorBalance( bytes32 balanceContainerRoot, uint40 validatorIndex, BalanceProof calldata proof ) internal view returns (uint64 validatorBalanceGwei) { /// Note: the reason we use `BALANCE_TREE_HEIGHT + 1` here is because the merklization process for /// this container includes hashing the root of the balances tree with the length of the balances list require( proof.proof.length == 32 * (BALANCE_TREE_HEIGHT + 1), "BeaconChainProofs.verifyValidatorBalance: Proof has incorrect length" ); /// When merkleized, beacon chain balances are combined into groups of 4 called a `balanceRoot`. The merkle /// proof here verifies that this validator's `balanceRoot` is included in the `balanceContainerRoot` /// - balanceContainerRoot /// | HEIGHT: BALANCE_TREE_HEIGHT /// -- balanceRoot uint256 balanceIndex = uint256(validatorIndex / 4); require( Merkle.verifyInclusionSha256({ proof: proof.proof, root: balanceContainerRoot, leaf: proof.balanceRoot, index: balanceIndex }), "BeaconChainProofs.verifyValidatorBalance: Invalid merkle proof" ); /// Extract the individual validator's balance from the `balanceRoot` return getBalanceAtIndex(proof.balanceRoot, validatorIndex); } /** * @notice Parses a balanceRoot to get the uint64 balance of a validator. * @dev During merkleization of the beacon state balance tree, four uint64 values are treated as a single * leaf in the merkle tree. We use validatorIndex % 4 to determine which of the four uint64 values to * extract from the balanceRoot. * @param balanceRoot is the combination of 4 validator balances being proven for * @param validatorIndex is the index of the validator being proven for * @return The validator's balance, in Gwei */ function getBalanceAtIndex(bytes32 balanceRoot, uint40 validatorIndex) internal pure returns (uint64) { uint256 bitShiftAmount = (validatorIndex % 4) * 64; return Endian.fromLittleEndianUint64(bytes32((uint256(balanceRoot) << bitShiftAmount))); } /// @notice Indices for fields in the `Validator` container: /// 0: pubkey /// 1: withdrawal credentials /// 2: effective balance /// 3: slashed? /// 4: activation elligibility epoch /// 5: activation epoch /// 6: exit epoch /// 7: withdrawable epoch /// /// (See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator) /// @dev Retrieves a validator's pubkey hash function getPubkeyHash(bytes32[] memory validatorFields) internal pure returns (bytes32) { return validatorFields[VALIDATOR_PUBKEY_INDEX]; } /// @dev Retrieves a validator's withdrawal credentials 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 true IFF a validator is marked slashed function isValidatorSlashed(bytes32[] memory validatorFields) internal pure returns (bool) { return validatorFields[VALIDATOR_SLASHED_INDEX] != 0; } /// @dev Retrieves a validator's exit epoch function getExitEpoch(bytes32[] memory validatorFields) internal pure returns (uint64) { return Endian.fromLittleEndianUint64(validatorFields[VALIDATOR_EXIT_EPOCH_INDEX]); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; import "./IStrategy.sol"; import "./ISignatureUtils.sol"; import "./IStrategyManager.sol"; /** * @title DelegationManager * @author Layr Labs, Inc. * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service * @notice This is the contract for delegation in EigenLayer. The main functionalities of this contract are * - enabling anyone to register as an operator in EigenLayer * - allowing operators to specify parameters related to stakers who delegate to them * - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time) * - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager) */ interface IDelegationManager is ISignatureUtils { // @notice Struct used for storing information about a single operator who has registered with EigenLayer struct OperatorDetails { /// @notice DEPRECATED -- this field is no longer used, payments are handled in PaymentCoordinator.sol address __deprecated_earningsReceiver; /** * @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations". * @dev Signature verification follows these rules: * 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed. * 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator. * 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value". */ address delegationApprover; /** * @notice A minimum delay -- measured in blocks -- enforced between: * 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing` * and * 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate` * @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails, * then they are only allowed to either increase this value or keep it the same. */ uint32 stakerOptOutWindowBlocks; } /** * @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator. * @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function. */ struct StakerDelegation { // the staker who is delegating address staker; // the operator being delegated to address operator; // the staker's nonce uint256 nonce; // the expiration timestamp (UTC) of the signature uint256 expiry; } /** * @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator. * @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function. */ struct DelegationApproval { // the staker who is delegating address staker; // the operator being delegated to address operator; // the operator's provided salt bytes32 salt; // the expiration timestamp (UTC) of the signature uint256 expiry; } /** * Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored. * In functions that operate on existing queued withdrawals -- e.g. completeQueuedWithdrawal`, the data is resubmitted and the hash of the submitted * data is computed by `calculateWithdrawalRoot` and checked against the stored hash in order to confirm the integrity of the submitted data. */ struct Withdrawal { // The address that originated the Withdrawal address staker; // The address that the staker was delegated to at the time that the Withdrawal was created address delegatedTo; // The address that can complete the Withdrawal + will receive funds when completing the withdrawal address withdrawer; // Nonce used to guarantee that otherwise identical withdrawals have unique hashes uint256 nonce; // Block number when the Withdrawal was created uint32 startBlock; // Array of strategies that the Withdrawal contains IStrategy[] strategies; // Array containing the amount of shares in each Strategy in the `strategies` array uint256[] shares; } struct QueuedWithdrawalParams { // Array of strategies that the QueuedWithdrawal contains IStrategy[] strategies; // Array containing the amount of shares in each Strategy in the `strategies` array uint256[] shares; // The address of the withdrawer address withdrawer; } // @notice Emitted when a new operator registers in EigenLayer and provides their OperatorDetails. event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails); /// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails); /** * @notice Emitted when @param operator indicates that they are updating their MetadataURI string * @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing */ event OperatorMetadataURIUpdated(address indexed operator, string metadataURI); /// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares. event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); /// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares. event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares); /// @notice Emitted when @param staker delegates to @param operator. event StakerDelegated(address indexed staker, address indexed operator); /// @notice Emitted when @param staker undelegates from @param operator. event StakerUndelegated(address indexed staker, address indexed operator); /// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself event StakerForceUndelegated(address indexed staker, address indexed operator); /** * @notice Emitted when a new withdrawal is queued. * @param withdrawalRoot Is the hash of the `withdrawal`. * @param withdrawal Is the withdrawal itself. */ event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal); /// @notice Emitted when a queued withdrawal is completed event WithdrawalCompleted(bytes32 withdrawalRoot); /// @notice Emitted when the `minWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`. event MinWithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue); /// @notice Emitted when the `strategyWithdrawalDelayBlocks` variable is modified from `previousValue` to `newValue`. event StrategyWithdrawalDelayBlocksSet(IStrategy strategy, uint256 previousValue, uint256 newValue); /** * @notice Registers the caller as an operator in EigenLayer. * @param registeringOperatorDetails is the `OperatorDetails` for the operator. * @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator. * * @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself". * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event */ function registerAsOperator( OperatorDetails calldata registeringOperatorDetails, string calldata metadataURI ) external; /** * @notice Updates an operator's stored `OperatorDetails`. * @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`. * * @dev The caller must have previously registered as an operator in EigenLayer. */ function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external; /** * @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated. * @param metadataURI The URI for metadata associated with an operator * @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event */ function updateOperatorMetadataURI(string calldata metadataURI) external; /** * @notice Caller delegates their stake to an operator. * @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on EigenLayer. * @param approverSignatureAndExpiry Verifies the operator approves of this delegation * @param approverSalt A unique single use value tied to an individual signature. * @dev The approverSignatureAndExpiry is used in the event that: * 1) the operator's `delegationApprover` address is set to a non-zero value. * AND * 2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator * or their delegationApprover is the `msg.sender`, then approval is assumed. * @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input * in this case to save on complexity + gas costs */ function delegateTo( address operator, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt ) external; /** * @notice Caller delegates a staker's stake to an operator with valid signatures from both parties. * @param staker The account delegating stake to an `operator` account * @param operator The account (`staker`) is delegating its assets to for use in serving applications built on EigenLayer. * @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator * @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that: * @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver. * * @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action. * @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271. * @dev the operator's `delegationApprover` address is set to a non-zero value. * @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover * is the `msg.sender`, then approval is assumed. * @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry * @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input * in this case to save on complexity + gas costs */ function delegateToBySignature( address staker, address operator, SignatureWithExpiry memory stakerSignatureAndExpiry, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt ) external; /** * @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager * and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary. * @param staker The account to be undelegated. * @return withdrawalRoot The root of the newly queued withdrawal, if a withdrawal was queued. Otherwise just bytes32(0). * * @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves. * @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover" * @dev Reverts if the `staker` is already undelegated. */ function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot); /** * Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed * from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from * their operator. * * All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay. */ function queueWithdrawals( QueuedWithdrawalParams[] calldata queuedWithdrawalParams ) external returns (bytes32[] memory); /** * @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer` * @param withdrawal The Withdrawal to complete. * @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array. * This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused) * @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array * @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves * and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies * will simply be transferred to the caller directly. * @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw` * @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that * any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in * any other strategies, which will be transferred to the withdrawer. */ function completeQueuedWithdrawal( Withdrawal calldata withdrawal, IERC20[] calldata tokens, uint256 middlewareTimesIndex, bool receiveAsTokens ) external; /** * @notice Array-ified version of `completeQueuedWithdrawal`. * Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer` * @param withdrawals The Withdrawals to complete. * @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array. * @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index. * @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean. * @dev See `completeQueuedWithdrawal` for relevant dev tags */ function completeQueuedWithdrawals( Withdrawal[] calldata withdrawals, IERC20[][] calldata tokens, uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens ) external; /** * @notice Increases a staker's delegated share balance in a strategy. * @param staker The address to increase the delegated shares for their operator. * @param strategy The strategy in which to increase the delegated shares. * @param shares The number of shares to increase. * * @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing. * @dev Callable only by the StrategyManager or EigenPodManager. */ function increaseDelegatedShares( address staker, IStrategy strategy, uint256 shares ) external; /** * @notice Decreases a staker's delegated share balance in a strategy. * @param staker The address to increase the delegated shares for their operator. * @param strategy The strategy in which to decrease the delegated shares. * @param shares The number of shares to decrease. * * @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing. * @dev Callable only by the StrategyManager or EigenPodManager. */ function decreaseDelegatedShares( address staker, IStrategy strategy, uint256 shares ) external; /** * @notice returns the address of the operator that `staker` is delegated to. * @notice Mapping: staker => operator whom the staker is currently delegated to. * @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator. */ function delegatedTo(address staker) external view returns (address); /** * @notice Returns the OperatorDetails struct associated with an `operator`. */ function operatorDetails(address operator) external view returns (OperatorDetails memory); /** * @notice Returns the delegationApprover account for an operator */ function delegationApprover(address operator) external view returns (address); /** * @notice Returns the stakerOptOutWindowBlocks for an operator */ function stakerOptOutWindowBlocks(address operator) external view returns (uint256); /** * @notice Given array of strategies, returns array of shares for the operator */ function getOperatorShares( address operator, IStrategy[] memory strategies ) external view returns (uint256[] memory); /** * @notice Given a list of strategies, return the minimum number of blocks that must pass to withdraw * from all the inputted strategies. Return value is >= minWithdrawalDelayBlocks as this is the global min withdrawal delay. * @param strategies The strategies to check withdrawal delays for */ function getWithdrawalDelay(IStrategy[] calldata strategies) external view returns (uint256); /** * @notice returns the total number of shares in `strategy` that are delegated to `operator`. * @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator. * @dev By design, the following invariant should hold for each Strategy: * (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator) * = sum (delegateable shares of all stakers delegated to the operator) */ function operatorShares(address operator, IStrategy strategy) external view returns (uint256); /** * @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise. */ function isDelegated(address staker) external view returns (bool); /** * @notice Returns true is an operator has previously registered for delegation. */ function isOperator(address operator) external view returns (bool); /// @notice Mapping: staker => number of signed delegation nonces (used in `delegateToBySignature`) from the staker that the contract has already checked function stakerNonce(address staker) external view returns (uint256); /** * @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover. * @dev Salts are used in the `delegateTo` and `delegateToBySignature` functions. Note that these functions only process the delegationApprover's * signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`. */ function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool); /** * @notice Minimum delay enforced by this contract for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). * Note that strategies each have a separate withdrawal delay, which can be greater than this value. So the minimum number of blocks that must pass * to withdraw a strategy is MAX(minWithdrawalDelayBlocks, strategyWithdrawalDelayBlocks[strategy]) */ function minWithdrawalDelayBlocks() external view returns (uint256); /** * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). */ function strategyWithdrawalDelayBlocks(IStrategy strategy) external view returns (uint256); /// @notice return address of the beaconChainETHStrategy function beaconChainETHStrategy() external view returns (IStrategy); /** * @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator` * @param staker The signing staker * @param operator The operator who is being delegated to * @param expiry The desired expiry time of the staker's signature */ function calculateCurrentStakerDelegationDigestHash( address staker, address operator, uint256 expiry ) external view returns (bytes32); /** * @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function * @param staker The signing staker * @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]` * @param operator The operator who is being delegated to * @param expiry The desired expiry time of the staker's signature */ function calculateStakerDelegationDigestHash( address staker, uint256 _stakerNonce, address operator, uint256 expiry ) external view returns (bytes32); /** * @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions. * @param staker The account delegating their stake * @param operator The account receiving delegated stake * @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general) * @param approverSalt A unique and single use value associated with the approver signature. * @param expiry Time after which the approver's signature becomes invalid */ function calculateDelegationApprovalDigestHash( address staker, address operator, address _delegationApprover, bytes32 approverSalt, uint256 expiry ) external view returns (bytes32); /// @notice The EIP-712 typehash for the contract's domain function DOMAIN_TYPEHASH() external view returns (bytes32); /// @notice The EIP-712 typehash for the StakerDelegation struct used by the contract function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32); /// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32); /** * @notice Getter function for the current EIP-712 domain separator for this contract. * * @dev The domain separator will change in the event of a fork that changes the ChainID. * @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision. * for more detailed information please read EIP-712. */ function domainSeparator() external view returns (bytes32); /// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated. /// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes. function cumulativeWithdrawalsQueued(address staker) external view returns (uint256); /// @notice Returns the keccak256 hash of `withdrawal`. function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import "./IETHPOSDeposit.sol"; import "./IStrategyManager.sol"; import "./IEigenPod.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 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 every time the total shares of a pod are updated event NewTotalShares(address indexed podOwner, int256 newTotalShares); /// @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 ); /** * @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 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 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; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; import "../libraries/BeaconChainProofs.sol"; import "./IEigenPodManager.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 * @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 { /******************************************************************************* STRUCTS / ENUMS *******************************************************************************/ 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 lastCheckpointedAt; // status of the validator VALIDATOR_STATUS status; } struct Checkpoint { bytes32 beaconBlockRoot; uint24 proofsRemaining; uint64 podBalanceGwei; int128 balanceDeltasGwei; } /******************************************************************************* EVENTS *******************************************************************************/ /// @notice Emitted when an ETH validator stakes via this eigenPod event EigenPodStaked(bytes pubkey); /// @notice Emitted when a pod owner updates the proof submitter address event ProofSubmitterUpdated(address prevProofSubmitter, address newProofSubmitter); /// @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 restaked beacon chain ETH is withdrawn from the eigenPod. event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount); /// @notice Emitted when ETH is received via the `receive` fallback event NonBeaconChainETHReceived(uint256 amountReceived); /// @notice Emitted when a checkpoint is created event CheckpointCreated(uint64 indexed checkpointTimestamp, bytes32 indexed beaconBlockRoot, uint256 validatorCount); /// @notice Emitted when a checkpoint is finalized event CheckpointFinalized(uint64 indexed checkpointTimestamp, int256 totalShareDeltaWei); /// @notice Emitted when a validator is proven for a given checkpoint event ValidatorCheckpointed(uint64 indexed checkpointTimestamp, uint40 indexed validatorIndex); /// @notice Emitted when a validaor is proven to have 0 balance at a given checkpoint event ValidatorWithdrawn(uint64 indexed checkpointTimestamp, uint40 indexed validatorIndex); /******************************************************************************* EXTERNAL STATE-CHANGING METHODS *******************************************************************************/ /// @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; /** * @dev Create a checkpoint used to prove this pod's active validator set. Checkpoints are completed * by submitting one checkpoint proof per ACTIVE validator. During the checkpoint process, the total * change in ACTIVE validator balance is tracked, and any validators with 0 balance are marked `WITHDRAWN`. * @dev Once finalized, the pod owner is awarded shares corresponding to: * - the total change in their ACTIVE validator balances * - any ETH in the pod not already awarded shares * @dev A checkpoint cannot be created if the pod already has an outstanding checkpoint. If * this is the case, the pod owner MUST complete the existing checkpoint before starting a new one. * @param revertIfNoBalance Forces a revert if the pod ETH balance is 0. This allows the pod owner * to prevent accidentally starting a checkpoint that will not increase their shares */ function startCheckpoint(bool revertIfNoBalance) external; /** * @dev Progress the current checkpoint towards completion by submitting one or more validator * checkpoint proofs. Anyone can call this method to submit proofs towards the current checkpoint. * For each validator proven, the current checkpoint's `proofsRemaining` decreases. * @dev If the checkpoint's `proofsRemaining` reaches 0, the checkpoint is finalized. * (see `_updateCheckpoint` for more details) * @dev This method can only be called when there is a currently-active checkpoint. * @param balanceContainerProof proves the beacon's current balance container root against a checkpoint's `beaconBlockRoot` * @param proofs Proofs for one or more validator current balances against the `balanceContainerRoot` */ function verifyCheckpointProofs( BeaconChainProofs.BalanceContainerProof calldata balanceContainerProof, BeaconChainProofs.BalanceProof[] calldata proofs ) external; /** * @dev Verify one or more validators have their withdrawal credentials pointed at this EigenPod, and award * shares based on their effective balance. Proven validators are marked `ACTIVE` within the EigenPod, and * future checkpoint proofs will need to include them. * @dev Withdrawal credential proofs MUST NOT be older than `currentCheckpointTimestamp`. * @dev Validators proven via this method MUST NOT have an exit epoch set already. * @param beaconTimestamp the beacon chain timestamp sent to the 4788 oracle contract. Corresponds * to the parent beacon block root against which the proof is verified. * @param stateRootProof proves a beacon state root against a beacon block root * @param validatorIndices a list of validator indices being proven * @param validatorFieldsProofs proofs of each validator's `validatorFields` against the beacon state root * @param validatorFields the fields of the beacon chain "Validator" container. See consensus specs for * details: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator */ function verifyWithdrawalCredentials( uint64 beaconTimestamp, BeaconChainProofs.StateRootProof calldata stateRootProof, uint40[] calldata validatorIndices, bytes[] calldata validatorFieldsProofs, bytes32[][] calldata validatorFields ) external; /** * @dev Prove that one of this pod's active validators was slashed on the beacon chain. A successful * staleness proof allows the caller to start a checkpoint. * * @dev Note that in order to start a checkpoint, any existing checkpoint must already be completed! * (See `_startCheckpoint` for details) * * @dev Note that this method allows anyone to start a checkpoint as soon as a slashing occurs on the beacon * chain. This is intended to make it easier to external watchers to keep a pod's balance up to date. * * @dev Note too that beacon chain slashings are not instant. There is a delay between the initial slashing event * and the validator's final exit back to the execution layer. During this time, the validator's balance may or * may not drop further due to a correlation penalty. This method allows proof of a slashed validator * to initiate a checkpoint for as long as the validator remains on the beacon chain. Once the validator * has exited and been checkpointed at 0 balance, they are no longer "checkpoint-able" and cannot be proven * "stale" via this method. * See https://eth2book.info/capella/part3/transition/epoch/#slashings for more info. * * @param beaconTimestamp the beacon chain timestamp sent to the 4788 oracle contract. Corresponds * to the parent beacon block root against which the proof is verified. * @param stateRootProof proves a beacon state root against a beacon block root * @param proof the fields of the beacon chain "Validator" container, along with a merkle proof against * the beacon state root. See the consensus specs for more details: * https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#validator * * @dev Staleness conditions: * - Validator's last checkpoint is older than `beaconTimestamp` * - Validator MUST be in `ACTIVE` status in the pod * - Validator MUST be slashed on the beacon chain */ function verifyStaleBalance( uint64 beaconTimestamp, BeaconChainProofs.StateRootProof calldata stateRootProof, BeaconChainProofs.ValidatorProof calldata proof ) 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; /// @notice Allows the owner of a pod to update the proof submitter, a permissioned /// address that can call `startCheckpoint` and `verifyWithdrawalCredentials`. /// @dev Note that EITHER the podOwner OR proofSubmitter can access these methods, /// so it's fine to set your proofSubmitter to 0 if you want the podOwner to be the /// only address that can call these methods. /// @param newProofSubmitter The new proof submitter address. If set to 0, only the /// pod owner will be able to call `startCheckpoint` and `verifyWithdrawalCredentials` function setProofSubmitter(address newProofSubmitter) external; /******************************************************************************* VIEW METHODS *******************************************************************************/ /// @notice An address with permissions to call `startCheckpoint` and `verifyWithdrawalCredentials`, set /// by the podOwner. This role exists to allow a podOwner to designate a hot wallet that can call /// these methods, allowing the podOwner to remain a cold wallet that is only used to manage funds. /// @dev If this address is NOT set, only the podOwner can call `startCheckpoint` and `verifyWithdrawalCredentials` function proofSubmitter() external view returns (address); /// @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 The single EigenPodManager for EigenLayer function eigenPodManager() external view returns (IEigenPodManager); /// @notice The owner of this EigenPod function podOwner() external view returns (address); /// @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 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 Number of validators with proven withdrawal credentials, who do not have proven full withdrawals function activeValidatorCount() external view returns (uint256); /// @notice The timestamp of the last checkpoint finalized function lastCheckpointTimestamp() external view returns (uint64); /// @notice The timestamp of the currently-active checkpoint. Will be 0 if there is not active checkpoint function currentCheckpointTimestamp() external view returns (uint64); /// @notice Returns the currently-active checkpoint function currentCheckpoint() external view returns (Checkpoint memory); /// @notice For each checkpoint, the total balance attributed to exited validators, in gwei /// /// NOTE that the values added to this mapping are NOT guaranteed to capture the entirety of a validator's /// exit - rather, they capture the total change in a validator's balance when a checkpoint shows their /// balance change from nonzero to zero. While a change from nonzero to zero DOES guarantee that a validator /// has been fully exited, it is possible that the magnitude of this change does not capture what is /// typically thought of as a "full exit." /// /// For example: /// 1. Consider a validator was last checkpointed at 32 ETH before exiting. Once the exit has been processed, /// it is expected that the validator's exited balance is calculated to be `32 ETH`. /// 2. However, before `startCheckpoint` is called, a deposit is made to the validator for 1 ETH. The beacon /// chain will automatically withdraw this ETH, but not until the withdrawal sweep passes over the validator /// again. Until this occurs, the validator's current balance (used for checkpointing) is 1 ETH. /// 3. If `startCheckpoint` is called at this point, the balance delta calculated for this validator will be /// `-31 ETH`, and because the validator has a nonzero balance, it is not marked WITHDRAWN. /// 4. After the exit is processed by the beacon chain, a subsequent `startCheckpoint` and checkpoint proof /// will calculate a balance delta of `-1 ETH` and attribute a 1 ETH exit to the validator. /// /// If this edge case impacts your usecase, it should be possible to mitigate this by monitoring for deposits /// to your exited validators, and waiting to call `startCheckpoint` until those deposits have been automatically /// exited. /// /// Additional edge cases this mapping does not cover: /// - If a validator is slashed, their balance exited will reflect their original balance rather than the slashed amount /// - The final partial withdrawal for an exited validator will be likely be included in this mapping. /// i.e. if a validator was last checkpointed at 32.1 ETH before exiting, the next checkpoint will calculate their /// "exited" amount to be 32.1 ETH rather than 32 ETH. function checkpointBalanceExitedGwei(uint64) external view returns (uint64); /// @notice Query the 4788 oracle to get the parent block root of the slot with the given `timestamp` /// @param timestamp of the block for which the parent block root will be returned. MUST correspond /// to an existing slot within the last 24 hours. If the slot at `timestamp` was skipped, this method /// will revert. function getParentBlockRoot(uint64 timestamp) 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.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: BSD 3-Clause License pragma solidity ^0.8.24; import {UpgradeableBeacon} from "lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol"; // import {IDelayedWithdrawalRouter} from "lib/eigenlayer-contracts/src/contracts/interfaces/IDelayedWithdrawalRouter.sol"; import {IDelegationManager} from "lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "lib/eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {RewardsType} from "src/interfaces/IRewardsDistributor.sol"; import {IEigenPodManager} from "lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {IStakingNode} from "src/interfaces/IStakingNode.sol"; import {IRedemptionAssetsVault} from "src/interfaces/IRedemptionAssetsVault.sol"; interface IStakingNodesManager { struct ValidatorData { bytes publicKey; bytes signature; bytes32 depositDataRoot; uint256 nodeId; } struct Validator { bytes publicKey; uint256 nodeId; } struct WithdrawalAction { uint256 nodeId; uint256 amountToReinvest; uint256 amountToQueue; } function eigenPodManager() external view returns (IEigenPodManager); function delegationManager() external view returns (IDelegationManager); function strategyManager() external view returns (IStrategyManager); // function delayedWithdrawalRouter() external view returns (IDelayedWithdrawalRouter); function getAllValidators() external view returns (Validator[] memory); function getAllNodes() external view returns (IStakingNode[] memory); function isStakingNodesOperator(address) external view returns (bool); function isStakingNodesDelegator(address _address) external view returns (bool); function processRewards(uint256 nodeId, RewardsType rewardsType) external payable; function registerValidators( ValidatorData[] calldata _depositData ) external; function nodesLength() external view returns (uint256); function upgradeableBeacon() external returns (UpgradeableBeacon); function totalDeposited() external view returns (uint256); function processPrincipalWithdrawals( WithdrawalAction[] memory actions ) external; function redemptionAssetsVault() external returns (IRedemptionAssetsVault); function isStakingNodesWithdrawer(address _address) external view returns (bool); }
// SPDX-License-Identifier: BSD 3-Clause License pragma solidity ^0.8.24; import {BeaconChainProofs} from "lib/eigenlayer-contracts/src/contracts/libraries/BeaconChainProofs.sol"; import {IStakingNodesManager} from "src/interfaces/IStakingNodesManager.sol"; import {IStrategy} from "lib/eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IEigenPod} from "lib/eigenlayer-contracts/src/contracts/interfaces/IEigenPod.sol"; import {ISignatureUtils} from "lib/eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; struct WithdrawalCompletionParams { uint256 middlewareTimesIndex; uint256 amount; uint32 withdrawalStartBlock; address delegatedAddress; uint96 nonce; } interface IStakingEvents { /// @notice Emitted when a user stakes ETH and receives ynETH. /// @param staker The address of the user staking ETH. /// @param ethAmount The amount of ETH staked. /// @param ynETHAmount The amount of ynETH received. event Staked(address indexed staker, uint256 ethAmount, uint256 ynETHAmount); event DepositETHPausedUpdated(bool isPaused); event Deposit(address indexed sender, address indexed receiver, uint256 assets, uint256 shares); } interface IStakingNode { /// @notice Configuration for contract initialization. struct Init { IStakingNodesManager stakingNodesManager; uint256 nodeId; } function stakingNodesManager() external view returns (IStakingNodesManager); function eigenPod() external view returns (IEigenPod); function initialize(Init memory init) external; function createEigenPod() external returns (IEigenPod); function delegate( address operator, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt ) external; function undelegate() external; // function withdrawNonBeaconChainETHBalanceWei() external; function processDelayedWithdrawals() external; function implementation() external view returns (address); function allocateStakedETH(uint256 amount) external payable; function deallocateStakedETH(uint256 amount) external payable; function getETHBalance() external view returns (uint256); function unverifiedStakedETH() external view returns (uint256); function nodeId() external view returns (uint256); /// @notice Returns the beaconChainETHStrategy address used by the StakingNode. function beaconChainETHStrategy() external view returns (IStrategy); /** * @notice Verifies the withdrawal credentials and balance of validators. * @param oracleTimestamp An array of oracle block numbers corresponding to each validator. * @param stateRootProof An array of state root proofs corresponding to each validator. * @param validatorIndices An array of validator indices. * @param validatorFieldsProofs An array of ValidatorFieldsAndBalanceProofs, containing the merkle proofs for validator fields and balances. * @param validatorFields An array of arrays, each containing the validator fields to be verified. */ function verifyWithdrawalCredentials( uint64 oracleTimestamp, BeaconChainProofs.StateRootProof calldata stateRootProof, uint40[] calldata validatorIndices, bytes[] calldata validatorFieldsProofs, bytes32[][] calldata validatorFields ) external; function queueWithdrawals( uint256 sharesAmount ) external returns (bytes32[] memory fullWithdrawalRoots); function completeQueuedWithdrawals( IDelegationManager.Withdrawal[] memory withdrawals, uint256[] memory middlewareTimesIndexes ) external; function getInitializedVersion() external view returns (uint64); function getUnverifiedStakedETH() external view returns (uint256); function getQueuedSharesAmount() external view returns (uint256); function getWithdrawnValidatorPrincipal() external view returns (uint256); function initializeV2(uint256 initialUnverifiedStakedETH) external; }
// SPDX-License-Identifier: BSD 3-Clause License pragma solidity ^0.8.24; import {IRewardsReceiver} from "src/interfaces/IRewardsReceiver.sol"; enum RewardsType { ExecutionLayer, ConsensusLayer } interface IRewardsDistributor { /// @notice Returns the address of the ynETH token. /// @return address of the ynETH token. function ynETH() external view returns (address); /// @notice Processes the rewards for the execution and consensus layer. /// @dev This function should be called by off-chain rewards distribution service. function processRewards() external; /// @notice Returns the address of the execution layer rewards receiver. /// @return address of the execution layer rewards receiver. function executionLayerReceiver() external view returns (IRewardsReceiver); /// @notice Returns the address of the consensus layer rewards receiver. /// @return address of the consensus layer rewards receiver. function consensusLayerReceiver() external view returns (IRewardsReceiver); /// @notice Returns the address of the fees receiver. /// @return address of the fees receiver. function feesReceiver() external view returns (address); /// @notice Returns the protocol fees in basis points (1/10000). /// @return uint16 fees in basis points. function feesBasisPoints() external view returns (uint16); /// @notice Sets the address to receive protocol fees. /// @param newReceiver The new fees receiver address. function setFeesReceiver(address payable newReceiver) external; /// @notice Sets the protocol fees in basis points (1/10000). /// @param newFeesBasisPoints The new fees in basis points. function setFeesBasisPoints(uint16 newFeesBasisPoints) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: BSD 3-Clause License pragma solidity ^0.8.24; import {IStrategy} from "lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; address constant ETH_ASSET = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; uint256 constant YNETH_UNIT = 1e18; uint256 constant ONE_GWEI = 1e9; IStrategy constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); uint256 constant DEFAULT_VALIDATOR_STAKE = 32 ether;
// 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: MIT // Adapted from OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates merkle trees that are safe * against this attack out of the box. */ library Merkle { /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. The tree is built assuming `leaf` is * the 0 indexed `index`'th leaf from the bottom left of the tree. * * Note this is for a Merkle tree using the keccak/sha3 hash function */ function verifyInclusionKeccak( bytes memory proof, bytes32 root, bytes32 leaf, uint256 index ) internal pure returns (bool) { return processInclusionProofKeccak(proof, leaf, index) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. The tree is built assuming `leaf` is * the 0 indexed `index`'th leaf from the bottom left of the tree. * @dev If the proof length is 0 then the leaf hash is returned. * * _Available since v4.4._ * * Note this is for a Merkle tree using the keccak/sha3 hash function */ function processInclusionProofKeccak( bytes memory proof, bytes32 leaf, uint256 index ) internal pure returns (bytes32) { require( proof.length % 32 == 0, "Merkle.processInclusionProofKeccak: proof length should be a multiple of 32" ); bytes32 computedHash = leaf; for (uint256 i = 32; i <= proof.length; i += 32) { if (index % 2 == 0) { // if ith bit of index is 0, then computedHash is a left sibling assembly { mstore(0x00, computedHash) mstore(0x20, mload(add(proof, i))) computedHash := keccak256(0x00, 0x40) index := div(index, 2) } } else { // if ith bit of index is 1, then computedHash is a right sibling assembly { mstore(0x00, mload(add(proof, i))) mstore(0x20, computedHash) computedHash := keccak256(0x00, 0x40) index := div(index, 2) } } } return computedHash; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. The tree is built assuming `leaf` is * the 0 indexed `index`'th leaf from the bottom left of the tree. * * Note this is for a Merkle tree using the sha256 hash function */ function verifyInclusionSha256( bytes memory proof, bytes32 root, bytes32 leaf, uint256 index ) internal view returns (bool) { return processInclusionProofSha256(proof, leaf, index) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. The tree is built assuming `leaf` is * the 0 indexed `index`'th leaf from the bottom left of the tree. * * _Available since v4.4._ * * Note this is for a Merkle tree using the sha256 hash function */ function processInclusionProofSha256( bytes memory proof, bytes32 leaf, uint256 index ) internal view returns (bytes32) { require( proof.length != 0 && proof.length % 32 == 0, "Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32" ); bytes32[1] memory computedHash = [leaf]; for (uint256 i = 32; i <= proof.length; i += 32) { if (index % 2 == 0) { // if ith bit of index is 0, then computedHash is a left sibling assembly { mstore(0x00, mload(computedHash)) mstore(0x20, mload(add(proof, i))) if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) } index := div(index, 2) } } else { // if ith bit of index is 1, then computedHash is a right sibling assembly { mstore(0x00, mload(add(proof, i))) mstore(0x20, mload(computedHash)) if iszero(staticcall(sub(gas(), 2000), 2, 0x00, 0x40, computedHash, 0x20)) { revert(0, 0) } index := div(index, 2) } } } return computedHash[0]; } /** @notice this function returns the merkle root of a tree created from a set of leaves using sha256 as its hash function @param leaves the leaves of the merkle tree @return The computed Merkle root of the tree. @dev A pre-condition to this function is that leaves.length is a power of two. If not, the function will merkleize the inputs incorrectly. */ function merkleizeSha256(bytes32[] memory leaves) internal pure returns (bytes32) { //there are half as many nodes in the layer above the leaves uint256 numNodesInLayer = leaves.length / 2; //create a layer to store the internal nodes bytes32[] memory layer = new bytes32[](numNodesInLayer); //fill the layer with the pairwise hashes of the leaves for (uint256 i = 0; i < numNodesInLayer; i++) { layer[i] = sha256(abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])); } //the next layer above has half as many nodes numNodesInLayer /= 2; //while we haven't computed the root while (numNodesInLayer != 0) { //overwrite the first numNodesInLayer nodes in layer with the pairwise hashes of their children for (uint256 i = 0; i < numNodesInLayer; i++) { layer[i] = sha256(abi.encodePacked(layer[2 * i], layer[2 * i + 1])); } //the next layer above has half as many nodes numNodesInLayer /= 2; } //the first node in the layer is the root return layer[0]; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; library Endian { /** * @notice Converts a little endian-formatted uint64 to a big endian-formatted uint64 * @param lenum little endian-formatted uint64 input, provided as 'bytes32' type * @return n The big endian-formatted uint64 * @dev Note that the input is formatted as a 'bytes32' type (i.e. 256 bits), but it is immediately truncated to a uint64 (i.e. 64 bits) * through a right-shift/shr operation. */ function fromLittleEndianUint64(bytes32 lenum) internal pure returns (uint64 n) { // the number needs to be stored in little-endian encoding (ie in bytes 0-8) n = uint64(uint256(lenum >> 192)); return (n >> 56) | ((0x00FF000000000000 & n) >> 40) | ((0x0000FF0000000000 & n) >> 24) | ((0x000000FF00000000 & n) >> 8) | ((0x00000000FF000000 & n) << 8) | ((0x0000000000FF0000 & n) << 24) | ((0x000000000000FF00 & n) << 40) | ((0x00000000000000FF & n) << 56); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; import "./IStrategy.sol"; import "./ISlasher.sol"; import "./IDelegationManager.sol"; import "./IEigenPodManager.sol"; /** * @title Interface for the primary entrypoint for funds into EigenLayer. * @author Layr Labs, Inc. * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service * @notice See the `StrategyManager` contract itself for implementation details. */ interface IStrategyManager { /** * @notice Emitted when a new deposit occurs on behalf of `staker`. * @param staker Is the staker who is depositing funds into EigenLayer. * @param strategy Is the strategy that `staker` has deposited into. * @param token Is the token that `staker` deposited. * @param shares Is the number of new shares `staker` has been granted in `strategy`. */ event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares); /// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value); /// @notice Emitted when the `strategyWhitelister` is changed event StrategyWhitelisterChanged(address previousAddress, address newAddress); /// @notice Emitted when a strategy is added to the approved list of strategies for deposit event StrategyAddedToDepositWhitelist(IStrategy strategy); /// @notice Emitted when a strategy is removed from the approved list of strategies for deposit event StrategyRemovedFromDepositWhitelist(IStrategy strategy); /** * @notice Deposits `amount` of `token` into the specified `strategy`, with the resultant shares credited to `msg.sender` * @param strategy is the specified strategy where deposit is to be made, * @param token is the denomination in which the deposit is to be made, * @param amount is the amount of token to be deposited in the strategy by the staker * @return shares The amount of new shares in the `strategy` created as part of the action. * @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf. * @dev Cannot be called by an address that is 'frozen' (this function will revert if the `msg.sender` is frozen). * * WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors * where the token balance and corresponding strategy shares are not in sync upon reentrancy. */ function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares); /** * @notice Used for depositing an asset into the specified strategy with the resultant shares credited to `staker`, * who must sign off on the action. * Note that the assets are transferred out/from the `msg.sender`, not from the `staker`; this function is explicitly designed * purely to help one address deposit 'for' another. * @param strategy is the specified strategy where deposit is to be made, * @param token is the denomination in which the deposit is to be made, * @param amount is the amount of token to be deposited in the strategy by the staker * @param staker the staker that the deposited assets will be credited to * @param expiry the timestamp at which the signature expires * @param signature is a valid signature from the `staker`. either an ECDSA signature if the `staker` is an EOA, or data to forward * following EIP-1271 if the `staker` is a contract * @return shares The amount of new shares in the `strategy` created as part of the action. * @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf. * @dev A signature is required for this function to eliminate the possibility of griefing attacks, specifically those * targeting stakers who may be attempting to undelegate. * @dev Cannot be called if thirdPartyTransfersForbidden is set to true for this strategy * * WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors * where the token balance and corresponding strategy shares are not in sync upon reentrancy */ function depositIntoStrategyWithSignature( IStrategy strategy, IERC20 token, uint256 amount, address staker, uint256 expiry, bytes memory signature ) external returns (uint256 shares); /// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue function removeShares(address staker, IStrategy strategy, uint256 shares) external; /// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external; /// @notice Used by the DelegationManager to convert withdrawn shares to tokens and send them to a recipient function withdrawSharesAsTokens(address recipient, IStrategy strategy, uint256 shares, IERC20 token) external; /// @notice Returns the current shares of `user` in `strategy` function stakerStrategyShares(address user, IStrategy strategy) external view returns (uint256 shares); /** * @notice Get all details on the staker's deposits and corresponding shares * @return (staker's strategies, shares in these strategies) */ function getDeposits(address staker) external view returns (IStrategy[] memory, uint256[] memory); /// @notice Simple getter function that returns `stakerStrategyList[staker].length`. function stakerStrategyListLength(address staker) external view returns (uint256); /** * @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into * @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already) * @param thirdPartyTransfersForbiddenValues bool values to set `thirdPartyTransfersForbidden` to for each strategy */ function addStrategiesToDepositWhitelist( IStrategy[] calldata strategiesToWhitelist, bool[] calldata thirdPartyTransfersForbiddenValues ) external; /** * @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into * @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it) */ function removeStrategiesFromDepositWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external; /// @notice Returns the single, central Delegation contract of EigenLayer function delegation() external view returns (IDelegationManager); /// @notice Returns the single, central Slasher contract of EigenLayer function slasher() external view returns (ISlasher); /// @notice Returns the EigenPodManager contract of EigenLayer function eigenPodManager() external view returns (IEigenPodManager); /// @notice Returns the address of the `strategyWhitelister` function strategyWhitelister() external view returns (address); /// @notice Returns bool for whether or not `strategy` is whitelisted for deposit function strategyIsWhitelistedForDeposit(IStrategy strategy) external view returns (bool); /** * @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling * depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker. */ function thirdPartyTransfersForbidden(IStrategy strategy) external view returns (bool); }
// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━ // ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓ // ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛ // ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━ // ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓ // ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // SPDX-License-Identifier: 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 "../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.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: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the 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.0) (proxy/beacon/UpgradeableBeacon.sol) pragma solidity ^0.8.20; import {IBeacon} from "./IBeacon.sol"; import {Ownable} from "../../access/Ownable.sol"; /** * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their * implementation contract, which is where they will delegate all function calls. * * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon. */ contract UpgradeableBeacon is IBeacon, Ownable { address private _implementation; /** * @dev The `implementation` of the beacon is invalid. */ error BeaconInvalidImplementation(address implementation); /** * @dev Emitted when the implementation returned by the beacon is changed. */ event Upgraded(address indexed implementation); /** * @dev Sets the address of the initial implementation, and the initial owner who can upgrade the beacon. */ constructor(address implementation_, address initialOwner) Ownable(initialOwner) { _setImplementation(implementation_); } /** * @dev Returns the current implementation address. */ function implementation() public view virtual returns (address) { return _implementation; } /** * @dev Upgrades the beacon to a new implementation. * * Emits an {Upgraded} event. * * Requirements: * * - msg.sender must be the owner of the contract. * - `newImplementation` must be a contract. */ function upgradeTo(address newImplementation) public virtual onlyOwner { _setImplementation(newImplementation); } /** * @dev Sets the implementation contract address for this beacon * * Requirements: * * - `newImplementation` must be a contract. */ function _setImplementation(address newImplementation) private { if (newImplementation.code.length == 0) { revert BeaconInvalidImplementation(newImplementation); } _implementation = newImplementation; emit Upgraded(newImplementation); } }
// SPDX-License-Identifier: BSD 3-Clause License pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IRedemptionAssetsVault { // Events event AssetsDeposited( address indexed asset, address indexed depositor, uint256 amount); event AssetTransferred(address indexed asset, address indexed redeemer, address indexed to, uint256 amount); event AssetWithdrawn(address indexed asset, address indexed redeemer, address indexed to, uint256 amount); /// @notice Transfers redemption assets to a specified address based on redemption. /// @dev This is only for INTERNAL USE /// @param to The address to which the assets will be transferred. /// @param amount The amount in unit of account function transferRedemptionAssets(address to, uint256 amount) external; /// @notice Withdraws redemption assets from the queue's balance /// @param amount The amount in unit of account function withdrawRedemptionAssets(uint256 amount) external; /// @notice Retrieves the current redemption rate for the asset in the unit of account. /// @return The current redemption rate function redemptionRate() external view returns (uint256); /// @notice Gets the total amount of redemption assets available for withdrawal in the unit of account. /// @return The available amount of redemption assets function availableRedemptionAssets() external view returns (uint256); }
// SPDX-License-Identifier: BSD 3-Clause License pragma solidity ^0.8.24; import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; interface IRewardsReceiver { /// @notice Configuration for contract initialization. struct Init { address admin; address withdrawer; } /// @notice Initializes the contract. /// @dev MUST be called during the contract upgrade to set up the proxies state. function initialize(Init memory init) external; /// @notice Transfers the given amount of ETH to an address. /// @dev Only callable by the withdrawer. function transferETH(address payable to, uint256 amount) external; /// @notice Transfers the given amount of an ERC20 token to an address. /// @dev Only callable by the withdrawer. function transferERC20(IERC20 token, address to, uint256 amount) external; }
// 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 // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * 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 Ownable is Context { address private _owner; /** * @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. */ constructor(address initialOwner) { 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) { 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 { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @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 Context { 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; } }
{ "remappings": [ "@openzeppelin-upgrades/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable/", "@openzeppelin/=lib/openzeppelin-contracts/", "@eigenlayer-contracts/=lib/eigenlayer-contracts/src/contracts/", "@openzeppelin-upgrades-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/", "@openzeppelin-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "eigenlayer-contracts/=lib/eigenlayer-contracts/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "forge-std/=lib/forge-std/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/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/", "safe-smart-account/=lib/safe-smart-account/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"name":"ClaimAmountTooLow","type":"error"},{"inputs":[],"name":"ETHDepositorNotDelayedWithdrawalRouterOrEigenPod","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"withdrawnValidatorPrincipal","type":"uint256"}],"name":"InsufficientWithdrawnValidatorPrincipal","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualWithdrawalAmount","type":"uint256"},{"internalType":"uint256","name":"totalWithdrawalAmount","type":"uint256"}],"name":"MismatchInExpectedETHBalanceAfterWithdrawals","type":"error"},{"inputs":[],"name":"NoBalanceToProcess","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotStakingNodesDelegator","type":"error"},{"inputs":[],"name":"NotStakingNodesManager","type":"error"},{"inputs":[],"name":"NotStakingNodesOperator","type":"error"},{"inputs":[],"name":"NotStakingNodesWithdrawer","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"currentUnverifiedStakedETH","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAmount","type":"uint256"}],"name":"AllocatedStakedETH","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"delegatedTo","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint32","name":"startBlock","type":"uint32"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"indexed":false,"internalType":"struct IDelegationManager.Withdrawal[]","name":"withdrawals","type":"tuple[]"},{"indexed":false,"internalType":"uint256","name":"totalWithdrawalAmount","type":"uint256"}],"name":"CompletedQueuedWithdrawals","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"currentWithdrawnValidatorPrincipal","type":"uint256"}],"name":"DeallocatedStakedETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bytes32","name":"approverSalt","type":"bytes32"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"ETHReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nodeAddress","type":"address"},{"indexed":true,"internalType":"address","name":"podAddress","type":"address"}],"name":"EigenPodCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"name":"NonBeaconChainETHWithdrawalsProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sharesAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32[]","name":"fullWithdrawalRoots","type":"bytes32[]"}],"name":"QueuedWithdrawals","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"Undelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint40","name":"validatorIndex","type":"uint40"},{"indexed":false,"internalType":"uint64","name":"oracleTimestamp","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"effectiveBalanceGwei","type":"uint256"}],"name":"ValidatorRestaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint40","name":"validatorIndex","type":"uint40"},{"indexed":false,"internalType":"uint64","name":"oracleTimestamp","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"effectiveBalanceGwei","type":"uint256"}],"name":"VerifyWithdrawalCredentialsCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint40","name":"validatorIndex","type":"uint40"},{"indexed":false,"internalType":"uint256","name":"effectiveBalance","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"withdrawalCredentials","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"withdrawalAmount","type":"uint256"},{"indexed":false,"internalType":"uint64","name":"oracleTimestamp","type":"uint64"}],"name":"WithdrawalProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"}],"name":"WithdrawnNonBeaconChainETH","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"allocateStakedETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"beaconChainETHStrategy","outputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"delegatedTo","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint32","name":"startBlock","type":"uint32"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"internalType":"struct IDelegationManager.Withdrawal[]","name":"withdrawals","type":"tuple[]"},{"internalType":"uint256[]","name":"middlewareTimesIndexes","type":"uint256[]"}],"name":"completeQueuedWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"createEigenPod","outputs":[{"internalType":"contract IEigenPod","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deallocateStakedETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct ISignatureUtils.SignatureWithExpiry","name":"approverSignatureAndExpiry","type":"tuple"},{"internalType":"bytes32","name":"approverSalt","type":"bytes32"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eigenPod","outputs":[{"internalType":"contract IEigenPod","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getETHBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitializedVersion","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQueuedSharesAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnverifiedStakedETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithdrawnValidatorPrincipal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IStakingNodesManager","name":"stakingNodesManager","type":"address"},{"internalType":"uint256","name":"nodeId","type":"uint256"}],"internalType":"struct IStakingNode.Init","name":"init","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"initialUnverifiedStakedETH","type":"uint256"}],"name":"initializeV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nodeId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"processDelayedWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesAmount","type":"uint256"}],"name":"queueWithdrawals","outputs":[{"internalType":"bytes32[]","name":"fullWithdrawalRoots","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"queuedSharesAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingNodesManager","outputs":[{"internalType":"contract IStakingNodesManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"revertIfNoBalance","type":"bool"}],"name":"startCheckpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"undelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unverifiedStakedETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"beaconTimestamp","type":"uint64"},{"components":[{"internalType":"bytes32","name":"beaconStateRoot","type":"bytes32"},{"internalType":"bytes","name":"proof","type":"bytes"}],"internalType":"struct BeaconChainProofs.StateRootProof","name":"stateRootProof","type":"tuple"},{"internalType":"uint40[]","name":"validatorIndices","type":"uint40[]"},{"internalType":"bytes[]","name":"validatorFieldsProofs","type":"bytes[]"},{"internalType":"bytes32[][]","name":"validatorFields","type":"bytes32[][]"}],"name":"verifyWithdrawalCredentials","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawnValidatorPrincipal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
608060405234801562000010575f80fd5b506200001b62000021565b620000d5565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000725760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d25780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b612a2080620000e35f395ff3fe608060405260043610610164575f3560e01c80636eade7c8116100cd57806392ab89bb11610087578063bfe8172d11610062578063bfe8172d14610409578063d60909931461041d578063f55b304014610432578063f99d9e5514610451575f80fd5b806392ab89bb146103aa578063a3aae136146103be578063b3c65015146103dd575f80fd5b80636eade7c8146102f157806377b349271461030657806379cb2d2f14610325578063852aa33d1461033857806388676cad146103645780639104c31914610383575f80fd5b8063443dc4281161011e578063443dc428146102595780635b4e128c146102775780635c60da1b146102965780635d21e3da146102aa57806360aa71fb146102c95780636e947298146102dd575f80fd5b80630b10b201146101a7578063139d7fed146101d857806313bebcf9146101fb57806319259db01461020f5780631f1fb913146102245780633f65cf1914610238575f80fd5b366101a357604080513381523460208201527fbfe611b001dfcd411432f7bf0d79b82b4b2ee81511edac123a3403c357fb972a910160405180910390a1005b5f80fd5b3480156101b2575f80fd5b506101bb610464565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101e3575f80fd5b506101ed60025481565b6040519081526020016101cf565b348015610206575f80fd5b506004546101ed565b34801561021a575f80fd5b506101ed60065481565b34801561022f575f80fd5b506006546101ed565b348015610243575f80fd5b50610257610252366004611cb8565b61063f565b005b348015610264575f80fd5b505f546101bb906001600160a01b031681565b348015610282575f80fd5b50610257610291366004611d85565b6108bf565b3480156102a1575f80fd5b506101bb6109a8565b3480156102b5575f80fd5b506102576102c4366004611e51565b610a4a565b3480156102d4575f80fd5b50610257610bed565b3480156102e8575f80fd5b506101ed610d62565b3480156102fc575f80fd5b506101ed60045481565b348015610311575f80fd5b50610257610320366004611f2b565b610e77565b610257610333366004611d85565b610fc1565b348015610343575f80fd5b50610357610352366004611d85565b6110e6565b6040516101cf9190611f9d565b34801561036f575f80fd5b5061025761037e366004611fc3565b6113df565b34801561038e575f80fd5b506101bb73beac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac081565b3480156103b5575f80fd5b506102576114c2565b3480156103c9575f80fd5b506001546101bb906001600160a01b031681565b3480156103e8575f80fd5b506103f16116cb565b6040516001600160401b0390911681526020016101cf565b348015610414575f80fd5b506005546101ed565b348015610428575f80fd5b506101ed60055481565b34801561043d575f80fd5b5061025761044c3660046120e4565b6116ef565b61025761045f366004611d85565b611abe565b5f61046d611b3d565b6001546001600160a01b03161561049057506001546001600160a01b0316610626565b5f805f9054906101000a90046001600160a01b03166001600160a01b0316634665bcda6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104e0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105049190612254565b9050806001600160a01b03166384d810626040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610543573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105679190612254565b5060405163a38406a360e01b81523060048201526001600160a01b0382169063a38406a390602401602060405180830381865afa1580156105aa573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105ce9190612254565b600180546001600160a01b0319166001600160a01b0392909216918217905560405130907fcdc82cfed67d9b46d3a15dd3b48745fb894a354d554cb5da5fb8c440f85c108e905f90a350506001546001600160a01b03165b61063c60015f805160206129ab83398151915255565b90565b5f5460405163d02e02cf60e01b81523360048201526001600160a01b039091169063d02e02cf90602401602060405180830381865afa158015610684573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106a8919061226f565b6106c55760405163304ac09760e01b815260040160405180910390fd5b600154604051633f65cf1960e01b81526001600160a01b0390911690633f65cf1990610703908b908b908b908b908b908b908b908b90600401612429565b5f604051808303815f87803b15801561071a575f80fd5b505af115801561072c573d5f803e3d5ffd5b505050505f5b858110156108b4575f610798848484818110610750576107506124e1565b905060200281019061076291906124f5565b808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250611b8792505050565b6001600160401b031690508787838181106107b5576107b56124e1565b90506020020160208101906107ca919061253a565b604080516001600160401b038d1681526020810184905264ffffffffff92909216917fc893ff408bc759105622c2fb34c5fa34a9d25f376cae386441e090e9d1ce1c3e910160405180910390a26801bc16d674ec80000060055f8282546108319190612567565b909155508890508783818110610849576108496124e1565b905060200201602081019061085e919061253a565b604080516001600160401b038d1681526020810184905264ffffffffff92909216917f95636f5f7adc353e75700ba4b3473fe8e0fdba943a168a06699b6e2a8c283d9a910160405180910390a250600101610732565b505050505050505050565b5f546001600160a01b031633146108e9576040516346f94cdd60e11b815260040160405180910390fd5b5f805160206129cb833981519152805460029190600160401b900460ff168061091f575080546001600160401b03808416911610155b1561093d5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b038316908117600160401b1782556005849055815460ff60401b191682556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050565b5f806109d560017fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d51612567565b5f1b90505f815490505f819050806001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a1e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a429190612254565b935050505090565b5f5460405163def8c18960e01b81523360048201526001600160a01b039091169063def8c18990602401602060405180830381865afa158015610a8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab3919061226f565b610ad05760405163c97c994760e01b815260040160405180910390fd5b5f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b449190612254565b60405163eea9064b60e01b81529091506001600160a01b0382169063eea9064b90610b779087908790879060040161257a565b5f604051808303815f87803b158015610b8e575f80fd5b505af1158015610ba0573d5f803e3d5ffd5b50505050836001600160a01b03167fa6ca69be1634c9486160d4fa9f11c9bf604a6a4b1fd23c8336ffc5889ef4b5ab83604051610bdf91815260200190565b60405180910390a250505050565b610bf5611b3d565b5f5460405163d02e02cf60e01b81523360048201526001600160a01b039091169063d02e02cf90602401602060405180830381865afa158015610c3a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c5e919061226f565b610c7b5760405163304ac09760e01b815260040160405180910390fd5b5f60045447610c8a9190612567565b9050805f03610cac5760405163e35ba1ef60e01b815260040160405180910390fd5b5f54600254604051638639285160e01b81526001600160a01b03909216916386392851918491610ce291906001906004016125f1565b5f604051808303818588803b158015610cf9575f80fd5b505af1158015610d0b573d5f803e3d5ffd5b50505050507fe759cd78e0cc36ad25dc7e3cb3e4b57e938c83e661b14e2f602717083edda6bc81604051610d4191815260200190565b60405180910390a150610d6060015f805160206129ab83398151915255565b565b5f805460408051632332de6d60e11b8152905183926001600160a01b031691634665bcda9160048083019260209291908290030181865afa158015610da9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd9190612254565b6040516360f4062b60e01b81523060048201529091505f906001600160a01b038316906360f4062b90602401602060405180830381865afa158015610e14573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e389190612621565b600654600554600454610e4b9190612638565b610e559190612638565b610e5f919061264b565b90505f811215610e71575f9250505090565b92915050565b80516001600160a01b038116610ea05760405163d92e233d60e01b815260040160405180910390fd5b5f805160206129cb8339815191528054600160401b810460ff1615906001600160401b03165f81158015610ed15750825b90505f826001600160401b03166001148015610eec5750303b155b905081158015610efa575080155b15610f185760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f4257845460ff60401b1916600160401b1785555b610f4a611c0d565b86515f80546001600160a01b0319166001600160a01b0390921691909117905560208701516002558315610fb857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f546001600160a01b03163314610feb576040516346f94cdd60e11b815260040160405180910390fd5b60045481111561101e57600480546040516308fe66d160e31b815291820183905260248201526044015b60405180910390fd5b6004546040805183815260208101929092527f2b349f43a65f38c08fc62d88fd2750e2ff318ddb2f862facc55cc4c98ac351d9910160405180910390a18060045f82825461106c9190612567565b90915550505f80546040516001600160a01b039091169083908381818185875af1925050503d805f81146110bb576040519150601f19603f3d011682016040523d82523d5f602084013e6110c0565b606091505b50509050806110e2576040516312171d8360e31b815260040160405180910390fd5b5050565b5f54604051630dc3e02960e01b81523360048201526060916001600160a01b031690630dc3e02990602401602060405180830381865afa15801561112c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611150919061226f565b61116d57604051639ed46c1f60e01b815260040160405180910390fd5b5f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e19190612254565b6040805160018082528183019092529192505f9190816020015b604080516060808201835280825260208201525f918101919091528152602001906001900390816111fb5750506040805160018082528183019092529192505f91906020808301908036833750506040805160018082528183019092529293505f9291506020808301908036833701905050905073beac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0825f81518110611296576112966124e1565b60200260200101906001600160a01b031690816001600160a01b03168152505085815f815181106112c9576112c96124e1565b6020026020010181815250506040518060600160405280838152602001828152602001306001600160a01b0316815250835f8151811061130b5761130b6124e1565b60209081029190910101526040516306ec6e8160e11b81526001600160a01b03851690630dd8dd02906113429086906004016126aa565b5f604051808303815f875af115801561135d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526113849190810190612736565b94508560065f8282546113979190612638565b90915550506040517f6001451f3352c7eb034f00744cc4a30b8a353c5b3a6115559440f7911435d876906113ce90889088906127c1565b60405180910390a150505050919050565b5f5460405163d02e02cf60e01b81523360048201526001600160a01b039091169063d02e02cf90602401602060405180830381865afa158015611424573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611448919061226f565b6114655760405163304ac09760e01b815260040160405180910390fd5b6001546040516388676cad60e01b815282151560048201526001600160a01b03909116906388676cad906024015f604051808303815f87803b1580156114a9575f80fd5b505af11580156114bb573d5f803e3d5ffd5b5050505050565b5f5460405163def8c18960e01b81523360048201526001600160a01b039091169063def8c18990602401602060405180830381865afa158015611507573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061152b919061226f565b6115485760405163c97c994760e01b815260040160405180910390fd5b5f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611598573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115bc9190612254565b604051631976849960e21b81523060048201529091505f906001600160a01b038316906365da126490602401602060405180830381865afa158015611603573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116279190612254565b6040519091506001600160a01b038216907f42176493fdfcada70cc1bcf321c9a2314e9571a9fe53c54a5385a1eeac8bc1d7905f90a26040516336a2fa1960e21b81523060048201526001600160a01b0383169063da8be864906024015f604051808303815f875af115801561169f573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526116c69190810190612736565b505050565b5f6116ea5f805160206129cb833981519152546001600160401b031690565b905090565b5f54604051630dc3e02960e01b81523360048201526001600160a01b0390911690630dc3e02990602401602060405180830381865afa158015611734573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611758919061226f565b61177557604051639ed46c1f60e01b815260040160405180910390fd5b5f8083516001600160401b0381111561179057611790611dc3565b6040519080825280602002602001820160405280156117b9578160200160208202803683370190505b5090505f84516001600160401b038111156117d6576117d6611dc3565b60405190808252806020026020018201604052801561180957816020015b60608152602001906001900390816117f45790505b5090505f5b855181101561193757600183828151811061182b5761182b6124e1565b60200260200101901515908115158152505085818151811061184f5761184f6124e1565b602002602001015160a00151516001600160401b0381111561187357611873611dc3565b60405190808252806020026020018201604052801561189c578160200160208202803683370190505b508282815181106118af576118af6124e1565b60200260200101819052505f5b8682815181106118ce576118ce6124e1565b602002602001015160c001515181101561192e578682815181106118f4576118f46124e1565b602002602001015160c001518181518110611911576119116124e1565b6020026020010151856119249190612638565b94506001016118bc565b5060010161180e565b505f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611988573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119ac9190612254565b6040516319a021cb60e11b815290915047906001600160a01b038316906333404396906119e3908a9087908b908a906004016128ce565b5f604051808303815f87803b1580156119fa575f80fd5b505af1158015611a0c573d5f803e3d5ffd5b504792505f9150611a1f90508383612567565b9050868114611a4b576040516393bdf3b360e01b81526004810182905260248101889052604401611015565b8060065f828254611a5c9190612567565b925050819055508060045f828254611a749190612638565b90915550506040517f9ee2712e473c1806731adbbcf7867f68825e48c8b4841e84470f5c3ef294972890611aab908b908a90612989565b60405180910390a1505050505050505050565b5f546001600160a01b03163314611ae8576040516346f94cdd60e11b815260040160405180910390fd5b60055460408051918252602082018390527f0993f0703b1d00b4bfb9c89b808b7d29986b127f1d0e543c318f82957ac109f8910160405180910390a18060055f828254611b359190612638565b909155505050565b5f805160206129ab833981519152805460011901611b6e57604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60015f805160206129ab83398151915255565b5f610e7182600281518110611b9e57611b9e6124e1565b602002602001015160f881901c60e882901c61ff00161760d882901c62ff0000161760c882901c63ff000000161764ff0000000060b883901c161765ff000000000060a883901c161766ff000000000000609883901c161767ff0000000000000060889290921c919091161790565b611c15611c1d565b610d60611c53565b5f805160206129cb83398151915254600160401b900460ff16610d6057604051631afcd79f60e31b815260040160405180910390fd5b611b74611c1d565b5f60408284031215611c6b575f80fd5b50919050565b5f8083601f840112611c81575f80fd5b5081356001600160401b03811115611c97575f80fd5b6020830191508360208260051b8501011115611cb1575f80fd5b9250929050565b5f805f805f805f8060a0898b031215611ccf575f80fd5b88356001600160401b038082168214611ce6575f80fd5b90985060208a01359080821115611cfb575f80fd5b611d078c838d01611c5b565b985060408b0135915080821115611d1c575f80fd5b611d288c838d01611c71565b909850965060608b0135915080821115611d40575f80fd5b611d4c8c838d01611c71565b909650945060808b0135915080821115611d64575f80fd5b50611d718b828c01611c71565b999c989b5096995094979396929594505050565b5f60208284031215611d95575f80fd5b5035919050565b6001600160a01b0381168114611db0575f80fd5b50565b8035611dbe81611d9c565b919050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715611df957611df9611dc3565b60405290565b60405160e081016001600160401b0381118282101715611df957611df9611dc3565b604051601f8201601f191681016001600160401b0381118282101715611e4957611e49611dc3565b604052919050565b5f805f60608486031215611e63575f80fd5b8335611e6e81611d9c565b92506020848101356001600160401b0380821115611e8a575f80fd5b9086019060408289031215611e9d575f80fd5b611ea5611dd7565b823582811115611eb3575f80fd5b8301601f81018a13611ec3575f80fd5b803583811115611ed557611ed5611dc3565b611ee7601f8201601f19168701611e21565b93508084528a86828401011115611efc575f80fd5b80868301878601375f908401860152509081529082013591810191909152929592945050506040919091013590565b5f60408284031215611f3b575f80fd5b611f43611dd7565b8235611f4e81611d9c565b81526020928301359281019290925250919050565b5f815180845260208085019450602084015f5b83811015611f9257815187529582019590820190600101611f76565b509495945050505050565b602081525f611faf6020830184611f63565b9392505050565b8015158114611db0575f80fd5b5f60208284031215611fd3575f80fd5b8135611faf81611fb6565b5f6001600160401b03821115611ff657611ff6611dc3565b5060051b60200190565b803563ffffffff81168114611dbe575f80fd5b5f82601f830112612022575f80fd5b8135602061203761203283611fde565b611e21565b8083825260208201915060208460051b870101935086841115612058575f80fd5b602086015b8481101561207d57803561207081611d9c565b835291830191830161205d565b509695505050505050565b5f82601f830112612097575f80fd5b813560206120a761203283611fde565b8083825260208201915060208460051b8701019350868411156120c8575f80fd5b602086015b8481101561207d57803583529183019183016120cd565b5f80604083850312156120f5575f80fd5b82356001600160401b038082111561210b575f80fd5b818501915085601f83011261211e575f80fd5b8135602061212e61203283611fde565b82815260059290921b8401810191818101908984111561214c575f80fd5b8286015b8481101561222757803586811115612166575f80fd5b870160e0818d03601f1901121561217b575f80fd5b612183611dff565b61218e868301611db3565b815261219c60408301611db3565b868201526121ac60608301611db3565b6040820152608082013560608201526121c760a08301612000565b608082015260c080830135898111156121de575f80fd5b6121ec8f8983870101612013565b60a08401525060e083013589811115612203575f80fd5b6122118f8983870101612088565b9183019190915250845250918301918301612150565b509650508601359250508082111561223d575f80fd5b5061224a85828601612088565b9150509250929050565b5f60208284031215612264575f80fd5b8151611faf81611d9c565b5f6020828403121561227f575f80fd5b8151611faf81611fb6565b5f808335601e1984360301811261229f575f80fd5b83016020810192503590506001600160401b038111156122bd575f80fd5b803603821315611cb1575f80fd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b803564ffffffffff81168114611dbe575f80fd5b5f838385526020808601955060208560051b830101845f5b8781101561235957848303601f1901895261233a828861228a565b6123458582846122cb565b9a86019a945050509083019060010161231f565b5090979650505050505050565b8183525f6001600160fb1b0383111561237d575f80fd5b8260051b80836020870137939093016020019392505050565b8183526020808401935f91600585811b8301820185855b8881101561241b57858303601f19018a52813536899003601e190181126123d2575f80fd5b880185810190356001600160401b038111156123ec575f80fd5b80861b36038213156123fc575f80fd5b612407858284612366565b9b87019b94505050908401906001016123ad565b509098975050505050505050565b6001600160401b03891681525f602060a06020840152893560a084015261245360208b018b61228a565b604060c086015261246860e0860182846122cb565b85810360408701528a81528b925060200190505f5b8a8110156124a75764ffffffffff612494846122f3565b168252918301919083019060010161247d565b5084810360608601526124bb81898b612307565b9250505082810360808401526124d2818587612396565b9b9a5050505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e1984360301811261250a575f80fd5b8301803591506001600160401b03821115612523575f80fd5b6020019150600581901b3603821315611cb1575f80fd5b5f6020828403121561254a575f80fd5b611faf826122f3565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610e7157610e71612553565b60018060a01b03841681525f60206060602084015284516040606085015280518060a08601525f5b818110156125be5782810184015186820160c0015283016125a2565b505f60c082870101526020870151608086015260c0601f19601f8301168601019350505050826040830152949350505050565b828152604081016002831061261457634e487b7160e01b5f52602160045260245ffd5b8260208301529392505050565b5f60208284031215612631575f80fd5b5051919050565b80820180821115610e7157610e71612553565b8082018281125f83128015821682158216171561266a5761266a612553565b505092915050565b5f815180845260208085019450602084015f5b83811015611f925781516001600160a01b031687529582019590820190600101612685565b5f60208083018184528085518083526040925060408601915060408160051b8701018488015f5b8381101561241b57603f198984030185528151606081518186526126f782870182612672565b915050888201518582038a87015261270f8282611f63565b928901516001600160a01b03169589019590955250948701949250908601906001016126d1565b5f6020808385031215612747575f80fd5b82516001600160401b0381111561275c575f80fd5b8301601f8101851361276c575f80fd5b805161277a61203282611fde565b81815260059190911b82018301908381019087831115612798575f80fd5b928401925b828410156127b65783518252928401929084019061279d565b979650505050505050565b828152604060208201525f6127d96040830184611f63565b949350505050565b5f82825180855260208086019550808260051b8401018186015f5b8481101561235957858303601f19018952815180516001600160a01b03908116855285820151811686860152604080830151909116908501526060808201519085015260808082015163ffffffff169085015260a08082015160e0828701819052919061286b83880182612672565b9250505060c080830151925085820381870152506128898183611f63565b9a86019a94505050908301906001016127fc565b5f815180845260208085019450602084015f5b83811015611f925781511515875295820195908201906001016128b0565b608081525f6128e060808301876127e1565b6020838203818501528187518084528284019150828160051b850101838a015f5b8381101561295d57868303601f190185528151805180855290870190878501905f5b818110156129485783516001600160a01b031683529289019291890191600101612923565b50509587019593505090850190600101612901565b50508681036040880152612971818a611f63565b94505050505082810360608401526127b6818561289d565b604081525f61299b60408301856127e1565b9050826020830152939250505056fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212209811afe105ca510b44cde85ef7d28e579075091a0662db3cba2e6d328873a7a464736f6c63430008180033
Deployed Bytecode
0x608060405260043610610164575f3560e01c80636eade7c8116100cd57806392ab89bb11610087578063bfe8172d11610062578063bfe8172d14610409578063d60909931461041d578063f55b304014610432578063f99d9e5514610451575f80fd5b806392ab89bb146103aa578063a3aae136146103be578063b3c65015146103dd575f80fd5b80636eade7c8146102f157806377b349271461030657806379cb2d2f14610325578063852aa33d1461033857806388676cad146103645780639104c31914610383575f80fd5b8063443dc4281161011e578063443dc428146102595780635b4e128c146102775780635c60da1b146102965780635d21e3da146102aa57806360aa71fb146102c95780636e947298146102dd575f80fd5b80630b10b201146101a7578063139d7fed146101d857806313bebcf9146101fb57806319259db01461020f5780631f1fb913146102245780633f65cf1914610238575f80fd5b366101a357604080513381523460208201527fbfe611b001dfcd411432f7bf0d79b82b4b2ee81511edac123a3403c357fb972a910160405180910390a1005b5f80fd5b3480156101b2575f80fd5b506101bb610464565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101e3575f80fd5b506101ed60025481565b6040519081526020016101cf565b348015610206575f80fd5b506004546101ed565b34801561021a575f80fd5b506101ed60065481565b34801561022f575f80fd5b506006546101ed565b348015610243575f80fd5b50610257610252366004611cb8565b61063f565b005b348015610264575f80fd5b505f546101bb906001600160a01b031681565b348015610282575f80fd5b50610257610291366004611d85565b6108bf565b3480156102a1575f80fd5b506101bb6109a8565b3480156102b5575f80fd5b506102576102c4366004611e51565b610a4a565b3480156102d4575f80fd5b50610257610bed565b3480156102e8575f80fd5b506101ed610d62565b3480156102fc575f80fd5b506101ed60045481565b348015610311575f80fd5b50610257610320366004611f2b565b610e77565b610257610333366004611d85565b610fc1565b348015610343575f80fd5b50610357610352366004611d85565b6110e6565b6040516101cf9190611f9d565b34801561036f575f80fd5b5061025761037e366004611fc3565b6113df565b34801561038e575f80fd5b506101bb73beac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac081565b3480156103b5575f80fd5b506102576114c2565b3480156103c9575f80fd5b506001546101bb906001600160a01b031681565b3480156103e8575f80fd5b506103f16116cb565b6040516001600160401b0390911681526020016101cf565b348015610414575f80fd5b506005546101ed565b348015610428575f80fd5b506101ed60055481565b34801561043d575f80fd5b5061025761044c3660046120e4565b6116ef565b61025761045f366004611d85565b611abe565b5f61046d611b3d565b6001546001600160a01b03161561049057506001546001600160a01b0316610626565b5f805f9054906101000a90046001600160a01b03166001600160a01b0316634665bcda6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104e0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105049190612254565b9050806001600160a01b03166384d810626040518163ffffffff1660e01b81526004016020604051808303815f875af1158015610543573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105679190612254565b5060405163a38406a360e01b81523060048201526001600160a01b0382169063a38406a390602401602060405180830381865afa1580156105aa573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105ce9190612254565b600180546001600160a01b0319166001600160a01b0392909216918217905560405130907fcdc82cfed67d9b46d3a15dd3b48745fb894a354d554cb5da5fb8c440f85c108e905f90a350506001546001600160a01b03165b61063c60015f805160206129ab83398151915255565b90565b5f5460405163d02e02cf60e01b81523360048201526001600160a01b039091169063d02e02cf90602401602060405180830381865afa158015610684573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106a8919061226f565b6106c55760405163304ac09760e01b815260040160405180910390fd5b600154604051633f65cf1960e01b81526001600160a01b0390911690633f65cf1990610703908b908b908b908b908b908b908b908b90600401612429565b5f604051808303815f87803b15801561071a575f80fd5b505af115801561072c573d5f803e3d5ffd5b505050505f5b858110156108b4575f610798848484818110610750576107506124e1565b905060200281019061076291906124f5565b808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250611b8792505050565b6001600160401b031690508787838181106107b5576107b56124e1565b90506020020160208101906107ca919061253a565b604080516001600160401b038d1681526020810184905264ffffffffff92909216917fc893ff408bc759105622c2fb34c5fa34a9d25f376cae386441e090e9d1ce1c3e910160405180910390a26801bc16d674ec80000060055f8282546108319190612567565b909155508890508783818110610849576108496124e1565b905060200201602081019061085e919061253a565b604080516001600160401b038d1681526020810184905264ffffffffff92909216917f95636f5f7adc353e75700ba4b3473fe8e0fdba943a168a06699b6e2a8c283d9a910160405180910390a250600101610732565b505050505050505050565b5f546001600160a01b031633146108e9576040516346f94cdd60e11b815260040160405180910390fd5b5f805160206129cb833981519152805460029190600160401b900460ff168061091f575080546001600160401b03808416911610155b1561093d5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b038316908117600160401b1782556005849055815460ff60401b191682556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a1505050565b5f806109d560017fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d51612567565b5f1b90505f815490505f819050806001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a1e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a429190612254565b935050505090565b5f5460405163def8c18960e01b81523360048201526001600160a01b039091169063def8c18990602401602060405180830381865afa158015610a8f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab3919061226f565b610ad05760405163c97c994760e01b815260040160405180910390fd5b5f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b449190612254565b60405163eea9064b60e01b81529091506001600160a01b0382169063eea9064b90610b779087908790879060040161257a565b5f604051808303815f87803b158015610b8e575f80fd5b505af1158015610ba0573d5f803e3d5ffd5b50505050836001600160a01b03167fa6ca69be1634c9486160d4fa9f11c9bf604a6a4b1fd23c8336ffc5889ef4b5ab83604051610bdf91815260200190565b60405180910390a250505050565b610bf5611b3d565b5f5460405163d02e02cf60e01b81523360048201526001600160a01b039091169063d02e02cf90602401602060405180830381865afa158015610c3a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c5e919061226f565b610c7b5760405163304ac09760e01b815260040160405180910390fd5b5f60045447610c8a9190612567565b9050805f03610cac5760405163e35ba1ef60e01b815260040160405180910390fd5b5f54600254604051638639285160e01b81526001600160a01b03909216916386392851918491610ce291906001906004016125f1565b5f604051808303818588803b158015610cf9575f80fd5b505af1158015610d0b573d5f803e3d5ffd5b50505050507fe759cd78e0cc36ad25dc7e3cb3e4b57e938c83e661b14e2f602717083edda6bc81604051610d4191815260200190565b60405180910390a150610d6060015f805160206129ab83398151915255565b565b5f805460408051632332de6d60e11b8152905183926001600160a01b031691634665bcda9160048083019260209291908290030181865afa158015610da9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dcd9190612254565b6040516360f4062b60e01b81523060048201529091505f906001600160a01b038316906360f4062b90602401602060405180830381865afa158015610e14573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e389190612621565b600654600554600454610e4b9190612638565b610e559190612638565b610e5f919061264b565b90505f811215610e71575f9250505090565b92915050565b80516001600160a01b038116610ea05760405163d92e233d60e01b815260040160405180910390fd5b5f805160206129cb8339815191528054600160401b810460ff1615906001600160401b03165f81158015610ed15750825b90505f826001600160401b03166001148015610eec5750303b155b905081158015610efa575080155b15610f185760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f4257845460ff60401b1916600160401b1785555b610f4a611c0d565b86515f80546001600160a01b0319166001600160a01b0390921691909117905560208701516002558315610fb857845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f546001600160a01b03163314610feb576040516346f94cdd60e11b815260040160405180910390fd5b60045481111561101e57600480546040516308fe66d160e31b815291820183905260248201526044015b60405180910390fd5b6004546040805183815260208101929092527f2b349f43a65f38c08fc62d88fd2750e2ff318ddb2f862facc55cc4c98ac351d9910160405180910390a18060045f82825461106c9190612567565b90915550505f80546040516001600160a01b039091169083908381818185875af1925050503d805f81146110bb576040519150601f19603f3d011682016040523d82523d5f602084013e6110c0565b606091505b50509050806110e2576040516312171d8360e31b815260040160405180910390fd5b5050565b5f54604051630dc3e02960e01b81523360048201526060916001600160a01b031690630dc3e02990602401602060405180830381865afa15801561112c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611150919061226f565b61116d57604051639ed46c1f60e01b815260040160405180910390fd5b5f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111bd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111e19190612254565b6040805160018082528183019092529192505f9190816020015b604080516060808201835280825260208201525f918101919091528152602001906001900390816111fb5750506040805160018082528183019092529192505f91906020808301908036833750506040805160018082528183019092529293505f9291506020808301908036833701905050905073beac0eeeeeeeeeeeeeeeeeeeeeeeeeeeeeebeac0825f81518110611296576112966124e1565b60200260200101906001600160a01b031690816001600160a01b03168152505085815f815181106112c9576112c96124e1565b6020026020010181815250506040518060600160405280838152602001828152602001306001600160a01b0316815250835f8151811061130b5761130b6124e1565b60209081029190910101526040516306ec6e8160e11b81526001600160a01b03851690630dd8dd02906113429086906004016126aa565b5f604051808303815f875af115801561135d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526113849190810190612736565b94508560065f8282546113979190612638565b90915550506040517f6001451f3352c7eb034f00744cc4a30b8a353c5b3a6115559440f7911435d876906113ce90889088906127c1565b60405180910390a150505050919050565b5f5460405163d02e02cf60e01b81523360048201526001600160a01b039091169063d02e02cf90602401602060405180830381865afa158015611424573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611448919061226f565b6114655760405163304ac09760e01b815260040160405180910390fd5b6001546040516388676cad60e01b815282151560048201526001600160a01b03909116906388676cad906024015f604051808303815f87803b1580156114a9575f80fd5b505af11580156114bb573d5f803e3d5ffd5b5050505050565b5f5460405163def8c18960e01b81523360048201526001600160a01b039091169063def8c18990602401602060405180830381865afa158015611507573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061152b919061226f565b6115485760405163c97c994760e01b815260040160405180910390fd5b5f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611598573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115bc9190612254565b604051631976849960e21b81523060048201529091505f906001600160a01b038316906365da126490602401602060405180830381865afa158015611603573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116279190612254565b6040519091506001600160a01b038216907f42176493fdfcada70cc1bcf321c9a2314e9571a9fe53c54a5385a1eeac8bc1d7905f90a26040516336a2fa1960e21b81523060048201526001600160a01b0383169063da8be864906024015f604051808303815f875af115801561169f573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526116c69190810190612736565b505050565b5f6116ea5f805160206129cb833981519152546001600160401b031690565b905090565b5f54604051630dc3e02960e01b81523360048201526001600160a01b0390911690630dc3e02990602401602060405180830381865afa158015611734573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611758919061226f565b61177557604051639ed46c1f60e01b815260040160405180910390fd5b5f8083516001600160401b0381111561179057611790611dc3565b6040519080825280602002602001820160405280156117b9578160200160208202803683370190505b5090505f84516001600160401b038111156117d6576117d6611dc3565b60405190808252806020026020018201604052801561180957816020015b60608152602001906001900390816117f45790505b5090505f5b855181101561193757600183828151811061182b5761182b6124e1565b60200260200101901515908115158152505085818151811061184f5761184f6124e1565b602002602001015160a00151516001600160401b0381111561187357611873611dc3565b60405190808252806020026020018201604052801561189c578160200160208202803683370190505b508282815181106118af576118af6124e1565b60200260200101819052505f5b8682815181106118ce576118ce6124e1565b602002602001015160c001515181101561192e578682815181106118f4576118f46124e1565b602002602001015160c001518181518110611911576119116124e1565b6020026020010151856119249190612638565b94506001016118bc565b5060010161180e565b505f805f9054906101000a90046001600160a01b03166001600160a01b031663ea4d3c9b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611988573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119ac9190612254565b6040516319a021cb60e11b815290915047906001600160a01b038316906333404396906119e3908a9087908b908a906004016128ce565b5f604051808303815f87803b1580156119fa575f80fd5b505af1158015611a0c573d5f803e3d5ffd5b504792505f9150611a1f90508383612567565b9050868114611a4b576040516393bdf3b360e01b81526004810182905260248101889052604401611015565b8060065f828254611a5c9190612567565b925050819055508060045f828254611a749190612638565b90915550506040517f9ee2712e473c1806731adbbcf7867f68825e48c8b4841e84470f5c3ef294972890611aab908b908a90612989565b60405180910390a1505050505050505050565b5f546001600160a01b03163314611ae8576040516346f94cdd60e11b815260040160405180910390fd5b60055460408051918252602082018390527f0993f0703b1d00b4bfb9c89b808b7d29986b127f1d0e543c318f82957ac109f8910160405180910390a18060055f828254611b359190612638565b909155505050565b5f805160206129ab833981519152805460011901611b6e57604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60015f805160206129ab83398151915255565b5f610e7182600281518110611b9e57611b9e6124e1565b602002602001015160f881901c60e882901c61ff00161760d882901c62ff0000161760c882901c63ff000000161764ff0000000060b883901c161765ff000000000060a883901c161766ff000000000000609883901c161767ff0000000000000060889290921c919091161790565b611c15611c1d565b610d60611c53565b5f805160206129cb83398151915254600160401b900460ff16610d6057604051631afcd79f60e31b815260040160405180910390fd5b611b74611c1d565b5f60408284031215611c6b575f80fd5b50919050565b5f8083601f840112611c81575f80fd5b5081356001600160401b03811115611c97575f80fd5b6020830191508360208260051b8501011115611cb1575f80fd5b9250929050565b5f805f805f805f8060a0898b031215611ccf575f80fd5b88356001600160401b038082168214611ce6575f80fd5b90985060208a01359080821115611cfb575f80fd5b611d078c838d01611c5b565b985060408b0135915080821115611d1c575f80fd5b611d288c838d01611c71565b909850965060608b0135915080821115611d40575f80fd5b611d4c8c838d01611c71565b909650945060808b0135915080821115611d64575f80fd5b50611d718b828c01611c71565b999c989b5096995094979396929594505050565b5f60208284031215611d95575f80fd5b5035919050565b6001600160a01b0381168114611db0575f80fd5b50565b8035611dbe81611d9c565b919050565b634e487b7160e01b5f52604160045260245ffd5b604080519081016001600160401b0381118282101715611df957611df9611dc3565b60405290565b60405160e081016001600160401b0381118282101715611df957611df9611dc3565b604051601f8201601f191681016001600160401b0381118282101715611e4957611e49611dc3565b604052919050565b5f805f60608486031215611e63575f80fd5b8335611e6e81611d9c565b92506020848101356001600160401b0380821115611e8a575f80fd5b9086019060408289031215611e9d575f80fd5b611ea5611dd7565b823582811115611eb3575f80fd5b8301601f81018a13611ec3575f80fd5b803583811115611ed557611ed5611dc3565b611ee7601f8201601f19168701611e21565b93508084528a86828401011115611efc575f80fd5b80868301878601375f908401860152509081529082013591810191909152929592945050506040919091013590565b5f60408284031215611f3b575f80fd5b611f43611dd7565b8235611f4e81611d9c565b81526020928301359281019290925250919050565b5f815180845260208085019450602084015f5b83811015611f9257815187529582019590820190600101611f76565b509495945050505050565b602081525f611faf6020830184611f63565b9392505050565b8015158114611db0575f80fd5b5f60208284031215611fd3575f80fd5b8135611faf81611fb6565b5f6001600160401b03821115611ff657611ff6611dc3565b5060051b60200190565b803563ffffffff81168114611dbe575f80fd5b5f82601f830112612022575f80fd5b8135602061203761203283611fde565b611e21565b8083825260208201915060208460051b870101935086841115612058575f80fd5b602086015b8481101561207d57803561207081611d9c565b835291830191830161205d565b509695505050505050565b5f82601f830112612097575f80fd5b813560206120a761203283611fde565b8083825260208201915060208460051b8701019350868411156120c8575f80fd5b602086015b8481101561207d57803583529183019183016120cd565b5f80604083850312156120f5575f80fd5b82356001600160401b038082111561210b575f80fd5b818501915085601f83011261211e575f80fd5b8135602061212e61203283611fde565b82815260059290921b8401810191818101908984111561214c575f80fd5b8286015b8481101561222757803586811115612166575f80fd5b870160e0818d03601f1901121561217b575f80fd5b612183611dff565b61218e868301611db3565b815261219c60408301611db3565b868201526121ac60608301611db3565b6040820152608082013560608201526121c760a08301612000565b608082015260c080830135898111156121de575f80fd5b6121ec8f8983870101612013565b60a08401525060e083013589811115612203575f80fd5b6122118f8983870101612088565b9183019190915250845250918301918301612150565b509650508601359250508082111561223d575f80fd5b5061224a85828601612088565b9150509250929050565b5f60208284031215612264575f80fd5b8151611faf81611d9c565b5f6020828403121561227f575f80fd5b8151611faf81611fb6565b5f808335601e1984360301811261229f575f80fd5b83016020810192503590506001600160401b038111156122bd575f80fd5b803603821315611cb1575f80fd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b803564ffffffffff81168114611dbe575f80fd5b5f838385526020808601955060208560051b830101845f5b8781101561235957848303601f1901895261233a828861228a565b6123458582846122cb565b9a86019a945050509083019060010161231f565b5090979650505050505050565b8183525f6001600160fb1b0383111561237d575f80fd5b8260051b80836020870137939093016020019392505050565b8183526020808401935f91600585811b8301820185855b8881101561241b57858303601f19018a52813536899003601e190181126123d2575f80fd5b880185810190356001600160401b038111156123ec575f80fd5b80861b36038213156123fc575f80fd5b612407858284612366565b9b87019b94505050908401906001016123ad565b509098975050505050505050565b6001600160401b03891681525f602060a06020840152893560a084015261245360208b018b61228a565b604060c086015261246860e0860182846122cb565b85810360408701528a81528b925060200190505f5b8a8110156124a75764ffffffffff612494846122f3565b168252918301919083019060010161247d565b5084810360608601526124bb81898b612307565b9250505082810360808401526124d2818587612396565b9b9a5050505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e1984360301811261250a575f80fd5b8301803591506001600160401b03821115612523575f80fd5b6020019150600581901b3603821315611cb1575f80fd5b5f6020828403121561254a575f80fd5b611faf826122f3565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610e7157610e71612553565b60018060a01b03841681525f60206060602084015284516040606085015280518060a08601525f5b818110156125be5782810184015186820160c0015283016125a2565b505f60c082870101526020870151608086015260c0601f19601f8301168601019350505050826040830152949350505050565b828152604081016002831061261457634e487b7160e01b5f52602160045260245ffd5b8260208301529392505050565b5f60208284031215612631575f80fd5b5051919050565b80820180821115610e7157610e71612553565b8082018281125f83128015821682158216171561266a5761266a612553565b505092915050565b5f815180845260208085019450602084015f5b83811015611f925781516001600160a01b031687529582019590820190600101612685565b5f60208083018184528085518083526040925060408601915060408160051b8701018488015f5b8381101561241b57603f198984030185528151606081518186526126f782870182612672565b915050888201518582038a87015261270f8282611f63565b928901516001600160a01b03169589019590955250948701949250908601906001016126d1565b5f6020808385031215612747575f80fd5b82516001600160401b0381111561275c575f80fd5b8301601f8101851361276c575f80fd5b805161277a61203282611fde565b81815260059190911b82018301908381019087831115612798575f80fd5b928401925b828410156127b65783518252928401929084019061279d565b979650505050505050565b828152604060208201525f6127d96040830184611f63565b949350505050565b5f82825180855260208086019550808260051b8401018186015f5b8481101561235957858303601f19018952815180516001600160a01b03908116855285820151811686860152604080830151909116908501526060808201519085015260808082015163ffffffff169085015260a08082015160e0828701819052919061286b83880182612672565b9250505060c080830151925085820381870152506128898183611f63565b9a86019a94505050908301906001016127fc565b5f815180845260208085019450602084015f5b83811015611f925781511515875295820195908201906001016128b0565b608081525f6128e060808301876127e1565b6020838203818501528187518084528284019150828160051b850101838a015f5b8381101561295d57868303601f190185528151805180855290870190878501905f5b818110156129485783516001600160a01b031683529289019291890191600101612923565b50509587019593505090850190600101612901565b50508681036040880152612971818a611f63565b94505050505082810360608401526127b6818561289d565b604081525f61299b60408301856127e1565b9050826020830152939250505056fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212209811afe105ca510b44cde85ef7d28e579075091a0662db3cba2e6d328873a7a464736f6c63430008180033
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.