Holesky Testnet

Contract

0x084066f4a4cC08679A0c92069053DA5A0C5f00f5

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xAF83e07C...2D352acA3
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
SSVClusters

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 10000 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 16 : SSVClusters.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import {ISSVClusters} from "../interfaces/ISSVClusters.sol";
import "../libraries/ClusterLib.sol";
import "../libraries/OperatorLib.sol";
import "../libraries/ProtocolLib.sol";
import "../libraries/CoreLib.sol";
import "../libraries/ValidatorLib.sol";
import {SSVStorage, StorageData} from "../libraries/SSVStorage.sol";
import {SSVStorageProtocol, StorageProtocol} from "../libraries/SSVStorageProtocol.sol";

contract SSVClusters is ISSVClusters {
    using ClusterLib for Cluster;
    using OperatorLib for Operator;
    using ProtocolLib for StorageProtocol;

    function registerValidator(
        bytes calldata publicKey,
        uint64[] memory operatorIds,
        bytes calldata sharesData,
        uint256 amount,
        Cluster memory cluster
    ) external override {
        StorageData storage s = SSVStorage.load();
        StorageProtocol storage sp = SSVStorageProtocol.load();

        ValidatorLib.validateOperatorsLength(operatorIds);

        ValidatorLib.registerPublicKey(publicKey, operatorIds, s);

        bytes32 hashedCluster = cluster.validateClusterOnRegistration(operatorIds, s);

        cluster.balance += amount;

        cluster.updateClusterOnRegistration(operatorIds, hashedCluster, 1, s, sp);

        if (amount != 0) {
            CoreLib.deposit(amount);
        }

        emit ValidatorAdded(msg.sender, operatorIds, publicKey, sharesData, cluster);
    }

    function bulkRegisterValidator(
        bytes[] memory publicKeys,
        uint64[] memory operatorIds,
        bytes[] calldata sharesData,
        uint256 amount,
        Cluster memory cluster
    ) external override {
        uint256 validatorsLength = publicKeys.length;

        if (validatorsLength == 0) revert EmptyPublicKeysList();
        if (validatorsLength != sharesData.length) revert PublicKeysSharesLengthMismatch();

        StorageData storage s = SSVStorage.load();
        StorageProtocol storage sp = SSVStorageProtocol.load();

        ValidatorLib.validateOperatorsLength(operatorIds);

        for (uint i; i < validatorsLength; ++i) {
            ValidatorLib.registerPublicKey(publicKeys[i], operatorIds, s);
        }
        bytes32 hashedCluster = cluster.validateClusterOnRegistration(operatorIds, s);

        cluster.balance += amount;

        cluster.updateClusterOnRegistration(operatorIds, hashedCluster, uint32(validatorsLength), s, sp);

        if (amount != 0) {
            CoreLib.deposit(amount);
        }

        for (uint i; i < validatorsLength; ++i) {
            bytes memory pk = publicKeys[i];
            bytes memory sh = sharesData[i];

            emit ValidatorAdded(msg.sender, operatorIds, pk, sh, cluster);
        }
    }

    function removeValidator(
        bytes calldata publicKey,
        uint64[] memory operatorIds,
        Cluster memory cluster
    ) external override {
        StorageData storage s = SSVStorage.load();

        bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
        bytes32 hashedOperatorIds = ValidatorLib.hashOperatorIds(operatorIds);

        bytes32 hashedValidator = keccak256(abi.encodePacked(publicKey, msg.sender));
        bytes32 validatorData = s.validatorPKs[hashedValidator];

        if (validatorData == bytes32(0)) {
            revert ISSVNetworkCore.ValidatorDoesNotExist();
        }

        if (!ValidatorLib.validateCorrectState(validatorData, hashedOperatorIds))
            revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKey);

        delete s.validatorPKs[hashedValidator];

        if (cluster.active) {
            StorageProtocol storage sp = SSVStorageProtocol.load();
            (uint64 clusterIndex, ) = OperatorLib.updateClusterOperators(operatorIds, false, 1, s, sp);

            cluster.updateClusterData(clusterIndex, sp.currentNetworkFeeIndex());

            sp.updateDAO(false, 1);
        }

        --cluster.validatorCount;

        s.clusters[hashedCluster] = cluster.hashClusterData();

        emit ValidatorRemoved(msg.sender, operatorIds, publicKey, cluster);
    }

    function bulkRemoveValidator(
        bytes[] calldata publicKeys,
        uint64[] memory operatorIds,
        Cluster memory cluster
    ) external override {
        uint256 validatorsLength = publicKeys.length;

        if (validatorsLength == 0) {
            revert ISSVNetworkCore.ValidatorDoesNotExist();
        }
        StorageData storage s = SSVStorage.load();

        bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
        bytes32 hashedOperatorIds = ValidatorLib.hashOperatorIds(operatorIds);

        bytes32 hashedValidator;
        bytes32 validatorData;

        uint32 validatorsRemoved;

        for (uint i; i < validatorsLength; ++i) {
            hashedValidator = keccak256(abi.encodePacked(publicKeys[i], msg.sender));
            validatorData = s.validatorPKs[hashedValidator];

            if (!ValidatorLib.validateCorrectState(validatorData, hashedOperatorIds))
                revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKeys[i]);

            delete s.validatorPKs[hashedValidator];
            validatorsRemoved++;
        }

        if (cluster.active) {
            StorageProtocol storage sp = SSVStorageProtocol.load();
            (uint64 clusterIndex, ) = OperatorLib.updateClusterOperators(operatorIds, false, validatorsRemoved, s, sp);

            cluster.updateClusterData(clusterIndex, sp.currentNetworkFeeIndex());

            sp.updateDAO(false, validatorsRemoved);
        }

        cluster.validatorCount -= validatorsRemoved;

        s.clusters[hashedCluster] = cluster.hashClusterData();

        for (uint i; i < validatorsLength; ++i) {
            emit ValidatorRemoved(msg.sender, operatorIds, publicKeys[i], cluster);
        }
    }

    function liquidate(address clusterOwner, uint64[] calldata operatorIds, Cluster memory cluster) external override {
        StorageData storage s = SSVStorage.load();

        bytes32 hashedCluster = cluster.validateHashedCluster(clusterOwner, operatorIds, s);
        cluster.validateClusterIsNotLiquidated();

        StorageProtocol storage sp = SSVStorageProtocol.load();

        (uint64 clusterIndex, uint64 burnRate) = OperatorLib.updateClusterOperators(
            operatorIds,
            false,
            cluster.validatorCount,
            s,
            sp
        );

        cluster.updateBalance(clusterIndex, sp.currentNetworkFeeIndex());

        uint256 balanceLiquidatable;

        if (
            clusterOwner != msg.sender &&
            !cluster.isLiquidatable(
                burnRate,
                sp.networkFee,
                sp.minimumBlocksBeforeLiquidation,
                sp.minimumLiquidationCollateral
            )
        ) {
            revert ClusterNotLiquidatable();
        }

        sp.updateDAO(false, cluster.validatorCount);

        if (cluster.balance != 0) {
            balanceLiquidatable = cluster.balance;
            cluster.balance = 0;
        }
        cluster.index = 0;
        cluster.networkFeeIndex = 0;
        cluster.active = false;

        s.clusters[hashedCluster] = cluster.hashClusterData();

        if (balanceLiquidatable != 0) {
            CoreLib.transferBalance(msg.sender, balanceLiquidatable);
        }

        emit ClusterLiquidated(clusterOwner, operatorIds, cluster);
    }

    function reactivate(uint64[] calldata operatorIds, uint256 amount, Cluster memory cluster) external override {
        StorageData storage s = SSVStorage.load();

        bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
        if (cluster.active) revert ClusterAlreadyEnabled();

        StorageProtocol storage sp = SSVStorageProtocol.load();

        (uint64 clusterIndex, uint64 burnRate) = OperatorLib.updateClusterOperators(
            operatorIds,
            true,
            cluster.validatorCount,
            s,
            sp
        );

        cluster.balance += amount;
        cluster.active = true;
        cluster.index = clusterIndex;
        cluster.networkFeeIndex = sp.currentNetworkFeeIndex();

        sp.updateDAO(true, cluster.validatorCount);

        if (
            cluster.isLiquidatable(
                burnRate,
                sp.networkFee,
                sp.minimumBlocksBeforeLiquidation,
                sp.minimumLiquidationCollateral
            )
        ) {
            revert InsufficientBalance();
        }

        s.clusters[hashedCluster] = cluster.hashClusterData();

        if (amount > 0) {
            CoreLib.deposit(amount);
        }

        emit ClusterReactivated(msg.sender, operatorIds, cluster);
    }

    function deposit(
        address clusterOwner,
        uint64[] calldata operatorIds,
        uint256 amount,
        Cluster memory cluster
    ) external override {
        StorageData storage s = SSVStorage.load();

        bytes32 hashedCluster = cluster.validateHashedCluster(clusterOwner, operatorIds, s);

        cluster.balance += amount;

        s.clusters[hashedCluster] = cluster.hashClusterData();

        CoreLib.deposit(amount);

        emit ClusterDeposited(clusterOwner, operatorIds, amount, cluster);
    }

    function withdraw(uint64[] calldata operatorIds, uint256 amount, Cluster memory cluster) external override {
        StorageData storage s = SSVStorage.load();

        bytes32 hashedCluster = cluster.validateHashedCluster(msg.sender, operatorIds, s);
        cluster.validateClusterIsNotLiquidated();

        StorageProtocol storage sp = SSVStorageProtocol.load();

        uint64 burnRate;
        if (cluster.active) {
            uint64 clusterIndex;
            {
                uint256 operatorsLength = operatorIds.length;
                for (uint256 i; i < operatorsLength; ++i) {
                    Operator storage operator = SSVStorage.load().operators[operatorIds[i]];
                    clusterIndex +=
                        operator.snapshot.index +
                        (uint64(block.number) - operator.snapshot.block) *
                        operator.fee;
                    burnRate += operator.fee;
                }
            }

            cluster.updateClusterData(clusterIndex, sp.currentNetworkFeeIndex());
        }
        if (cluster.balance < amount) revert InsufficientBalance();

        cluster.balance -= amount;

        if (
            cluster.active &&
            cluster.validatorCount != 0 &&
            cluster.isLiquidatable(
                burnRate,
                sp.networkFee,
                sp.minimumBlocksBeforeLiquidation,
                sp.minimumLiquidationCollateral
            )
        ) {
            revert InsufficientBalance();
        }

        s.clusters[hashedCluster] = cluster.hashClusterData();

        CoreLib.transferBalance(msg.sender, amount);

        emit ClusterWithdrawn(msg.sender, operatorIds, amount, cluster);
    }

    function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external override {
        if (
            !ValidatorLib.validateCorrectState(
                SSVStorage.load().validatorPKs[keccak256(abi.encodePacked(publicKey, msg.sender))],
                ValidatorLib.hashOperatorIds(operatorIds)
            )
        ) revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKey);

        emit ValidatorExited(msg.sender, operatorIds, publicKey);
    }

    function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external override {
        if (publicKeys.length == 0) {
            revert ISSVNetworkCore.ValidatorDoesNotExist();
        }
        bytes32 hashedOperatorIds = ValidatorLib.hashOperatorIds(operatorIds);

        for (uint i; i < publicKeys.length; ++i) {
            if (
                !ValidatorLib.validateCorrectState(
                    SSVStorage.load().validatorPKs[keccak256(abi.encodePacked(publicKeys[i], msg.sender))],
                    hashedOperatorIds
                )
            ) revert ISSVNetworkCore.IncorrectValidatorStateWithData(publicKeys[i]);

            emit ValidatorExited(msg.sender, operatorIds, publicKeys[i]);
        }
    }
}

File 2 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 3 of 16 : Counters.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 4 of 16 : ERC165Checker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId);

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

File 5 of 16 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 6 of 16 : ISSVWhitelistingContract.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

interface ISSVWhitelistingContract {
    /// @notice Checks if the caller is whitelisted
    /// @param account The account that is being checked for whitelisting
    /// @param operatorId The SSV Operator Id which is being checked
    function isWhitelisted(address account, uint256 operatorId) external view returns (bool);
}

File 7 of 16 : ISSVClusters.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import {ISSVNetworkCore} from "./ISSVNetworkCore.sol";

interface ISSVClusters is ISSVNetworkCore {
    /// @notice Registers a new validator on the SSV Network
    /// @param publicKey The public key of the new validator
    /// @param operatorIds Array of IDs of operators managing this validator
    /// @param sharesData Encrypted shares related to the new validator
    /// @param amount Amount of SSV tokens to be deposited
    /// @param cluster Cluster to be used with the new validator
    function registerValidator(
        bytes calldata publicKey,
        uint64[] memory operatorIds,
        bytes calldata sharesData,
        uint256 amount,
        Cluster memory cluster
    ) external;

    /// @notice Registers new validators on the SSV Network
    /// @param publicKeys The public keys of the new validators
    /// @param operatorIds Array of IDs of operators managing this validator
    /// @param sharesData Encrypted shares related to the new validators
    /// @param amount Amount of SSV tokens to be deposited
    /// @param cluster Cluster to be used with the new validator
    function bulkRegisterValidator(
        bytes[] calldata publicKeys,
        uint64[] memory operatorIds,
        bytes[] calldata sharesData,
        uint256 amount,
        Cluster memory cluster
    ) external;

    /// @notice Removes an existing validator from the SSV Network
    /// @param publicKey The public key of the validator to be removed
    /// @param operatorIds Array of IDs of operators managing the validator
    /// @param cluster Cluster associated with the validator
    function removeValidator(bytes calldata publicKey, uint64[] memory operatorIds, Cluster memory cluster) external;

    /// @notice Bulk removes a set of existing validators in the same cluster from the SSV Network
    /// @notice Reverts if publicKeys contains duplicates or non-existent validators
    /// @param publicKeys The public keys of the validators to be removed
    /// @param operatorIds Array of IDs of operators managing the validator
    /// @param cluster Cluster associated with the validator
    function bulkRemoveValidator(
        bytes[] calldata publicKeys,
        uint64[] memory operatorIds,
        Cluster memory cluster
    ) external;

    /**************************/
    /* Cluster External Functions */
    /**************************/

    /// @notice Liquidates a cluster
    /// @param owner The owner of the cluster
    /// @param operatorIds Array of IDs of operators managing the cluster
    /// @param cluster Cluster to be liquidated
    function liquidate(address owner, uint64[] memory operatorIds, Cluster memory cluster) external;

    /// @notice Reactivates a cluster
    /// @param operatorIds Array of IDs of operators managing the cluster
    /// @param amount Amount of SSV tokens to be deposited for reactivation
    /// @param cluster Cluster to be reactivated
    function reactivate(uint64[] memory operatorIds, uint256 amount, Cluster memory cluster) external;

    /******************************/
    /* Balance External Functions */
    /******************************/

    /// @notice Deposits tokens into a cluster
    /// @param owner The owner of the cluster
    /// @param operatorIds Array of IDs of operators managing the cluster
    /// @param amount Amount of SSV tokens to be deposited
    /// @param cluster Cluster where the deposit will be made
    function deposit(address owner, uint64[] memory operatorIds, uint256 amount, Cluster memory cluster) external;

    /// @notice Withdraws tokens from a cluster
    /// @param operatorIds Array of IDs of operators managing the cluster
    /// @param tokenAmount Amount of SSV tokens to be withdrawn
    /// @param cluster Cluster where the withdrawal will be made
    function withdraw(uint64[] memory operatorIds, uint256 tokenAmount, Cluster memory cluster) external;

    /// @notice Fires the exit event for a validator
    /// @param publicKey The public key of the validator to be exited
    /// @param operatorIds Array of IDs of operators managing the validator
    function exitValidator(bytes calldata publicKey, uint64[] calldata operatorIds) external;

    /// @notice Fires the exit event for a set of validators
    /// @param publicKeys The public keys of the validators to be exited
    /// @param operatorIds Array of IDs of operators managing the validators
    function bulkExitValidator(bytes[] calldata publicKeys, uint64[] calldata operatorIds) external;

    /**
     * @dev Emitted when the validator has been added.
     * @param publicKey The public key of a validator.
     * @param operatorIds The operator ids list.
     * @param shares snappy compressed shares(a set of encrypted and public shares).
     * @param cluster All the cluster data.
     */
    event ValidatorAdded(address indexed owner, uint64[] operatorIds, bytes publicKey, bytes shares, Cluster cluster);

    /**
     * @dev Emitted when the validator is removed.
     * @param publicKey The public key of a validator.
     * @param operatorIds The operator ids list.
     * @param cluster All the cluster data.
     */
    event ValidatorRemoved(address indexed owner, uint64[] operatorIds, bytes publicKey, Cluster cluster);

    /**
     * @dev Emitted when a cluster is liquidated.
     * @param owner The owner of the liquidated cluster.
     * @param operatorIds The operator IDs managing the cluster.
     * @param cluster The liquidated cluster data.
     */
    event ClusterLiquidated(address indexed owner, uint64[] operatorIds, Cluster cluster);

    /**
     * @dev Emitted when a cluster is reactivated.
     * @param owner The owner of the reactivated cluster.
     * @param operatorIds The operator IDs managing the cluster.
     * @param cluster The reactivated cluster data.
     */
    event ClusterReactivated(address indexed owner, uint64[] operatorIds, Cluster cluster);

    /**
     * @dev Emitted when tokens are withdrawn from a cluster.
     * @param owner The owner of the cluster.
     * @param operatorIds The operator IDs managing the cluster.
     * @param value The amount of tokens withdrawn.
     * @param cluster The cluster from which tokens were withdrawn.
     */
    event ClusterWithdrawn(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster);

    /**
     * @dev Emitted when tokens are deposited into a cluster.
     * @param owner The owner of the cluster.
     * @param operatorIds The operator IDs managing the cluster.
     * @param value The amount of SSV tokens deposited.
     * @param cluster The cluster into which SSV tokens were deposited.
     */
    event ClusterDeposited(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster);

    /**
     * @dev Emitted when a validator begins the exit process.
     * @param owner The owner of the exiting validator.
     * @param operatorIds The operator IDs managing the validator.
     * @param publicKey The public key of the exiting validator.
     */
    event ValidatorExited(address indexed owner, uint64[] operatorIds, bytes publicKey);
}

File 8 of 16 : ISSVNetworkCore.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

interface ISSVNetworkCore {
    /***********/
    /* Structs */
    /***********/

    /// @notice Represents a snapshot of an operator's or a DAO's state at a certain block
    struct Snapshot {
        /// @dev The block number when the snapshot was taken
        uint32 block;
        /// @dev The last index calculated by the formula index += (currentBlock - block) * fee
        uint64 index;
        /// @dev Total accumulated earnings calculated by the formula accumulated + lastIndex * validatorCount
        uint64 balance;
    }

    /// @notice Represents an SSV operator
    struct Operator {
        /// @dev The number of validators associated with this operator
        uint32 validatorCount;
        /// @dev The fee charged by the operator, set to zero for private operators and cannot be increased once set
        uint64 fee;
        /// @dev The address of the operator's owner
        address owner;
        /// @dev private flag for this operator
        bool whitelisted;
        /// @dev The state snapshot of the operator
        Snapshot snapshot;
    }

    /// @notice Represents a request to change an operator's fee
    struct OperatorFeeChangeRequest {
        /// @dev The new fee proposed by the operator
        uint64 fee;
        /// @dev The time when the approval period for the fee change begins
        uint64 approvalBeginTime;
        /// @dev The time when the approval period for the fee change ends
        uint64 approvalEndTime;
    }

    /// @notice Represents a cluster of validators
    struct Cluster {
        /// @dev The number of validators in the cluster
        uint32 validatorCount;
        /// @dev The index of network fees related to this cluster
        uint64 networkFeeIndex;
        /// @dev The last index calculated for the cluster
        uint64 index;
        /// @dev Flag indicating whether the cluster is active
        bool active;
        /// @dev The balance of the cluster
        uint256 balance;
    }

    /**********/
    /* Errors */
    /**********/

    error CallerNotOwnerWithData(address caller, address owner); // 0x163678e9
    error CallerNotWhitelistedWithData(uint64 operatorId); // 0xb7f529fe
    error FeeTooLow(); // 0x732f9413
    error FeeExceedsIncreaseLimit(); // 0x958065d9
    error NoFeeDeclared(); // 0x1d226c30
    error ApprovalNotWithinTimeframe(); // 0x97e4b518
    error OperatorDoesNotExist(); // 0x961e3e8c
    error InsufficientBalance(); // 0xf4d678b8
    error ValidatorDoesNotExist(); // 0xe51315d2
    error ClusterNotLiquidatable(); // 0x60300a8d
    error InvalidPublicKeyLength(); // 0x637297a4
    error InvalidOperatorIdsLength(); // 0x38186224
    error ClusterAlreadyEnabled(); // 0x3babafd2
    error ClusterIsLiquidated(); // 0x95a0cf33
    error ClusterDoesNotExists(); // 0x185e2b16
    error IncorrectClusterState(); // 0x12e04c87
    error UnsortedOperatorsList(); // 0xdd020e25
    error NewBlockPeriodIsBelowMinimum(); // 0x6e6c9cac
    error ExceedValidatorLimitWithData(uint64 operatorId); // 0x8ddf7de4
    error TokenTransferFailed(); // 0x045c4b02
    error SameFeeChangeNotAllowed(); // 0xc81272f8
    error FeeIncreaseNotAllowed(); // 0x410a2b6c
    error NotAuthorized(); // 0xea8e4eb5
    error OperatorsListNotUnique(); // 0xa5a1ff5d
    error OperatorAlreadyExists(); // 0x289c9494
    error TargetModuleDoesNotExistWithData(uint8 moduleId); // 0x208bb85d
    error MaxValueExceeded(); // 0x91aa3017
    error FeeTooHigh(); // 0xcd4e6167
    error PublicKeysSharesLengthMismatch(); // 0x9ad467b8
    error IncorrectValidatorStateWithData(bytes publicKey); // 0x89307938
    error ValidatorAlreadyExistsWithData(bytes publicKey); // 0x388e7999
    error EmptyPublicKeysList(); // df83e679
    error InvalidContractAddress(); // 0xa710429d
    error AddressIsWhitelistingContract(address contractAddress); // 0x71cadba7
    error InvalidWhitelistingContract(address contractAddress); // 0x886e6a03
    error InvalidWhitelistAddressesLength(); // 0xcbb362dc
    error ZeroAddressNotAllowed(); // 0x8579befe

    // legacy errors
    error ValidatorAlreadyExists(); // 0x8d09a73e
    error IncorrectValidatorState(); // 0x2feda3c1
    error ExceedValidatorLimit(uint64 operatorId); // 0x6df5ab76
    error CallerNotOwner(); // 0x5cd83192
    error TargetModuleDoesNotExist(); // 0x8f9195fb
    error CallerNotWhitelisted(); // 0x8c6e5d71
}

File 9 of 16 : ClusterLib.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "../interfaces/ISSVNetworkCore.sol";
import {StorageData} from "./SSVStorage.sol";
import {StorageProtocol} from "./SSVStorageProtocol.sol";
import "./OperatorLib.sol";
import "./ProtocolLib.sol";
import {Types64} from "./Types.sol";

library ClusterLib {
    using Types64 for uint64;
    using ProtocolLib for StorageProtocol;

    function updateBalance(
        ISSVNetworkCore.Cluster memory cluster,
        uint64 newIndex,
        uint64 currentNetworkFeeIndex
    ) internal pure {
        uint64 networkFee = uint64(currentNetworkFeeIndex - cluster.networkFeeIndex) * cluster.validatorCount;
        uint64 usage = (newIndex - cluster.index) * cluster.validatorCount + networkFee;
        cluster.balance = usage.expand() > cluster.balance ? 0 : cluster.balance - usage.expand();
    }

    function isLiquidatable(
        ISSVNetworkCore.Cluster memory cluster,
        uint64 burnRate,
        uint64 networkFee,
        uint64 minimumBlocksBeforeLiquidation,
        uint64 minimumLiquidationCollateral
    ) internal pure returns (bool liquidatable) {
        if (cluster.validatorCount != 0) {
            if (cluster.balance < minimumLiquidationCollateral.expand()) return true;
            uint64 liquidationThreshold = minimumBlocksBeforeLiquidation *
                (burnRate + networkFee) *
                cluster.validatorCount;

            return cluster.balance < liquidationThreshold.expand();
        }
    }

    function validateClusterIsNotLiquidated(ISSVNetworkCore.Cluster memory cluster) internal pure {
        if (!cluster.active) revert ISSVNetworkCore.ClusterIsLiquidated();
    }

    function validateHashedCluster(
        ISSVNetworkCore.Cluster memory cluster,
        address owner,
        uint64[] memory operatorIds,
        StorageData storage s
    ) internal view returns (bytes32 hashedCluster) {
        hashedCluster = keccak256(abi.encodePacked(owner, operatorIds));
        bytes32 hashedClusterData = hashClusterData(cluster);

        bytes32 clusterData = s.clusters[hashedCluster];
        if (clusterData == bytes32(0)) {
            revert ISSVNetworkCore.ClusterDoesNotExists();
        } else if (clusterData != hashedClusterData) {
            revert ISSVNetworkCore.IncorrectClusterState();
        }
    }

    function updateClusterData(
        ISSVNetworkCore.Cluster memory cluster,
        uint64 clusterIndex,
        uint64 currentNetworkFeeIndex
    ) internal pure {
        updateBalance(cluster, clusterIndex, currentNetworkFeeIndex);
        cluster.index = clusterIndex;
        cluster.networkFeeIndex = currentNetworkFeeIndex;
    }

    function hashClusterData(ISSVNetworkCore.Cluster memory cluster) internal pure returns (bytes32) {
        return
            keccak256(
                abi.encodePacked(
                    cluster.validatorCount,
                    cluster.networkFeeIndex,
                    cluster.index,
                    cluster.balance,
                    cluster.active
                )
            );
    }

    function validateClusterOnRegistration(
        ISSVNetworkCore.Cluster memory cluster,
        uint64[] memory operatorIds,
        StorageData storage s
    ) internal view returns (bytes32 hashedCluster) {
        hashedCluster = keccak256(abi.encodePacked(msg.sender, operatorIds));

        bytes32 clusterData = s.clusters[hashedCluster];
        if (clusterData == bytes32(0)) {
            if (
                cluster.validatorCount != 0 ||
                cluster.networkFeeIndex != 0 ||
                cluster.index != 0 ||
                cluster.balance != 0 ||
                !cluster.active
            ) {
                revert ISSVNetworkCore.IncorrectClusterState();
            }
        } else if (clusterData != hashClusterData(cluster)) {
            revert ISSVNetworkCore.IncorrectClusterState();
        } else {
            validateClusterIsNotLiquidated(cluster);
        }
    }

    function updateClusterOnRegistration(
        ISSVNetworkCore.Cluster memory cluster,
        uint64[] memory operatorIds,
        bytes32 hashedCluster,
        uint32 validatorCountDelta,
        StorageData storage s,
        StorageProtocol storage sp
    ) internal {
        (uint64 clusterIndex, uint64 burnRate) = OperatorLib.updateClusterOperatorsOnRegistration(
            operatorIds,
            validatorCountDelta,
            s,
            sp
        );

        updateClusterData(cluster, clusterIndex, sp.currentNetworkFeeIndex());

        sp.updateDAO(true, validatorCountDelta);

        cluster.validatorCount += validatorCountDelta;

        if (
            isLiquidatable(
                cluster,
                burnRate,
                sp.networkFee,
                sp.minimumBlocksBeforeLiquidation,
                sp.minimumLiquidationCollateral
            )
        ) {
            revert ISSVNetworkCore.InsufficientBalance();
        }

        s.clusters[hashedCluster] = hashClusterData(cluster);
    }
}

File 10 of 16 : CoreLib.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "./SSVStorage.sol";

library CoreLib {
    event ModuleUpgraded(SSVModules indexed moduleId, address moduleAddress);

    function getVersion() internal pure returns (string memory) {
        return "v1.2.0";
    }

    function transferBalance(address to, uint256 amount) internal {
        if (!SSVStorage.load().token.transfer(to, amount)) {
            revert ISSVNetworkCore.TokenTransferFailed();
        }
    }

    function deposit(uint256 amount) internal {
        if (!SSVStorage.load().token.transferFrom(msg.sender, address(this), amount)) {
            revert ISSVNetworkCore.TokenTransferFailed();
        }
    }

    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        if (account == address(0)) {
            return false;
        }
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function setModuleContract(SSVModules moduleId, address moduleAddress) internal {
        if (!isContract(moduleAddress)) revert ISSVNetworkCore.TargetModuleDoesNotExistWithData(uint8(moduleId));

        SSVStorage.load().ssvContracts[moduleId] = moduleAddress;
        emit ModuleUpgraded(moduleId, moduleAddress);
    }
}

File 11 of 16 : OperatorLib.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "../interfaces/ISSVNetworkCore.sol";
import {ISSVWhitelistingContract} from "../interfaces/external/ISSVWhitelistingContract.sol";
import {StorageData} from "./SSVStorage.sol";
import {StorageProtocol} from "./SSVStorageProtocol.sol";
import {Types64} from "./Types.sol";

import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";

library OperatorLib {
    using Types64 for uint64;

    function updateSnapshot(ISSVNetworkCore.Operator memory operator) internal view {
        uint64 blockDiffFee = (uint32(block.number) - operator.snapshot.block) * operator.fee;

        operator.snapshot.index += blockDiffFee;
        operator.snapshot.balance += blockDiffFee * operator.validatorCount;
        operator.snapshot.block = uint32(block.number);
    }

    function updateSnapshotSt(ISSVNetworkCore.Operator storage operator) internal {
        uint64 blockDiffFee = (uint32(block.number) - operator.snapshot.block) * operator.fee;

        operator.snapshot.index += blockDiffFee;
        operator.snapshot.balance += blockDiffFee * operator.validatorCount;
        operator.snapshot.block = uint32(block.number);
    }

    function checkOwner(ISSVNetworkCore.Operator memory operator) internal view {
        if (operator.snapshot.block == 0) revert ISSVNetworkCore.OperatorDoesNotExist();
        if (operator.owner != msg.sender) revert ISSVNetworkCore.CallerNotOwnerWithData(msg.sender, operator.owner);
    }

    function updateClusterOperatorsOnRegistration(
        uint64[] memory operatorIds,
        uint32 deltaValidatorCount,
        StorageData storage s,
        StorageProtocol storage sp
    ) internal returns (uint64 cumulativeIndex, uint64 cumulativeFee) {
        uint256 operatorsLength = operatorIds.length;

        uint256 blockIndex;
        uint256 lastBlockIndex = ~uint256(0); // Use an invalid block index as the initial value
        uint256 currentWhitelistedMask;

        for (uint256 i; i < operatorsLength; ++i) {
            uint64 operatorId = operatorIds[i];

            if (i + 1 < operatorsLength) {
                if (operatorId > operatorIds[i + 1]) {
                    revert ISSVNetworkCore.UnsortedOperatorsList();
                } else if (operatorId == operatorIds[i + 1]) {
                    revert ISSVNetworkCore.OperatorsListNotUnique();
                }
            }
            ISSVNetworkCore.Operator memory operator = s.operators[operatorId];

            if (operator.snapshot.block == 0) {
                revert ISSVNetworkCore.OperatorDoesNotExist();
            }

            // check if the pending operator is whitelisted (must be backward compatible)
            if (operator.whitelisted) {
                // Handle bitmap-based whitelisting
                blockIndex = operatorId >> 8;
                if (blockIndex != lastBlockIndex) {
                    currentWhitelistedMask = s.addressWhitelistedForOperators[msg.sender][blockIndex];
                    lastBlockIndex = blockIndex;
                }

                // if msg.sender is not whitelisted via bitmap, check for legacy whitelist/whitelisting contract
                if (currentWhitelistedMask & (1 << (operatorId & 0xFF)) == 0) {
                    address whitelistedAddress = s.operatorsWhitelist[operatorId];
                    if (whitelistedAddress == address(0)) {
                        // msg.sender is not whitelisted via bitmap or legacy whitelist/whitelisting contract
                        revert ISSVNetworkCore.CallerNotWhitelistedWithData(operatorId);
                    }
                    // Legacy address & whitelisting contract check
                    if (whitelistedAddress != msg.sender) {
                        // Check if whitelistedAddress is a valid whitelisting contract and if msg.sender is whitelisted by it
                        // For non-whitelisting contracts, check if msg.sender is whitelisted (EOAs or generic contracts)
                        if (
                            !OperatorLib.isWhitelistingContract(whitelistedAddress) ||
                            !ISSVWhitelistingContract(whitelistedAddress).isWhitelisted(msg.sender, operatorId)
                        ) {
                            revert ISSVNetworkCore.CallerNotWhitelistedWithData(operatorId);
                        }
                    }
                }
            }

            updateSnapshot(operator);
            if ((operator.validatorCount += deltaValidatorCount) > sp.validatorsPerOperatorLimit) {
                revert ISSVNetworkCore.ExceedValidatorLimitWithData(operatorId);
            }

            cumulativeFee += operator.fee;
            cumulativeIndex += operator.snapshot.index;

            s.operators[operatorId] = operator;
        }
    }

    function updateClusterOperators(
        uint64[] memory operatorIds,
        bool increaseValidatorCount,
        uint32 deltaValidatorCount,
        StorageData storage s,
        StorageProtocol storage sp
    ) internal returns (uint64 cumulativeIndex, uint64 cumulativeFee) {
        uint256 operatorsLength = operatorIds.length;

        for (uint256 i; i < operatorsLength; ++i) {
            uint64 operatorId = operatorIds[i];

            ISSVNetworkCore.Operator storage operator = s.operators[operatorId];

            if (operator.snapshot.block != 0) {
                updateSnapshotSt(operator);
                if (!increaseValidatorCount) {
                    operator.validatorCount -= deltaValidatorCount;
                } else if ((operator.validatorCount += deltaValidatorCount) > sp.validatorsPerOperatorLimit) {
                    revert ISSVNetworkCore.ExceedValidatorLimitWithData(operatorId);
                }

                cumulativeFee += operator.fee;
            }
            cumulativeIndex += operator.snapshot.index;
        }
    }

    function updateMultipleWhitelists(
        address[] calldata whitelistAddresses,
        uint64[] calldata operatorIds,
        bool registerAddresses,
        StorageData storage s
    ) internal {
        uint256 addressesLength = whitelistAddresses.length;
        if (addressesLength == 0) revert ISSVNetworkCore.InvalidWhitelistAddressesLength();

        checkOperatorsLength(operatorIds);

        // create the max number of masks that will be updated
        (uint256[] memory masks, uint256 startBlockIndex) = generateBlockMasks(operatorIds, true, s);
        uint256 endBlockIndex = startBlockIndex + masks.length;

        for (uint256 i; i < addressesLength; ++i) {
            address whitelistAddress = whitelistAddresses[i];
            checkZeroAddress(whitelistAddress);

            // If whitelistAddress is a custom contract, reverts only when registering addresses
            if (registerAddresses && isWhitelistingContract(whitelistAddress))
                revert ISSVNetworkCore.AddressIsWhitelistingContract(whitelistAddress);

            for (uint256 blockIndex = startBlockIndex; blockIndex < endBlockIndex; ++blockIndex) {
                // only update storage for updated masks
                uint256 mask = masks[blockIndex - startBlockIndex];
                if (mask != 0) {
                    if (registerAddresses) {
                        s.addressWhitelistedForOperators[whitelistAddress][blockIndex] |= mask;
                    } else {
                        s.addressWhitelistedForOperators[whitelistAddress][blockIndex] &= ~mask;
                    }
                }
            }
        }
    }

    function generateBlockMasks(
        uint64[] calldata operatorIds,
        bool checkOperatorsOwnership,
        StorageData storage s
    ) internal view returns (uint256[] memory masks, uint256 startBlockIndex) {
        uint256 operatorsLength = operatorIds.length;
        startBlockIndex = operatorIds[0] >> 8;

        // Create the masks array from startBlockIndex to the last block index
        masks = new uint256[]((operatorIds[operatorsLength - 1] >> 8) - startBlockIndex + 1);

        uint64 currentOperatorId;
        uint64 prevOperatorId;

        for (uint256 i; i < operatorsLength; ++i) {
            currentOperatorId = operatorIds[i];

            if (checkOperatorsOwnership) {
                checkOwner(s.operators[currentOperatorId]);
            }

            if (i > 0 && currentOperatorId <= prevOperatorId) {
                if (currentOperatorId == prevOperatorId) {
                    revert ISSVNetworkCore.OperatorsListNotUnique();
                }
                revert ISSVNetworkCore.UnsortedOperatorsList();
            }

            (uint256 blockIndex, uint256 bitPosition) = getBitmapIndexes(currentOperatorId);

            masks[blockIndex - startBlockIndex] |= (1 << bitPosition);
            prevOperatorId = currentOperatorId;
        }
    }

    function updatePrivacyStatus(uint64[] calldata operatorIds, bool setPrivate, StorageData storage s) internal {
        uint256 operatorsLength = checkOperatorsLength(operatorIds);

        ISSVNetworkCore.Operator storage operator;
        for (uint256 i; i < operatorsLength; ++i) {
            uint64 operatorId = operatorIds[i];
            operator = s.operators[operatorId];
            checkOwner(operator);

            operator.whitelisted = setPrivate;
        }
    }

    function getBitmapIndexes(uint64 operatorId) internal pure returns (uint256 blockIndex, uint256 bitPosition) {
        blockIndex = operatorId >> 8; // Equivalent to operatorId / 256
        bitPosition = operatorId & 0xFF; // Equivalent to operatorId % 256
    }

    function checkZeroAddress(address whitelistAddress) internal pure {
        if (whitelistAddress == address(0)) revert ISSVNetworkCore.ZeroAddressNotAllowed();
    }

    function checkOperatorsLength(uint64[] calldata operatorIds) internal pure returns (uint256 operatorsLength) {
        operatorsLength = operatorIds.length;
        if (operatorsLength == 0) revert ISSVNetworkCore.InvalidOperatorIdsLength();
    }

    function isWhitelistingContract(address whitelistingContract) internal view returns (bool) {
        return ERC165Checker.supportsInterface(whitelistingContract, type(ISSVWhitelistingContract).interfaceId);
    }
}

File 12 of 16 : ProtocolLib.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "../interfaces/ISSVNetworkCore.sol";
import {Types256} from "./Types.sol";
import {StorageProtocol} from "./SSVStorageProtocol.sol";

library ProtocolLib {
    using Types256 for uint256;

    /******************************/
    /* Network internal functions */
    /******************************/
    function currentNetworkFeeIndex(StorageProtocol storage sp) internal view returns (uint64) {
        return sp.networkFeeIndex + uint64(block.number - sp.networkFeeIndexBlockNumber) * sp.networkFee;
    }

    function updateNetworkFee(StorageProtocol storage sp, uint256 fee) internal {
        updateDAOEarnings(sp);

        sp.networkFeeIndex = currentNetworkFeeIndex(sp);
        sp.networkFeeIndexBlockNumber = uint32(block.number);
        sp.networkFee = fee.shrink();
    }

    /**************************/
    /* DAO internal functions */
    /**************************/
    function updateDAOEarnings(StorageProtocol storage sp) internal {
        sp.daoBalance = networkTotalEarnings(sp);
        sp.daoIndexBlockNumber = uint32(block.number);
    }

    function networkTotalEarnings(StorageProtocol storage sp) internal view returns (uint64) {
        return sp.daoBalance + (uint64(block.number) - sp.daoIndexBlockNumber) * sp.networkFee * sp.daoValidatorCount;
    }

    function updateDAO(StorageProtocol storage sp, bool increaseValidatorCount, uint32 deltaValidatorCount) internal {
        updateDAOEarnings(sp);
        if (!increaseValidatorCount) {
            sp.daoValidatorCount -= deltaValidatorCount;
        } else if ((sp.daoValidatorCount += deltaValidatorCount) > type(uint32).max) {
            revert ISSVNetworkCore.MaxValueExceeded();
        }
    }
}

File 13 of 16 : SSVStorage.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "../interfaces/ISSVNetworkCore.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

enum SSVModules {
    SSV_OPERATORS,
    SSV_CLUSTERS,
    SSV_DAO,
    SSV_VIEWS,
    SSV_OPERATORS_WHITELIST
}

/// @title SSV Network Storage Data
/// @notice Represents all operational state required by the SSV Network
struct StorageData {
    /// @notice Maps each validator's public key to its hashed representation of: operator Ids used by the validator and active / inactive flag (uses LSB)
    mapping(bytes32 => bytes32) validatorPKs;
    /// @notice Maps each cluster's bytes32 identifier to its hashed representation of ISSVNetworkCore.Cluster
    mapping(bytes32 => bytes32) clusters;
    /// @notice Maps each operator's public key to its corresponding ID
    mapping(bytes32 => uint64) operatorsPKs;
    /// @notice Maps each SSVModules' module to its corresponding contract address
    mapping(SSVModules => address) ssvContracts;
    /// @notice Operators' whitelist: Maps each operator's ID to a whitelisting contract
    mapping(uint64 => address) operatorsWhitelist;
    /// @notice Maps each operator's ID to its corresponding operator fee change request data
    mapping(uint64 => ISSVNetworkCore.OperatorFeeChangeRequest) operatorFeeChangeRequests;
    /// @notice Maps each operator's ID to its corresponding operator data
    mapping(uint64 => ISSVNetworkCore.Operator) operators;
    /// @notice The SSV token used within the network (fees, rewards)
    IERC20 token;
    /// @notice Counter keeping track of the last Operator ID issued
    Counters.Counter lastOperatorId;
    /// @notice Operators' whitelist: Maps each whitelisted address to a list of operators
    /// @notice that are whitelisted for that address using bitmaps
    /// @dev The nested mapping's key represents a uint256 slot to handle more than 256 operators per address
    mapping(address => mapping(uint256 => uint256)) addressWhitelistedForOperators;
}

library SSVStorage {
    uint256 private constant SSV_STORAGE_POSITION = uint256(keccak256("ssv.network.storage.main")) - 1;

    function load() internal pure returns (StorageData storage sd) {
        uint256 position = SSV_STORAGE_POSITION;
        assembly {
            sd.slot := position
        }
    }
}

File 14 of 16 : SSVStorageProtocol.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

/// @title SSV Network Storage Protocol
/// @notice Represents the operational settings and parameters required by the SSV Network
struct StorageProtocol {
    /// @notice The block number when the network fee index was last updated
    uint32 networkFeeIndexBlockNumber;
    /// @notice The count of validators governed by the DAO
    uint32 daoValidatorCount;
    /// @notice The block number when the DAO index was last updated
    uint32 daoIndexBlockNumber;
    /// @notice The maximum limit of validators per operator
    uint32 validatorsPerOperatorLimit;
    /// @notice The current network fee value
    uint64 networkFee;
    /// @notice The current network fee index value
    uint64 networkFeeIndex;
    /// @notice The current balance of the DAO
    uint64 daoBalance;
    /// @notice The minimum number of blocks before a liquidation event can be triggered
    uint64 minimumBlocksBeforeLiquidation;
    /// @notice The minimum collateral required for liquidation
    uint64 minimumLiquidationCollateral;
    /// @notice The period in which an operator can declare a fee change
    uint64 declareOperatorFeePeriod;
    /// @notice The period in which an operator fee change can be executed
    uint64 executeOperatorFeePeriod;
    /// @notice The maximum increase in operator fee that is allowed (percentage)
    uint64 operatorMaxFeeIncrease;
    /// @notice The maximum value in operator fee that is allowed (SSV)
    uint64 operatorMaxFee;
}

library SSVStorageProtocol {
    uint256 private constant SSV_STORAGE_POSITION = uint256(keccak256("ssv.network.storage.protocol")) - 1;

    function load() internal pure returns (StorageProtocol storage sd) {
        uint256 position = SSV_STORAGE_POSITION;
        assembly {
            sd.slot := position
        }
    }
}

File 15 of 16 : Types.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

uint256 constant DEDUCTED_DIGITS = 10_000_000;

library Types64 {
    function expand(uint64 value) internal pure returns (uint256) {
        return value * DEDUCTED_DIGITS;
    }
}

library Types256 {
    function shrink(uint256 value) internal pure returns (uint64) {
        require(value < (2 ** 64 * DEDUCTED_DIGITS), "Max value exceeded");
        return uint64(shrinkable(value) / DEDUCTED_DIGITS);
    }

    function shrinkable(uint256 value) internal pure returns (uint256) {
        require(value % DEDUCTED_DIGITS == 0, "Max precision exceeded");
        return value;
    }
}

File 16 of 16 : ValidatorLib.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

import "../interfaces/ISSVNetworkCore.sol";
import {StorageData} from "./SSVStorage.sol";

library ValidatorLib {
    uint64 private constant MIN_OPERATORS_LENGTH = 4;
    uint64 private constant MAX_OPERATORS_LENGTH = 13;
    uint64 private constant MODULO_OPERATORS_LENGTH = 3;
    uint64 private constant PUBLIC_KEY_LENGTH = 48;

    function validateOperatorsLength(uint64[] memory operatorIds) internal pure {
        uint256 operatorsLength = operatorIds.length;

        if (
            operatorsLength < MIN_OPERATORS_LENGTH ||
            operatorsLength > MAX_OPERATORS_LENGTH ||
            operatorsLength % MODULO_OPERATORS_LENGTH != 1
        ) {
            revert ISSVNetworkCore.InvalidOperatorIdsLength();
        }
    }

    function registerPublicKey(bytes memory publicKey, uint64[] memory operatorIds, StorageData storage s) internal {
        if (publicKey.length != PUBLIC_KEY_LENGTH) {
            revert ISSVNetworkCore.InvalidPublicKeyLength();
        }

        bytes32 hashedPk = keccak256(abi.encodePacked(publicKey, msg.sender));

        if (s.validatorPKs[hashedPk] != bytes32(0)) {
            revert ISSVNetworkCore.ValidatorAlreadyExistsWithData(publicKey);
        }

        s.validatorPKs[hashedPk] = bytes32(uint256(keccak256(abi.encodePacked(operatorIds))) | uint256(0x01)); // set LSB to 1
    }

    function hashOperatorIds(uint64[] memory operatorIds) internal pure returns (bytes32) {
        bytes32 mask = ~bytes32(uint256(1)); // All bits set to 1 except LSB
        return keccak256(abi.encodePacked(operatorIds)) & mask; // Clear LSB of provided operator ids
    }

    function validateCorrectState(bytes32 validatorData, bytes32 hashedOperatorIds) internal pure returns (bool) {
        // All bits set to 1 except LSB
        // Clear LSB of stored validator data and compare
        return (validatorData & ~bytes32(uint256(1))) == hashedOperatorIds;
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "evmVersion": "cancun",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"AddressIsWhitelistingContract","type":"error"},{"inputs":[],"name":"ApprovalNotWithinTimeframe","type":"error"},{"inputs":[],"name":"CallerNotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"CallerNotOwnerWithData","type":"error"},{"inputs":[],"name":"CallerNotWhitelisted","type":"error"},{"inputs":[{"internalType":"uint64","name":"operatorId","type":"uint64"}],"name":"CallerNotWhitelistedWithData","type":"error"},{"inputs":[],"name":"ClusterAlreadyEnabled","type":"error"},{"inputs":[],"name":"ClusterDoesNotExists","type":"error"},{"inputs":[],"name":"ClusterIsLiquidated","type":"error"},{"inputs":[],"name":"ClusterNotLiquidatable","type":"error"},{"inputs":[],"name":"EmptyPublicKeysList","type":"error"},{"inputs":[{"internalType":"uint64","name":"operatorId","type":"uint64"}],"name":"ExceedValidatorLimit","type":"error"},{"inputs":[{"internalType":"uint64","name":"operatorId","type":"uint64"}],"name":"ExceedValidatorLimitWithData","type":"error"},{"inputs":[],"name":"FeeExceedsIncreaseLimit","type":"error"},{"inputs":[],"name":"FeeIncreaseNotAllowed","type":"error"},{"inputs":[],"name":"FeeTooHigh","type":"error"},{"inputs":[],"name":"FeeTooLow","type":"error"},{"inputs":[],"name":"IncorrectClusterState","type":"error"},{"inputs":[],"name":"IncorrectValidatorState","type":"error"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"IncorrectValidatorStateWithData","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidContractAddress","type":"error"},{"inputs":[],"name":"InvalidOperatorIdsLength","type":"error"},{"inputs":[],"name":"InvalidPublicKeyLength","type":"error"},{"inputs":[],"name":"InvalidWhitelistAddressesLength","type":"error"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"InvalidWhitelistingContract","type":"error"},{"inputs":[],"name":"MaxValueExceeded","type":"error"},{"inputs":[],"name":"NewBlockPeriodIsBelowMinimum","type":"error"},{"inputs":[],"name":"NoFeeDeclared","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"OperatorAlreadyExists","type":"error"},{"inputs":[],"name":"OperatorDoesNotExist","type":"error"},{"inputs":[],"name":"OperatorsListNotUnique","type":"error"},{"inputs":[],"name":"PublicKeysSharesLengthMismatch","type":"error"},{"inputs":[],"name":"SameFeeChangeNotAllowed","type":"error"},{"inputs":[],"name":"TargetModuleDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint8","name":"moduleId","type":"uint8"}],"name":"TargetModuleDoesNotExistWithData","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"UnsortedOperatorsList","type":"error"},{"inputs":[],"name":"ValidatorAlreadyExists","type":"error"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"ValidatorAlreadyExistsWithData","type":"error"},{"inputs":[],"name":"ValidatorDoesNotExist","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterReactivated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ClusterWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"shares","type":"bytes"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ValidatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"}],"name":"ValidatorExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"indexed":false,"internalType":"bytes","name":"publicKey","type":"bytes"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"ValidatorRemoved","type":"event"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"bulkExitValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"bytes[]","name":"sharesData","type":"bytes[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"bulkRegisterValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"bulkRemoveValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"clusterOwner","type":"address"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"exitValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"clusterOwner","type":"address"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"reactivate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"bytes","name":"sharesData","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"registerValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"removeValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106100b9575f3560e01c80635aed114211610072578063686e682c11610058578063686e682c14610144578063bc26e7e514610157578063bf0f2fb21461016a575f80fd5b80635aed11421461011e5780635fec6dd014610131575f80fd5b806322f18bf5116100a257806322f18bf5146100e557806332afd02f146100f85780633877322b1461010b575f80fd5b806306e8fb9c146100bd57806312b3fc19146100d2575b5f80fd5b6100d06100cb366004612b5c565b61017d565b005b6100d06100e0366004612c04565b610283565b6100d06100f3366004612cbb565b610459565b6100d0610106366004612e2a565b61063d565b6100d0610119366004612e91565b61081c565b6100d061012c366004612ec7565b61095a565b6100d061013f366004612efd565b610b84565b6100d0610152366004612efd565b610da7565b6100d0610165366004612f6f565b611081565b6100d0610178366004612fda565b611164565b5f610186611372565b90505f6101916113a5565b905061019c876113d2565b6101de89898080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508b92508691506114349050565b5f6101ea848985611520565b905084846080018181516101fe919061305e565b90525061021084898360018787611645565b841561021f5761021f85611732565b3373ffffffffffffffffffffffffffffffffffffffff167f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5898c8c8b8b8a60405161026f969594939291906130de565b60405180910390a250505050505050505050565b5f61028c611372565b90505f61029b83338685611812565b90505f6102a7856118d5565b90505f8787336040516020016102bf9392919061316e565b60408051601f1981840301815291815281516020928301205f818152928790529120549091508061031c576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe811683146103845788886040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b60405180910390fd5b5f828152602086905260408120556060860151156103dd575f6103a56113a5565b90505f6103b6895f60018a8661192b565b5090506103ce816103c684611ac6565b8a9190611b35565b6103da825f6001611b5a565b50505b855186906103ea906131c2565b63ffffffff1690526103fb86611c31565b5f85815260018701602052604090819020919091555133907fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e90610446908a908d908d908c906131ff565b60405180910390a2505050505050505050565b85515f819003610495576040517fdf83e67900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8084146104ce576040517f9ad467b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6104d7611372565b90505f6104e26113a5565b90506104ed886113d2565b5f5b838110156105235761051b8a828151811061050c5761050c613277565b60200260200101518a85611434565b6001016104ef565b505f610530858a85611520565b90508585608001818151610544919061305e565b905250610555858a83878787611645565b85156105645761056486611732565b5f5b84811015610630575f8b828151811061058157610581613277565b602002602001015190505f8a8a8481811061059e5761059e613277565b90506020028101906105b091906132a4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152505060405192935033927f48a3ea0796746043948f6341d17ff8200937b99262a0b48c2663b951ed7114e5925061061e91508f90869086908e90613352565b60405180910390a25050600101610566565b5050505050505050505050565b5f839003610677576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6106b38383808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b90505f5b84811015610814576107506106ca611372565b5f8888858181106106dd576106dd613277565b90506020028101906106ef91906132a4565b336040516020016107029392919061316e565b6040516020818303038152906040528051906020012081526020019081526020015f2054837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6107ac5785858281811061076657610766613277565b905060200281019061077891906132a4565b6040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b337fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c85858989868181106107e2576107e2613277565b90506020028101906107f491906132a4565b604051610804949392919061341b565b60405180910390a26001016106b7565b505050505050565b6108c6610827611372565b6040515f9061083e9088908890339060200161316e565b6040516020818303038152906040528051906020012081526020019081526020015f205461089d8484808060200260200160405190810160405280939291908181526020018383602002808284375f920191909152506118d592505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe919091161490565b6109005783836040517f8930793800000000000000000000000000000000000000000000000000000000815260040161037b9291906131a7565b3373ffffffffffffffffffffffffffffffffffffffff167fb4b20ffb2eb1f020be3df600b2287914f50c07003526d3a9d89a9dd12351828c8383878760405161094c949392919061341b565b60405180910390a250505050565b825f819003610995576040517fe51315d200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099e611372565b90505f6109ad84338785611812565b90505f6109b9866118d5565b90505f805f805b87811015610a85578b8b828181106109da576109da613277565b90506020028101906109ec91906132a4565b336040516020016109ff9392919061316e565b60408051808303601f1901815291815281516020928301205f818152928a905291205490945092507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe83168514610a62578b8b8281811061076657610766613277565b5f8481526020889052604081205581610a7a81613441565b9250506001016109c0565b50876060015115610acf575f610a996113a5565b90505f610aa98b5f858b8661192b565b509050610ac181610ab984611ac6565b8c9190611b35565b610acc825f85611b5a565b50505b80885f01818151610ae09190613463565b63ffffffff16905250610af288611c31565b5f8681526001880160205260408120919091555b87811015610b7657337fccf4370403e5fbbde0cd3f13426479dcd8a5916b05db424b7a2c04978cf8ce6e8b8e8e85818110610b4357610b43613277565b9050602002810190610b5591906132a4565b8d604051610b6694939291906131ff565b60405180910390a2600101610b06565b505050505050505050505050565b5f610b8d611372565b90505f610bd1338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050826060015115610c0f576040517f3babafd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610c186113a5565b90505f80610c5e8989808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250508951600192509050888761192b565b915091508686608001818151610c74919061305e565b9052506001606087015267ffffffffffffffff82166040870152610c9783611ac6565b67ffffffffffffffff1660208701528551610cb6908490600190611b5a565b82546001840154610cfe918891849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15610d35576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d3e86611c31565b5f8581526001870160205260409020558615610d5d57610d5d87611732565b3373ffffffffffffffffffffffffffffffffffffffff167fc803f8c01343fcdaf32068f4c283951623ef2b3fa0c547551931356f456b68598a8a8960405161044693929190613487565b5f610db0611372565b90505f610df4338787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b9050610dff83611d71565b5f610e086113a5565b90505f846060015115610f10575f87815b81811015610ef7575f610e2a611372565b6006015f8d8d85818110610e4057610e40613277565b9050602002016020810190610e5591906134ec565b67ffffffffffffffff908116825260208201929092526040015f2080546002820154919350640100000000900490911690610e969063ffffffff1643613505565b610ea09190613526565b6002820154610ec19190640100000000900467ffffffffffffffff16613552565b610ecb9085613552565b8154909450610eec90640100000000900467ffffffffffffffff1686613552565b945050600101610e19565b5050610f0e81610f0685611ac6565b889190611b35565b505b8585608001511015610f4e576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8585608001818151610f609190613573565b90525060608501518015610f7a5750845163ffffffff1615155b8015610fc9575081546001830154610fc9918791849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b15611000576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61100985611c31565b5f8481526001860160205260409020556110233387611dac565b3373ffffffffffffffffffffffffffffffffffffffff167f39d1320bbda24947e77f3560661323384aa0a1cb9d5e040e617e5cbf50b6dbe08989898960405161106f9493929190613586565b60405180910390a25050505050505050565b5f61108a611372565b90505f6110ce878787808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b905083836080018181516110e2919061305e565b9052506110ee83611c31565b5f82815260018401602052604090205561110784611732565b8673ffffffffffffffffffffffffffffffffffffffff167f2bac1912f2481d12f0df08647c06bee174967c62d3a03cbc078eb215dc1bd9a2878787876040516111539493929190613586565b60405180910390a250505050505050565b5f61116d611372565b90505f6111b1868686808060200260200160405190810160405280939291908181526020018383602002808284375f92019190915250889493925087915050611812565b90506111bc83611d71565b5f6111c56113a5565b90505f8061120a8888808060200260200160405190810160405280939291908181526020018383602002808284375f9201829052508b5190935091508990508761192b565b915091506112238261121b85611ac6565b889190611e85565b5f73ffffffffffffffffffffffffffffffffffffffff8a16331480159061128f57508354600185015461128d918991859167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b155b156112c6576040517f60300a8d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86516112d59085905f90611b5a565b6080870151156112eb57506080860180515f9091525b5f6040880181905260208801819052606088015261130887611c31565b5f8681526001880160205260409020558015611328576113283382611dac565b8973ffffffffffffffffffffffffffffffffffffffff167f1fce24c373e07f89214e9187598635036111dbb363e99f4ce498488cdc66e6888a8a8a60405161026f93929190613487565b5f8061139f60017fd56c4f4aab8ca22f9fde432777379f436593c6027698a6995e2daea890bed105613573565b92915050565b5f8061139f60017f0f1d85405047bdb6b0a60e27531f52a1f7a948613527b9b83a7552558207aa16613573565b805160048110806113e35750600d81115b806113f957506113f46003826135f1565b600114155b15611430576040517f3818622400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b825160301461146f576040517f637297a400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8333604051602001611483929190613629565b60408051601f1981840301815291815281516020928301205f81815292859052912054909150156114e257836040517f388e799900000000000000000000000000000000000000000000000000000000815260040161037b9190613672565b6001836040516020016114f591906136b2565b60408051601f1981840301815291815281516020928301205f94855294909152909120911790555050565b5f33836040516020016115349291906136bd565b60408051601f1981840301815291815281516020928301205f81815260018601909352912054909150806115f357845163ffffffff161515806115845750602085015167ffffffffffffffff1615155b8061159c5750604085015167ffffffffffffffff1615155b806115aa5750608085015115155b806115b757508460600151155b156115ee576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d565b6115fc85611c31565b8114611634576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61163d85611d71565b509392505050565b5f8061165387868686611f32565b9150915061166a888361166586611ac6565b611b35565b61167683600187611b5a565b84885f0181815161168791906136f3565b63ffffffff16905250825460018401546116d8918a91849167ffffffffffffffff70010000000000000000000000000000000091829004811692680100000000000000008104821692900416611cec565b1561170f576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61171888611c31565b5f9687526001909401602052505060409093205550505050565b61173a611372565b600701546040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff909116906323b872dd906064016020604051808303815f875af11580156117b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117d99190613710565b61180f576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f83836040516020016118269291906136bd565b6040516020818303038152906040528051906020012090505f61184886611c31565b5f83815260018501602052604090205490915080611892576040517f185e2b1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8181146118cb576040517f12e04c8700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b6040515f907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe90819061190c9085906020016136b2565b6040516020818303038152906040528051906020012016915050919050565b84515f908190815b81811015611aba575f89828151811061194e5761194e613277565b60209081029190910181015167ffffffffffffffff81165f90815260068a01909252604090912060028101549192509063ffffffff1615611a8a5761199281612527565b896119d3578054899082905f906119b090849063ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550611a69565b8654815463ffffffff6c010000000000000000000000009092048216918b9184915f91611a02918591166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611a69576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b8054611a8790640100000000900467ffffffffffffffff1686613552565b94505b6002810154611aab90640100000000900467ffffffffffffffff1687613552565b95505050806001019050611933565b50509550959350505050565b80545f9067ffffffffffffffff70010000000000000000000000000000000082041690611af99063ffffffff1643613573565b611b039190613526565b825461139f91907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16613552565b611b40838383611e85565b67ffffffffffffffff918216604084015216602090910152565b611b638361264b565b81611bad57825481908490600490611b8a908490640100000000900463ffffffff16613463565b92506101000a81548163ffffffff021916908363ffffffff160217905550505050565b825463ffffffff9082908590600490611bd1908490640100000000900486166136f3565b92506101000a81548163ffffffff021916908363ffffffff160217905563ffffffff161115611c2c576040517f91aa301700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b80516020808301516040808501516080860151606087015192515f96611ccf96909594910160e09590951b7fffffffff0000000000000000000000000000000000000000000000000000000016855260c093841b7fffffffffffffffff00000000000000000000000000000000000000000000000090811660048701529290931b909116600c8401526014830152151560f81b603482015260350190565b604051602081830303815290604052805190602001209050919050565b84515f9063ffffffff1615611d6857611d0e8267ffffffffffffffff166126ca565b86608001511015611d2157506001611d68565b85515f9063ffffffff16611d358688613552565b611d3f9086613526565b611d499190613526565b9050611d5e8167ffffffffffffffff166126ca565b8760800151109150505b95945050505050565b806060015161180f576040517f95a0cf3300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611db4611372565b600701546040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490529091169063a9059cbb906044016020604051808303815f875af1158015611e2b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e4f9190613710565b611430576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b825160208401515f9163ffffffff1690611e9f9084613505565b611ea99190613526565b90505f81855f015163ffffffff16866040015186611ec79190613505565b611ed19190613526565b611edb9190613552565b90508460800151611ef58267ffffffffffffffff166126ca565b11611f2157611f0d8167ffffffffffffffff166126ca565b8560800151611f1c9190613573565b611f23565b5f5b60809095019490945250505050565b83515f908190817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81805b84811015612519575f8b8281518110611f7857611f78613277565b6020026020010151905085826001611f90919061305e565b1015612077578b611fa283600161305e565b81518110611fb257611fb2613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff161115612007576040517fdd020e2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8b61201383600161305e565b8151811061202357612023613277565b602002602001015167ffffffffffffffff168167ffffffffffffffff1603612077576040517fa5a1ff5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8082165f90815260068c0160209081526040808320815160a081018352815463ffffffff808216835264010000000080830489168488015273ffffffffffffffffffffffffffffffffffffffff6c01000000000000000000000000938490041684870152600185015460ff1615156060808601919091528651908101875260029095015480831686529081048916968501969096529404909516918101919091526080840181905251169003612162576040517f961e3e8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060600151156123385766ffffffffffffff600883901c1695508486146121a557335f90815260098c016020908152604080832089845290915290205486955093505b600160ff83161b84165f036123385767ffffffffffffffff82165f90815260048c01602052604090205473ffffffffffffffffffffffffffffffffffffffff1680612228576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b73ffffffffffffffffffffffffffffffffffffffff811633146123365761224e816126e2565b15806122f357506040517f830639ac00000000000000000000000000000000000000000000000000000000815233600482015267ffffffffffffffff8416602482015273ffffffffffffffffffffffffffffffffffffffff82169063830639ac90604401602060405180830381865afa1580156122cd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122f19190613710565b155b15612336576040517fb7f529fe00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8416600482015260240161037b565b505b6123418161270d565b895481516c0100000000000000000000000090910463ffffffff16908d90839061236c9083906136f3565b63ffffffff1690819052919091111590506123bf576040517f639f585100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8316600482015260240161037b565b60208101516123ce9089613552565b9750806080015160200151896123e49190613552565b67ffffffffffffffff9283165f90815260068d01602090815260409182902084518154868401518786015163ffffffff9384167fffffffffffffffffffffffffffffffffffffffff00000000000000000000000093841617640100000000928b168302176bffffffffffffffffffffffff166c0100000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90921682021785556060890151600180870180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001692151592909217909155608090990151805160029096018054978201519190980151959094169590921694909417918816909302177fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff169516029390931790925590975001611f5d565b505050505094509492505050565b805460028201545f91640100000000900467ffffffffffffffff16906125539063ffffffff1643613463565b63ffffffff166125639190613526565b600283018054919250829160049061258e908490640100000000900467ffffffffffffffff16613552565b825467ffffffffffffffff9182166101009390930a92830291909202199091161790555081546125c49063ffffffff1682613526565b600283018054600c906125f29084906c01000000000000000000000000900467ffffffffffffffff16613552565b82546101009290920a67ffffffffffffffff818102199093169190921691909102179055505060020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000164363ffffffff16179055565b6126548161279e565b6001820180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff9290921691909117905580547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff16680100000000000000004363ffffffff1602179055565b5f61139f6298968067ffffffffffffffff841661372b565b5f61139f827f830639ac00000000000000000000000000000000000000000000000000000000612815565b60208101516080820151515f91906127259043613463565b63ffffffff166127359190613526565b905080826080015160200181815161274d9190613552565b67ffffffffffffffff16905250815161276c9063ffffffff1682613526565b82608001516040018181516127819190613552565b67ffffffffffffffff16905250506080015163ffffffff43169052565b80545f9063ffffffff640100000000820481169167ffffffffffffffff700100000000000000000000000000000000820416916127e8916801000000000000000090041643613505565b6127f29190613526565b6127fc9190613526565b600183015461139f919067ffffffffffffffff16613552565b5f61281f83612837565b80156128305750612830838361289a565b9392505050565b5f612862827f01ffc9a70000000000000000000000000000000000000000000000000000000061289a565b801561139f5750612893827fffffffff0000000000000000000000000000000000000000000000000000000061289a565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a70000000000000000000000000000000000000000000000000000000017815282515f9392849283928392918391908a617530fa92503d91505f519050828015612950575060208210155b801561295b57505f81115b979650505050505050565b5f8083601f840112612976575f80fd5b50813567ffffffffffffffff81111561298d575f80fd5b6020830191508360208285010111156129a4575f80fd5b9250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a0157612a016129ab565b604052919050565b5f67ffffffffffffffff821115612a2257612a226129ab565b5060051b60200190565b803567ffffffffffffffff81168114612a43575f80fd5b919050565b5f82601f830112612a57575f80fd5b81356020612a6c612a6783612a09565b6129d8565b8083825260208201915060208460051b870101935086841115612a8d575f80fd5b602086015b84811015612ab057612aa381612a2c565b8352918301918301612a92565b509695505050505050565b801515811461180f575f80fd5b5f60a08284031215612ad8575f80fd5b60405160a0810181811067ffffffffffffffff82111715612afb57612afb6129ab565b604052905080823563ffffffff81168114612b14575f80fd5b8152612b2260208401612a2c565b6020820152612b3360408401612a2c565b60408201526060830135612b4681612abb565b6060820152608092830135920191909152919050565b5f805f805f805f610120888a031215612b73575f80fd5b873567ffffffffffffffff80821115612b8a575f80fd5b612b968b838c01612966565b909950975060208a0135915080821115612bae575f80fd5b612bba8b838c01612a48565b965060408a0135915080821115612bcf575f80fd5b50612bdc8a828b01612966565b90955093505060608801359150612bf68960808a01612ac8565b905092959891949750929550565b5f805f8060e08587031215612c17575f80fd5b843567ffffffffffffffff80821115612c2e575f80fd5b612c3a88838901612966565b90965094506020870135915080821115612c52575f80fd5b50612c5f87828801612a48565b925050612c6f8660408701612ac8565b905092959194509250565b5f8083601f840112612c8a575f80fd5b50813567ffffffffffffffff811115612ca1575f80fd5b6020830191508360208260051b85010111156129a4575f80fd5b5f805f805f806101208789031215612cd1575f80fd5b67ffffffffffffffff8088351115612ce7575f80fd5b8735880189601f820112612cf9575f80fd5b612d06612a678235612a09565b81358082526020808301929160051b8401018c1015612d23575f80fd5b602083015b6020843560051b850101811015612dbd578481351115612d46575f80fd5b803584018d603f820112612d58575f80fd5b602081013586811115612d6d57612d6d6129ab565b612d806020601f19601f840116016129d8565b8181528f6040838501011115612d94575f80fd5b816040840160208301375f60208383010152808652505050602083019250602081019050612d28565b50985050506020880135811015612dd2575f80fd5b612de28960208a01358a01612a48565b95508060408901351115612df4575f80fd5b50612e058860408901358901612c7a565b909450925060608701359150612e1e8860808901612ac8565b90509295509295509295565b5f805f8060408587031215612e3d575f80fd5b843567ffffffffffffffff80821115612e54575f80fd5b612e6088838901612c7a565b90965094506020870135915080821115612e78575f80fd5b50612e8587828801612c7a565b95989497509550505050565b5f805f8060408587031215612ea4575f80fd5b843567ffffffffffffffff80821115612ebb575f80fd5b612e6088838901612966565b5f805f8060e08587031215612eda575f80fd5b843567ffffffffffffffff80821115612ef1575f80fd5b612c3a88838901612c7a565b5f805f8060e08587031215612f10575f80fd5b843567ffffffffffffffff811115612f26575f80fd5b612f3287828801612c7a565b90955093505060208501359150612c6f8660408701612ac8565b803573ffffffffffffffffffffffffffffffffffffffff81168114612a43575f80fd5b5f805f805f6101008688031215612f84575f80fd5b612f8d86612f4c565b9450602086013567ffffffffffffffff811115612fa8575f80fd5b612fb488828901612c7a565b90955093505060408601359150612fce8760608801612ac8565b90509295509295909350565b5f805f8060e08587031215612fed575f80fd5b612ff685612f4c565b9350602085013567ffffffffffffffff811115613011575f80fd5b61301d87828801612c7a565b9094509250612c6f90508660408701612ac8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8082018082111561139f5761139f613031565b5f815180845260208085019450602084015f5b838110156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b509495945050505050565b81835281816020850137505f602082840101525f6020601f19601f840116840101905092915050565b5f6101008083526130f18184018a613071565b9050828103602084015261310681888a6130b5565b9050828103604084015261311b8186886130b5565b91505061295b606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8284823760609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169101908152601401919050565b602081525f6131ba6020830184866130b5565b949350505050565b5f63ffffffff8216806131d7576131d7613031565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60e081525f61321160e0830187613071565b82810360208401526132248186886130b5565b915050611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126132d7575f80fd5b83018035915067ffffffffffffffff8211156132f1575f80fd5b6020019150368190038213156129a4575f80fd5b5f5b8381101561331f578181015183820152602001613307565b50505f910152565b5f815180845261333e816020860160208601613305565b601f01601f19169290920160200192915050565b5f61010080835261336581840188613071565b905082810360208401526133798187613327565b9050828103604084015261338d8186613327565b915050611d68606083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b8183525f60208085019450825f5b858110156130aa5767ffffffffffffffff61340883612a2c565b16875295820195908201906001016133ee565b604081525f61342e6040830186886133e0565b828103602084015261295b8185876130b5565b5f63ffffffff80831681810361345957613459613031565b6001019392505050565b63ffffffff82811682821603908082111561348057613480613031565b5092915050565b60c081525f61349a60c0830185876133e0565b90506131ba602083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f602082840312156134fc575f80fd5b61283082612a2c565b67ffffffffffffffff82811682821603908082111561348057613480613031565b67ffffffffffffffff81811683821602808216919082811461354a5761354a613031565b505092915050565b67ffffffffffffffff81811683821601908082111561348057613480613031565b8181038181111561139f5761139f613031565b60e081525f61359960e0830186886133e0565b9050836020830152611d68604083018463ffffffff8151168252602081015167ffffffffffffffff80821660208501528060408401511660408501525050606081015115156060830152608081015160808301525050565b5f82613624577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b500690565b5f835161363a818460208801613305565b60609390931b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169190920190815260140192915050565b602081525f6128306020830184613327565b80515f9060208084018383156130aa57815167ffffffffffffffff1687529582019590820190600101613084565b5f6128308284613684565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008360601b1681525f6131ba6014830184613684565b63ffffffff81811683821601908082111561348057613480613031565b5f60208284031215613720575f80fd5b815161283081612abb565b808202811582820484141761139f5761139f61303156fea264697066735822122001f8ca2b992b1c23e324c159bf3203b3e68ec294c5a7a5bc258834d2006e6d2764736f6c63430008180033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
Loading...
Loading

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.