ETH Price: $3,252.18 (+0.33%)

Contract

0x10F4ec919E3e692cB79301E58a7055C783630Dfc
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Deposit Eth And ...212199382024-11-19 6:19:1162 days ago1731997151IN
0x10F4ec91...783630Dfc
64 ETH0.007133038.72993803
Deposit Eth And ...211737032024-11-12 19:30:2369 days ago1731439823IN
0x10F4ec91...783630Dfc
32 ETH0.0115643833.39807597
Deposit Eth And ...210911972024-11-01 7:07:1180 days ago1730444831IN
0x10F4ec91...783630Dfc
32 ETH0.003721765.37625635
Deposit Eth And ...210757172024-10-30 3:16:2382 days ago1730258183IN
0x10F4ec91...783630Dfc
416 ETH0.0210133911.38678258
Deposit Eth And ...210757032024-10-30 3:13:3582 days ago1730258015IN
0x10F4ec91...783630Dfc
1,600 ETH0.0915713314.29239767
Deposit Eth And ...210756822024-10-30 3:09:2382 days ago1730257763IN
0x10F4ec91...783630Dfc
1,600 ETH0.0880900813.74973382
Deposit Eth And ...210756642024-10-30 3:05:3582 days ago1730257535IN
0x10F4ec91...783630Dfc
1,600 ETH0.1119804717.47168897
Deposit Eth And ...210756422024-10-30 3:00:5982 days ago1730257259IN
0x10F4ec91...783630Dfc
1,600 ETH0.0935484614.57921065
Deposit Eth And ...210756242024-10-30 2:57:2382 days ago1730257043IN
0x10F4ec91...783630Dfc
1,600 ETH0.0891468713.8604664
Set Ssv Operator...210721432024-10-29 15:19:3583 days ago1730215175IN
0x10F4ec91...783630Dfc
0 ETH0.0022008413.72023173
Set Ssv Operator...210721362024-10-29 15:18:1183 days ago1730215091IN
0x10F4ec91...783630Dfc
0 ETH0.0022961414.31431359
Set Ssv Operator...210721302024-10-29 15:16:5983 days ago1730215019IN
0x10F4ec91...783630Dfc
0 ETH0.0022780814.20173841
Set Ssv Operator...210721222024-10-29 15:15:2383 days ago1730214923IN
0x10F4ec91...783630Dfc
0 ETH0.0022922214.28987866
Deposit Eth And ...210702472024-10-29 8:58:2383 days ago1730192303IN
0x10F4ec91...783630Dfc
1,600 ETH0.056955388.85001951
Deposit Eth And ...210423622024-10-25 11:34:1187 days ago1729856051IN
0x10F4ec91...783630Dfc
160 ETH0.004360384.94142576
Deposit Eth And ...210423412024-10-25 11:29:5987 days ago1729855799IN
0x10F4ec91...783630Dfc
864 ETH0.019185835.3695985
Deposit Eth And ...210423072024-10-25 11:23:1187 days ago1729855391IN
0x10F4ec91...783630Dfc
864 ETH0.021316355.96006203
Deposit Eth And ...210422782024-10-25 11:17:2387 days ago1729855043IN
0x10F4ec91...783630Dfc
864 ETH0.019107644.89460122
Deposit Eth And ...210421122024-10-25 10:43:5987 days ago1729853039IN
0x10F4ec91...783630Dfc
352 ETH0.00797954.97318438
Deposit Eth And ...210420932024-10-25 10:40:1187 days ago1729852811IN
0x10F4ec91...783630Dfc
864 ETH0.023395566.51773771
Deposit Eth And ...210420662024-10-25 10:34:4787 days ago1729852487IN
0x10F4ec91...783630Dfc
864 ETH0.025224726.45777652
Deposit Eth And ...210352682024-10-24 11:50:1188 days ago1729770611IN
0x10F4ec91...783630Dfc
512 ETH0.019663128.82020191
Deposit Eth And ...210352372024-10-24 11:43:5988 days ago1729770239IN
0x10F4ec91...783630Dfc
864 ETH0.034808649.71209049
Deposit Eth And ...210351962024-10-24 11:35:3588 days ago1729769735IN
0x10F4ec91...783630Dfc
864 ETH0.035524839.8941986
Deposit Eth And ...210351672024-10-24 11:29:4788 days ago1729769387IN
0x10F4ec91...783630Dfc
864 ETH0.038062259.73965841
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
212199382024-11-19 6:19:1162 days ago1731997151
0x10F4ec91...783630Dfc
 Contract Creation0 ETH
212199382024-11-19 6:19:1162 days ago1731997151
0x10F4ec91...783630Dfc
32 ETH
212199382024-11-19 6:19:1162 days ago1731997151
0x10F4ec91...783630Dfc
32 ETH
211961382024-11-15 22:41:1166 days ago1731710471
0x10F4ec91...783630Dfc
32 ETH
211961382024-11-15 22:41:1166 days ago1731710471
0x10F4ec91...783630Dfc
32 ETH
211961382024-11-15 22:41:1166 days ago1731710471
0x10F4ec91...783630Dfc
64 ETH
211737562024-11-12 19:40:5969 days ago1731440459
0x10F4ec91...783630Dfc
 Contract Creation0 ETH
211737562024-11-12 19:40:5969 days ago1731440459
0x10F4ec91...783630Dfc
32 ETH
211737562024-11-12 19:40:5969 days ago1731440459
0x10F4ec91...783630Dfc
32 ETH
211737032024-11-12 19:30:2369 days ago1731439823
0x10F4ec91...783630Dfc
32 ETH
210911972024-11-01 7:07:1180 days ago1730444831
0x10F4ec91...783630Dfc
 Contract Creation0 ETH
210911972024-11-01 7:07:1180 days ago1730444831
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
210757172024-10-30 3:16:2382 days ago1730258183
0x10F4ec91...783630Dfc
32 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
P2pSsvProxyFactory

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 34 : P2pSsvProxyFactory.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "../@openzeppelin/contracts/proxy/Clones.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165.sol";

import "../interfaces/IDepositContract.sol";
import "../interfaces/p2p/IFeeDistributor.sol";
import "../interfaces/p2p/IFeeDistributorFactory.sol";
import "../interfaces/ssv/ISSVViews.sol";

import "../assetRecovering/OwnableAssetRecoverer.sol";
import "../access/OwnableWithOperator.sol";
import "../p2pSsvProxy/P2pSsvProxy.sol";
import "../structs/P2pStructs.sol";
import "./IP2pSsvProxyFactory.sol";

/// @notice Passed address is not a valid FeeDistributorFactory
/// @param _passedAddress Passed address
error P2pSsvProxyFactory__NotFeeDistributorFactory(address _passedAddress);

/// @notice Passed address is not a valid FeeDistributor
/// @param _passedAddress Passed address
error P2pSsvProxyFactory__NotFeeDistributor(address _passedAddress);

/// @notice Passed address is not a valid P2pSsvProxy
/// @param _passedAddress Passed address
error P2pSsvProxyFactory__NotP2pSsvProxy(address _passedAddress);

/// @notice Caller in not an allowed SSV operator owner
/// @param _caller Caller address
error P2pSsvProxyFactory__NotAllowedSsvOperatorOwner(address _caller);

/// @notice Cannot add an already existing SSV operator owner address
/// @param _ssvOperatorOwner an already existing SSV operator owner address
error P2pSsvProxyFactory__SsvOperatorOwnerAlreadyExists(address _ssvOperatorOwner);

/// @notice Cannot remove a nonexisting SSV operator owner address
/// @param _ssvOperatorOwner a nonexisting SSV operator owner address
error P2pSsvProxyFactory__SsvOperatorOwnerDoesNotExist(address _ssvOperatorOwner);

/// @notice This SSV operator ID is not allowed. Check both the operator owner address and the ID for being allowed
/// @param _ssvOperatorOwner operator owner address
/// @param _ssvOperatorId operator ID
error P2pSsvProxyFactory__SsvOperatorNotAllowed(address _ssvOperatorOwner, uint64 _ssvOperatorId);

/// @notice All operators should belong to different owners
/// @param _ssvOperatorOwner operator owner who owns at least 2 of the passed operator IDs
/// @param _ssvOperatorId1 passed operator ID owned by the same owner
/// @param _ssvOperatorId2 passed operator ID owned by the same owner
error P2pSsvProxyFactory__DuplicateOperatorOwnersNotAllowed(
    address _ssvOperatorOwner,
    uint64 _ssvOperatorId1,
    uint64 _ssvOperatorId2
);

/// @notice All the SSV operator IDs must be unique
/// @param _ssvOperatorId duplicated operator ID
error P2pSsvProxyFactory__DuplicateIdsNotAllowed(uint64 _ssvOperatorId);

/// @notice ETH value passed with the transaction must be equal to the needed value
/// @param _needed needed ETH value
/// @param _paid actually sent ETH value
error P2pSsvProxyFactory__NotEnoughEtherPaidToCoverSsvFees(uint256 _needed, uint256 _paid);

/// @notice ETH value passed with the transaction must be equal to 32 times validator count
/// @param _actualEthValue actually sent ETH value
error P2pSsvProxyFactory__EthValueMustBe32TimesValidatorCount(uint256 _actualEthValue);

/// @dev We assume, SSV won't either drop 7539x or soar higher than 100 ETH.
/// If it does, this contract won't be operational and another contract will have to be deployed.
error P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiOutOfRange();

/// @notice Maximum amount of SSV tokens per validator must be >= 10^12 and <= 10^24
error P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorOutOfRange();

/// @notice SSV per ETH exchange rate has not been set. Cannot register validators without it.
error P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiNotSet();

/// @notice Maximum amount of SSV tokens per validator has not been set. Cannot do depositEthAndRegisterValidators without it.
error P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorNotSet();

/// @notice Cannot use token amount per validator larger than Maximum amount of SSV tokens per validator.
error P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorExceeded();

/// @notice This SSV operator ID does not belong to the passed owner
/// @param _operatorId SSV operator ID
/// @param _passedOwner passed address for SSV operator owner
/// @param _actualOwner actual SSV operator owner address
error P2pSsvProxyFactory__SsvOperatorIdDoesNotBelongToOwner(
    uint64 _operatorId,
    address _passedOwner,
    address _actualOwner
);

/// @notice Should pass at least 1 selector
error P2pSsvProxyFactory__CannotSetZeroSelectors();

/// @notice Should pass at least 1 selector
error P2pSsvProxyFactory__CannotRemoveZeroSelectors();

/// @notice Should pass at least 1 SSV operator owner
error P2pSsvProxyFactory__CannotSetZeroAllowedSsvOperatorOwners();

/// @notice Should pass at least 1 SSV operator owner
error P2pSsvProxyFactory__CannotRemoveZeroAllowedSsvOperatorOwners();

/// @notice There should equal number of pubkeys, signatures, and depositDataRoots
/// @param _ssvValidatorsLength validators list length
/// @param _signaturesLength signatures list length
/// @param _depositDataRootsLength depositDataRoots list length
error P2pSsvProxyFactory__DepositDataArraysShouldHaveTheSameLength(
    uint256 _ssvValidatorsLength,
    uint256 _signaturesLength,
    uint256 _depositDataRootsLength
);

/// @title Entry point for SSV validator registration
/// @dev Deploys P2pSsvProxy instances
contract P2pSsvProxyFactory is OwnableAssetRecoverer, OwnableWithOperator, ERC165, IP2pSsvProxyFactory {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @notice Beacon Deposit Contract
    IDepositContract private immutable i_depositContract;

    /// @notice FeeDistributorFactory
    IFeeDistributorFactory private immutable i_feeDistributorFactory;

    /// @notice SSV ERC-20 token
    IERC20 private immutable i_ssvToken;

    /// @notice SSVNetworkViews
    ISSVViews private immutable i_ssvViews;

    /// @notice Template set by P2P to be used for new FeeDistributor instances.
    /// @dev Can be changed by P2P at any time. It will only affect the new clusters.
    /// Existing clusters will keep their existing FeeDistributor instance.
    address private s_referenceFeeDistributor;

    /// @notice Template set by P2P to be used for new P2pSsvProxy instances.
    /// @dev Can be changed by P2P at any time. It will only affect the new clusters.
    /// Existing clusters will keep their existing P2pSsvProxy instance.
    P2pSsvProxy private s_referenceP2pSsvProxy;

    /// @notice a set of addresses of SSV operator owners (both P2P and partners).
    /// @dev Only P2P can add or remove addresses from the set.
    EnumerableSet.AddressSet private s_allowedSsvOperatorOwners;

    /// @notice a mapping of (operator owner address → SSV operator IDs list).
    /// @dev The list of allowed SSV operator IDs for each address is limited to 8 IDs.
    /// The operator owner can update only their list. P2P can update lists of any owners.
    mapping(address => uint64[MAX_ALLOWED_SSV_OPERATOR_IDS]) private s_allowedSsvOperatorIds;

    /// @notice a mapping of (client address → a list of addresses of the deployed client P2pSsvProxy instances).
    /// @dev Updated automatically during P2pSsvProxy instance deployment.
    mapping(address => address[]) private s_allClientP2pSsvProxies;

    /// @notice a list of all ever deployed client P2pSsvProxy instances.
    /// @dev Updated automatically during P2pSsvProxy instance deployment.
    address[] private s_allP2pSsvProxies;

    /// @notice a mapping to check if a certain selector (function signature) is allowed for clients to call on SSVNetwork via P2pSsvProxy.
    mapping(bytes4 => bool) private s_clientSelectors;

    /// @notice a mapping to check if a certain selector (function signature) is allowed for a P2P operator to call on SSVNetwork via P2pSsvProxy.
    mapping(bytes4 => bool) private s_operatorSelectors;

    /// @notice Exchange rate between SSV and ETH set by P2P.
    /// @dev (If 1 SSV = 0.007539 ETH, it should be 0.007539 * 10^18 = 7539000000000000).
    /// Only used during validator registration without ETH deposits to cover SSV token costs with client ETH.
    /// SSV tokens exchanged with this rate cannot be withdrawn by the client.
    /// P2P is willing to tolarate potential discrepancies with the market exchange rate for the sake of simplicity.
    /// The client agrees to this rate when calls `registerValidators` function.
    uint112 private s_ssvPerEthExchangeRateDividedByWei;

    /// @notice Maximum amount of SSV tokens per validator that is allowed for client to deposit during `depositEthAndRegisterValidators`
    uint112 private s_maxSsvTokenAmountPerValidator;

    /// @notice If the given _ssvOperatorOwner is not allowed, revert
    modifier onlyAllowedSsvOperatorOwner(address _ssvOperatorOwner) {
        bool isAllowed = s_allowedSsvOperatorOwners.contains(_ssvOperatorOwner);
        if (!isAllowed) {
            revert P2pSsvProxyFactory__NotAllowedSsvOperatorOwner(_ssvOperatorOwner);
        }
        _;
    }

    /// @notice If the msg.sender is not an allowed SSV operator owner, revert
    modifier onlySsvOperatorOwner() {
        bool isAllowed = s_allowedSsvOperatorOwners.contains(msg.sender);
        if (!isAllowed) {
            revert P2pSsvProxyFactory__NotAllowedSsvOperatorOwner(msg.sender);
        }
        _;
    }

    /// @notice Revert if either 1) one of the operator IDs is not allowed 2) at least 2 operator IDs belong to the same owner
    modifier onlyAllowedOperators(SsvOperator[] calldata _operators) {
        uint256 operatorCount = _operators.length;
        for (uint256 i = 0; i < operatorCount;) {
            address currentOperatorOwner = _operators[i].owner;

            uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] memory allowedIds = s_allowedSsvOperatorIds[currentOperatorOwner];

            bool isAllowed;
            for (uint256 j = 0; j < MAX_ALLOWED_SSV_OPERATOR_IDS;) {
                if (allowedIds[j] == _operators[i].id) {
                    isAllowed = true;
                    break;
                }

                unchecked {++j;}
            }
            if (!isAllowed) {
                revert P2pSsvProxyFactory__SsvOperatorNotAllowed(currentOperatorOwner, _operators[i].id);
            }

            for (uint256 k = 0; k < operatorCount;) {
                if (i != k && currentOperatorOwner == _operators[k].owner) {
                    revert P2pSsvProxyFactory__DuplicateOperatorOwnersNotAllowed(
                        currentOperatorOwner,
                        _operators[i].id,
                        _operators[k].id
                    );
                }

                unchecked {++k;}
            }

            unchecked {++i;}
        }

        _;
    }

    /// @dev Set values that are constant, common for all clients, known at the initial deploy time.
    /// @param _feeDistributorFactory FeeDistributorFactory address
    /// @param _referenceFeeDistributor reference FeeDistributor address
    constructor(
        address _feeDistributorFactory,
        address _referenceFeeDistributor
    ) {
        if (!ERC165Checker.supportsInterface(_feeDistributorFactory, type(IFeeDistributorFactory).interfaceId)) {
            revert P2pSsvProxyFactory__NotFeeDistributorFactory(_feeDistributorFactory);
        }
        i_feeDistributorFactory = IFeeDistributorFactory(_feeDistributorFactory);

        if (!ERC165Checker.supportsInterface(_referenceFeeDistributor, type(IFeeDistributor).interfaceId)) {
            revert P2pSsvProxyFactory__NotFeeDistributor(_referenceFeeDistributor);
        }

        s_referenceFeeDistributor = _referenceFeeDistributor;
        emit P2pSsvProxyFactory__ReferenceFeeDistributorSet(_referenceFeeDistributor);

        i_depositContract = (block.chainid == 1)
            ? IDepositContract(0x00000000219ab540356cBB839Cbe05303d7705Fa)
            : (block.chainid == 5)
                    ? IDepositContract(0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b)
                    : IDepositContract(0x4242424242424242424242424242424242424242);

        i_ssvToken = (block.chainid == 1)
            ? IERC20(0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54)
            : (block.chainid == 5)
                    ? IERC20(0x3a9f01091C446bdE031E39ea8354647AFef091E7)
                    : IERC20(0xad45A78180961079BFaeEe349704F411dfF947C6);

        i_ssvViews = (block.chainid == 1)
            ? ISSVViews(0xafE830B6Ee262ba11cce5F32fDCd760FFE6a66e4)
            : (block.chainid == 5)
                    ? ISSVViews(0xAE2C84c48272F5a1746150ef333D5E5B51F68763)
                    : ISSVViews(0x352A18AEe90cdcd825d1E37d9939dCA86C00e281);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setSsvPerEthExchangeRateDividedByWei(uint112 _ssvPerEthExchangeRateDividedByWei) external onlyOwner {
        if (_ssvPerEthExchangeRateDividedByWei < 10 ** 12 || _ssvPerEthExchangeRateDividedByWei > 10 ** 20) {
            revert P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiOutOfRange();
        }

        s_ssvPerEthExchangeRateDividedByWei = _ssvPerEthExchangeRateDividedByWei;
        emit P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiSet(_ssvPerEthExchangeRateDividedByWei);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setMaxSsvTokenAmountPerValidator(uint112 _maxSsvTokenAmountPerValidator) external onlyOwner {
        if (_maxSsvTokenAmountPerValidator < 10 ** 12 || _maxSsvTokenAmountPerValidator > 10 ** 24) {
            revert P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorOutOfRange();
        }

        s_maxSsvTokenAmountPerValidator = _maxSsvTokenAmountPerValidator;
        emit P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorSet(_maxSsvTokenAmountPerValidator);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setReferenceP2pSsvProxy(address _referenceP2pSsvProxy) external onlyOwner {
        if (!ERC165Checker.supportsInterface(_referenceP2pSsvProxy, type(IP2pSsvProxy).interfaceId)) {
            revert P2pSsvProxyFactory__NotP2pSsvProxy(_referenceP2pSsvProxy);
        }

        s_referenceP2pSsvProxy = P2pSsvProxy(_referenceP2pSsvProxy);
        emit P2pSsvProxyFactory__ReferenceP2pSsvProxySet(_referenceP2pSsvProxy);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setAllowedSelectorsForClient(bytes4[] calldata _selectors) external onlyOwner {
        uint256 count = _selectors.length;

        if (count == 0) {
            revert P2pSsvProxyFactory__CannotSetZeroSelectors();
        }

        for (uint256 i = 0; i < count;) {
            s_clientSelectors[_selectors[i]] = true;

            unchecked {
                ++i;
            }
        }

        emit P2pSsvProxyFactory__AllowedSelectorsForClientSet(_selectors);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function removeAllowedSelectorsForClient(bytes4[] calldata _selectors) external onlyOwner {
        uint256 count = _selectors.length;

        if (count == 0) {
            revert P2pSsvProxyFactory__CannotRemoveZeroSelectors();
        }

        for (uint256 i = 0; i < count;) {
            s_clientSelectors[_selectors[i]] = false;

            unchecked {
                ++i;
            }
        }

        emit P2pSsvProxyFactory__AllowedSelectorsForClientRemoved(_selectors);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setAllowedSelectorsForOperator(bytes4[] calldata _selectors) external onlyOwner {
        uint256 count = _selectors.length;

        if (count == 0) {
            revert P2pSsvProxyFactory__CannotSetZeroSelectors();
        }

        for (uint256 i = 0; i < count;) {
            s_operatorSelectors[_selectors[i]] = true;

            unchecked {
                ++i;
            }
        }

        emit P2pSsvProxyFactory__AllowedSelectorsForOperatorSet(_selectors);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function removeAllowedSelectorsForOperator(bytes4[] calldata _selectors) external onlyOwner {
        uint256 count = _selectors.length;

        if (count == 0) {
            revert P2pSsvProxyFactory__CannotRemoveZeroSelectors();
        }

        for (uint256 i = 0; i < count;) {
            s_operatorSelectors[_selectors[i]] = false;

            unchecked {
                ++i;
            }
        }

        emit P2pSsvProxyFactory__AllowedSelectorsForOperatorRemoved(_selectors);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setReferenceFeeDistributor(
        address _referenceFeeDistributor
    ) external onlyOperatorOrOwner {
        if (!ERC165Checker.supportsInterface(_referenceFeeDistributor, type(IFeeDistributor).interfaceId)) {
            revert P2pSsvProxyFactory__NotFeeDistributor(_referenceFeeDistributor);
        }

        s_referenceFeeDistributor = _referenceFeeDistributor;
        emit P2pSsvProxyFactory__ReferenceFeeDistributorSet(_referenceFeeDistributor);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setAllowedSsvOperatorOwners(
        address[] calldata _allowedSsvOperatorOwners
    ) external onlyOperatorOrOwner {
        uint256 count = _allowedSsvOperatorOwners.length;

        if (count == 0) {
            revert P2pSsvProxyFactory__CannotSetZeroAllowedSsvOperatorOwners();
        }

        for (uint256 i = 0; i < count;) {
            address allowedSsvOperatorOwner = _allowedSsvOperatorOwners[i];

            if (!s_allowedSsvOperatorOwners.add(allowedSsvOperatorOwner)) {
                revert P2pSsvProxyFactory__SsvOperatorOwnerAlreadyExists(allowedSsvOperatorOwner);
            }

            unchecked {
                ++i;
            }
        }

        emit P2pSsvProxyFactory__AllowedSsvOperatorOwnersSet(_allowedSsvOperatorOwners);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function removeAllowedSsvOperatorOwners(
        address[] calldata _allowedSsvOperatorOwnersToRemove
    ) external onlyOperatorOrOwner {
        uint256 count = _allowedSsvOperatorOwnersToRemove.length;

        if (count == 0) {
            revert P2pSsvProxyFactory__CannotRemoveZeroAllowedSsvOperatorOwners();
        }

        for (uint256 i = 0; i < count;) {
            address allowedSsvOperatorOwnersToRemove = _allowedSsvOperatorOwnersToRemove[i];

            if (!s_allowedSsvOperatorOwners.remove(allowedSsvOperatorOwnersToRemove)) {
                revert P2pSsvProxyFactory__SsvOperatorOwnerDoesNotExist(allowedSsvOperatorOwnersToRemove);
            }

            unchecked {
                ++i;
            }
        }

        emit P2pSsvProxyFactory__AllowedSsvOperatorOwnersRemoved(_allowedSsvOperatorOwnersToRemove);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setSsvOperatorIds(
        uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] calldata _operatorIds
    ) external onlySsvOperatorOwner {
        _setSsvOperatorIds(_operatorIds, msg.sender);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function setSsvOperatorIds(
        uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] calldata _operatorIds,
        address _ssvOperatorOwner
    ) external onlyOperatorOrOwner onlyAllowedSsvOperatorOwner(_ssvOperatorOwner) {
        _setSsvOperatorIds(_operatorIds, _ssvOperatorOwner);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function clearSsvOperatorIds() external onlySsvOperatorOwner {
        _clearSsvOperatorIds(msg.sender);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function clearSsvOperatorIds(
        address _ssvOperatorOwner
    ) external onlyOperatorOrOwner {
        _clearSsvOperatorIds(_ssvOperatorOwner);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function predictP2pSsvProxyAddress(
        address _feeDistributorInstance
    ) public view returns (address) {
        return Clones.predictDeterministicAddress(
            address(s_referenceP2pSsvProxy),
            bytes32(bytes20(_feeDistributorInstance))
        );
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function createP2pSsvProxy(
        address _feeDistributorInstance
    ) external onlyOperatorOrOwner returns(address p2pSsvProxyInstance) {
        p2pSsvProxyInstance = _createP2pSsvProxy(_feeDistributorInstance);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function depositEthAndRegisterValidators(
        DepositData calldata _depositData,
        address _withdrawalCredentialsAddress,

        SsvPayload calldata _ssvPayload,

        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external payable returns (address p2pSsvProxy) {
        _checkTokenAmount(_ssvPayload.tokenAmount, _ssvPayload.ssvValidators.length);

        _makeBeaconDeposits(_depositData, _withdrawalCredentialsAddress, _ssvPayload.ssvValidators);

        p2pSsvProxy = _registerValidators(_ssvPayload, _clientConfig, _referrerConfig);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function registerValidators(
        SsvPayload calldata _ssvPayload,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external payable returns (address p2pSsvProxy) {
        _checkEthValue(_ssvPayload.tokenAmount);

        p2pSsvProxy = _registerValidators(_ssvPayload, _clientConfig, _referrerConfig);
    }

    function _checkTokenAmount(
        uint256 _tokenAmount,
        uint256 _validatorCount
    ) private view {
        uint112 maxSsvTokenAmountPerValidator = s_maxSsvTokenAmountPerValidator;

        if (maxSsvTokenAmountPerValidator == 0) {
            revert P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorNotSet();
        }

        if (_tokenAmount > maxSsvTokenAmountPerValidator * _validatorCount) {
            revert P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorExceeded();
        }
    }

    /// @notice Register validators with SSV (up to 60, calldata size is the limit) without ETH deposits
    /// @dev Common logic for depositEthAndRegisterValidators and registerValidators functions
    /// @param _ssvPayload a stuct with data necessary for SSV registration (see `SsvPayload` struct for details)
    /// @param _clientConfig address and basis points (percent * 100) of the client (for FeeDistributor)
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer (for FeeDistributor)
    /// @return p2pSsvProxy client P2pSsvProxy instance that became the SSV cluster owner
    function _registerValidators(
        SsvPayload calldata _ssvPayload,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) private onlyAllowedOperators(_ssvPayload.ssvOperators) returns (address p2pSsvProxy) {
        address feeDistributorInstance = _createFeeDistributor(_clientConfig, _referrerConfig);
        p2pSsvProxy = _createP2pSsvProxy(feeDistributorInstance);

        i_ssvToken.transfer(address(p2pSsvProxy), _ssvPayload.tokenAmount);

        P2pSsvProxy(p2pSsvProxy).registerValidators(_ssvPayload);

        emit P2pSsvProxyFactory__RegistrationCompleted(p2pSsvProxy);
    }

    /// @notice Deploy P2pSsvProxy instance if not deployed before
    /// @param _feeDistributorInstance The address of FeeDistributor instance
    /// @return p2pSsvProxyInstance client P2pSsvProxy instance that has been deployed
    function _createP2pSsvProxy(
        address _feeDistributorInstance
    ) private returns(address p2pSsvProxyInstance) {
        p2pSsvProxyInstance = predictP2pSsvProxyAddress(_feeDistributorInstance);
        if (p2pSsvProxyInstance.code.length == 0) { // if p2pSsvProxyInstance doesn't exist, deploy it
            if (!ERC165Checker.supportsInterface(_feeDistributorInstance, type(IFeeDistributor).interfaceId)) {
                revert P2pSsvProxyFactory__NotFeeDistributor(_feeDistributorInstance);
            }

            // clone the reference implementation of P2pSsvProxy
            p2pSsvProxyInstance = Clones.cloneDeterministic(
                address(s_referenceP2pSsvProxy),
                bytes32(bytes20(_feeDistributorInstance))
            );

            // set the client address to the cloned P2pSsvProxy instance
            P2pSsvProxy(p2pSsvProxyInstance).initialize(_feeDistributorInstance);

            address client = IFeeDistributor(_feeDistributorInstance).client();

            // append new P2pSsvProxy address to all client P2pSsvProxies array
            s_allClientP2pSsvProxies[client].push(p2pSsvProxyInstance);

            // append new P2pSsvProxy address to all P2pSsvProxies array
            s_allP2pSsvProxies.push(p2pSsvProxyInstance);

            // emit event with the address of the newly created instance for the external listener
            emit P2pSsvProxyFactory__P2pSsvProxyCreated(
                p2pSsvProxyInstance,
                client,
                _feeDistributorInstance
            );
        }
    }

    /// @notice Deploy FeeDistributor instance if not deployed before
    /// @param _clientConfig address and basis points (percent * 100) of the client (for FeeDistributor)
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer (for FeeDistributor)
    /// @return feeDistributorInstance client FeeDistributor instance that has been deployed
    function _createFeeDistributor(
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) private returns(address feeDistributorInstance) {
        address referenceFeeDistributor_ = s_referenceFeeDistributor;

        feeDistributorInstance = i_feeDistributorFactory.predictFeeDistributorAddress(
            referenceFeeDistributor_,
            _clientConfig,
            _referrerConfig
        );
        if (feeDistributorInstance.code.length == 0) {
            // if feeDistributorInstance doesn't exist, deploy it
            i_feeDistributorFactory.createFeeDistributor(
                referenceFeeDistributor_,
                _clientConfig,
                _referrerConfig
            );
        }
    }

    /// @notice Check ETH value for validator registrations without ETH deposits.
    /// @dev P2P cannot afford totally free validator registrations since they are paid with P2P's SSV tokens.
    /// It's OK for validator registrations with ETH deposits since we can be confident in the existense of EL rewards
    /// that will cover the SSV tokens cost in that case.
    /// If there are no ETH deposits, to prevent draining of SSV tokens from P2pSsvProxyFactory,
    /// the client pays for the used SSV tokens.
    /// The client will only need to pay once. Starting from the next month, P2P will be depositing SSV tokens to the clusters
    /// the same way as with ETH deposits.
    /// @param _tokenAmount amount of ERC-20 SSV tokens for validator registration
    function _checkEthValue(
        uint256 _tokenAmount
    ) private view {
        uint112 exchangeRate = s_ssvPerEthExchangeRateDividedByWei;
        if (exchangeRate == 0) {
            revert P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiNotSet();
        }

        uint256 ssvTokensValueInWei = (_tokenAmount * exchangeRate) / 10**18;
        if (msg.value != ssvTokensValueInWei) {
            revert P2pSsvProxyFactory__NotEnoughEtherPaidToCoverSsvFees(ssvTokensValueInWei, msg.value);
        }
    }

    /// @notice Set SSV operator IDs list for a SSV operator owner
    /// @param _operatorIds SSV operator IDs list
    /// @param _ssvOperatorOwner SSV operator owner
    function _setSsvOperatorIds(
        uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] calldata _operatorIds,
        address _ssvOperatorOwner
    ) private {
        for (uint i = 0; i < _operatorIds.length;) {
            uint64 id = _operatorIds[i];

            for (uint j = i + 1; j < _operatorIds.length;) {
                if (id == _operatorIds[j] && id != 0) {
                    revert P2pSsvProxyFactory__DuplicateIdsNotAllowed(id);
                }
                unchecked {
                    ++j;
                }
            }

            if (id != 0) {
                (address actualOwner,,,,,) = i_ssvViews.getOperatorById(id);
                if (actualOwner != _ssvOperatorOwner) {
                    revert P2pSsvProxyFactory__SsvOperatorIdDoesNotBelongToOwner(id, _ssvOperatorOwner, actualOwner);
                }
            }

            unchecked {
                ++i;
            }
        }

        s_allowedSsvOperatorIds[_ssvOperatorOwner] = _operatorIds;
        emit P2pSsvProxyFactory__SsvOperatorIdsSet(_ssvOperatorOwner, _operatorIds);
    }

    /// @notice Clear SSV operator IDs list for a SSV operator owner
    /// @param _ssvOperatorOwner SSV operator owner
    function _clearSsvOperatorIds(
        address _ssvOperatorOwner
    ) private {
        delete s_allowedSsvOperatorIds[_ssvOperatorOwner];
        emit P2pSsvProxyFactory__SsvOperatorIdsCleared(_ssvOperatorOwner);
    }

    /// @notice Make ETH2 (Beacon) deposits via the official Beacon Deposit Contract
    /// @param _depositData signatures and depositDataRoots from Beacon deposit data
    /// @param _withdrawalCredentialsAddress address for 0x01 withdrawal credentials from Beacon deposit data (1 for the batch)
    /// @param _ssvValidators list of pubkeys and SSV sharesData
    function _makeBeaconDeposits(
        DepositData calldata _depositData,
        address _withdrawalCredentialsAddress,
        SsvValidator[] calldata _ssvValidators
    ) private {
        uint256 validatorCount = _ssvValidators.length;

        if (msg.value != COLLATERAL * validatorCount) {
            revert P2pSsvProxyFactory__EthValueMustBe32TimesValidatorCount(msg.value);
        }

        if (_depositData.signatures.length != validatorCount || _depositData.depositDataRoots.length != validatorCount) {
            revert P2pSsvProxyFactory__DepositDataArraysShouldHaveTheSameLength(
                validatorCount,
                _depositData.signatures.length,
                _depositData.depositDataRoots.length
            );
        }

        bytes memory withdrawalCredentials = abi.encodePacked(
            hex'010000000000000000000000',
            _withdrawalCredentialsAddress
        );

        for (uint256 i = 0; i < validatorCount;) {
            // ETH deposit
            i_depositContract.deposit{value: COLLATERAL}(
                _ssvValidators[i].pubkey,
                withdrawalCredentials,
                _depositData.signatures[i],
                _depositData.depositDataRoots[i]
            );

            unchecked {++i;}
        }
    }

    /// @inheritdoc IOwnable
    function owner() public view override(Ownable, OwnableBase, IOwnable) returns (address) {
        return super.owner();
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getFeeDistributorFactory() external view returns (address) {
        return address(i_feeDistributorFactory);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getAllClientP2pSsvProxies(
        address _client
    ) external view returns (address[] memory) {
        return s_allClientP2pSsvProxies[_client];
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getAllP2pSsvProxies() external view returns (address[] memory) {
        return s_allP2pSsvProxies;
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function isClientSelectorAllowed(bytes4 _selector) external view returns (bool) {
        return s_clientSelectors[_selector];
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function isOperatorSelectorAllowed(bytes4 _selector) external view returns (bool) {
        return s_operatorSelectors[_selector];
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getAllowedSsvOperatorIds(address _ssvOperatorOwner) external view returns (uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] memory) {
        return s_allowedSsvOperatorIds[_ssvOperatorOwner];
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getAllowedSsvOperatorOwners() external view returns (address[] memory) {
        return s_allowedSsvOperatorOwners.values();
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getReferenceFeeDistributor() external view returns (address) {
        return s_referenceFeeDistributor;
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getReferenceP2pSsvProxy() external view returns (address) {
        return address(s_referenceP2pSsvProxy);
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getSsvPerEthExchangeRateDividedByWei() external view returns (uint112) {
        return s_ssvPerEthExchangeRateDividedByWei;
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getNeededAmountOfEtherToCoverSsvFees(uint256 _tokenAmount) external view returns (uint256) {
        return (_tokenAmount * s_ssvPerEthExchangeRateDividedByWei) / 10**18;
    }

    /// @inheritdoc IP2pSsvProxyFactory
    function getMaxSsvTokenAmountPerValidator() external view returns (uint112) {
        return s_maxSsvTokenAmountPerValidator;
    }

    /// @inheritdoc ERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IP2pSsvProxyFactory).interfaceId || super.supportsInterface(interfaceId);
    }
}

File 2 of 34 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity 0.8.18;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 3 of 34 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)

pragma solidity 0.8.18;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 4 of 34 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity 0.8.18;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 5 of 34 : IDepositContract.sol
// SPDX-License-Identifier: CC0-1.0

pragma solidity 0.8.18;

// This interface is designed to be compatible with the Vyper version.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
interface IDepositContract {

    /// @notice Submit a Phase 0 DepositData object.
    /// @param pubkey A BLS12-381 public key.
    /// @param withdrawal_credentials Commitment to a public key for withdrawals.
    /// @param signature A BLS12-381 signature.
    /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
    /// Used as a protection against malformed input.
    function deposit(
        bytes calldata pubkey,
        bytes calldata withdrawal_credentials,
        bytes calldata signature,
        bytes32 deposit_data_root
    ) external payable;
}

File 6 of 34 : IFeeDistributor.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../../@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../../structs/P2pStructs.sol";

/// @dev External interface of FeeDistributor declared to support ERC165 detection.
interface IFeeDistributor is IERC165 {

    /// @notice Emits once the client and the optional referrer have been set.
    /// @param _client address of the client.
    /// @param _clientBasisPoints basis points (percent * 100) of EL rewards that should go to the client
    /// @param _referrer address of the referrer.
    /// @param _referrerBasisPoints basis points (percent * 100) of EL rewards that should go to the referrer
    event FeeDistributor__Initialized(
        address indexed _client,
        uint96 _clientBasisPoints,
        address indexed _referrer,
        uint96 _referrerBasisPoints
    );

    /// @notice Emits on successful withdrawal
    /// @param _serviceAmount how much wei service received
    /// @param _clientAmount how much wei client received
    /// @param _referrerAmount how much wei referrer received
    event FeeDistributor__Withdrawn(
        uint256 _serviceAmount,
        uint256 _clientAmount,
        uint256 _referrerAmount
    );

    /// @notice Emits on request for a voluntary exit of validators
    /// @param _pubkeys pubkeys of validators
    event FeeDistributor__VoluntaryExit(
        bytes[] _pubkeys
    );

    /// @notice Emits if case there was some ether left after `withdraw` and it has been sent successfully.
    /// @param _to destination address for ether.
    /// @param _amount how much wei the destination address received.
    event FeeDistributor__EtherRecovered(
        address indexed _to,
        uint256 _amount
    );

    /// @notice Set client address.
    /// @dev Could not be in the constructor since it is different for different clients.
    /// _referrerConfig can be zero if there is no referrer.
    /// @param _clientConfig address and basis points (percent * 100) of the client
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer.
    function initialize(
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external;

    /// @notice Increase the number of deposited validators.
    /// @dev Should be called when a new ETH2 deposit has been made
    /// @param _validatorCountToAdd number of newly deposited validators
    function increaseDepositedCount(
        uint32 _validatorCountToAdd
    ) external;

    /// @notice Request a voluntary exit of validators
    /// @dev Should be called by the client when they want to signal P2P that certain validators need to be exited
    /// @param _pubkeys pubkeys of validators
    function voluntaryExit(
        bytes[] calldata _pubkeys
    ) external;

    /// @notice Returns the factory address
    /// @return address factory address
    function factory() external view returns (address);

    /// @notice Returns the service address
    /// @return address service address
    function service() external view returns (address);

    /// @notice Returns the client address
    /// @return address client address
    function client() external view returns (address);

    /// @notice Returns the client basis points
    /// @return uint256 client basis points
    function clientBasisPoints() external view returns (uint256);

    /// @notice Returns the referrer address
    /// @return address referrer address
    function referrer() external view returns (address);

    /// @notice Returns the referrer basis points
    /// @return uint256 referrer basis points
    function referrerBasisPoints() external view returns (uint256);

    /// @notice Returns the address for ETH2 0x01 withdrawal credentials associated with this FeeDistributor
    /// @dev Return FeeDistributor's own address if FeeDistributor should be CL rewards recipient
    /// Otherwise, return the client address
    /// @return address address for ETH2 0x01 withdrawal credentials
    function eth2WithdrawalCredentialsAddress() external view returns (address);
}

File 7 of 34 : IFeeDistributorFactory.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../../@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../../access/IOwnable.sol";
import "./IFeeDistributor.sol";
import "../../structs/P2pStructs.sol";

/// @dev External interface of FeeDistributorFactory
interface IFeeDistributorFactory is IOwnable, IERC165 {

    /// @notice Creates a FeeDistributor instance for a client
    /// @dev _referrerConfig can be zero if there is no referrer.
    ///
    /// @param _referenceFeeDistributor The address of the reference implementation of FeeDistributor used as the basis for clones
    /// @param _clientConfig address and basis points (percent * 100) of the client
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer.
    /// @return newFeeDistributorAddress user FeeDistributor instance that has just been deployed
    function createFeeDistributor(
        address _referenceFeeDistributor,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external returns (address newFeeDistributorAddress);

    /// @notice Computes the address of a FeeDistributor created by `createFeeDistributor` function
    /// @dev FeeDistributor instances are guaranteed to have the same address if all of
    /// 1) referenceFeeDistributor 2) clientConfig 3) referrerConfig
    /// are the same
    /// @param _referenceFeeDistributor The address of the reference implementation of FeeDistributor used as the basis for clones
    /// @param _clientConfig address and basis points (percent * 100) of the client
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer.
    /// @return address user FeeDistributor instance that will be or has been deployed
    function predictFeeDistributorAddress(
        address _referenceFeeDistributor,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external view returns (address);

    /// @notice Returns an array of client FeeDistributors
    /// @param _client client address
    /// @return address[] array of client FeeDistributors
    function allClientFeeDistributors(
        address _client
    ) external view returns (address[] memory);

    /// @notice Returns an array of all FeeDistributors for all clients
    /// @return address[] array of all FeeDistributors
    function allFeeDistributors() external view returns (address[] memory);

    /// @notice The address of P2pEth2Depositor
    /// @return address of P2pEth2Depositor
    function p2pEth2Depositor() external view returns (address);

    /// @notice Returns default client basis points
    /// @return default client basis points
    function defaultClientBasisPoints() external view returns (uint96);

    /// @notice Returns the current operator
    /// @return address of the current operator
    function operator() external view returns (address);

    /// @notice Reverts if the passed address is neither operator nor owner
    /// @param _address passed address
    function checkOperatorOrOwner(address _address) external view;

    /// @notice Reverts if the passed address is not P2pEth2Depositor
    /// @param _address passed address
    function checkP2pEth2Depositor(address _address) external view;

    /// @notice Reverts if the passed address is neither of: 1) operator 2) owner 3) P2pEth2Depositor
    /// @param _address passed address
    function check_Operator_Owner_P2pEth2Depositor(address _address) external view;
}

File 8 of 34 : ISSVViews.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @dev https://github.com/bloxapp/ssv-network/blob/8c945e82cc063eb8e40c467d314a470121821157/contracts/interfaces/ISSVViews.sol
interface ISSVViews {
    /// @notice Gets operator details by ID
    /// @param operatorId The ID of the operator
    /// @return owner The owner of the operator
    /// @return fee The fee associated with the operator (SSV)
    /// @return validatorCount The count of validators associated with the operator
    /// @return whitelisted The whitelisted address of the operator, if any
    /// @return isPrivate A boolean indicating if the operator is private
    /// @return active A boolean indicating if the operator is active
    function getOperatorById(uint64 operatorId) external view returns (address owner, uint256 fee, uint32 validatorCount, address whitelisted, bool isPrivate, bool active);
}

File 9 of 34 : OwnableAssetRecoverer.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, Lido <[email protected]>
// SPDX-License-Identifier: MIT

// https://github.com/lidofinance/lido-otc-seller/blob/master/contracts/lib/AssetRecoverer.sol
pragma solidity 0.8.18;

import "./OwnableTokenRecoverer.sol";
import "./AssetRecoverer.sol";

/// @title Public Asset Recoverer with public functions callable by assetAccessingAddress
/// @notice Recover ether, ERC20, ERC721 and ERC1155 from a derived contract
abstract contract OwnableAssetRecoverer is OwnableTokenRecoverer, AssetRecoverer {

    // Functions

    /**
     * @notice transfers ether from this contract
     * @dev using `address.call` is safer to transfer to other contracts
     * @param _recipient address to transfer ether to
     * @param _amount amount of ether to transfer
     */
    function transferEther(address _recipient, uint256 _amount) external onlyOwner {
        _transferEther(_recipient, _amount);
    }
}

File 10 of 34 : OwnableWithOperator.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "./Ownable2Step.sol";
import "./IOwnableWithOperator.sol";

/**
* @notice newOperator is the zero address
*/
error Access__ZeroNewOperator();

/**
* @notice newOperator is the same as the old one
*/
error Access__SameOperator(address _operator);

/**
* @notice caller is neither the operator nor owner
*/
error Access__CallerNeitherOperatorNorOwner(address _caller, address _operator, address _owner);

/**
* @notice address is neither the operator nor owner
*/
error Access__AddressNeitherOperatorNorOwner(address _address, address _operator, address _owner);

/**
 * @dev Ownable with an additional role of operator
 */
abstract contract OwnableWithOperator is Ownable2Step, IOwnableWithOperator {
    address private s_operator;

    /**
     * @dev Emits when the operator has been changed
     * @param _previousOperator address of the previous operator
     * @param _newOperator address of the new operator
     */
    event OperatorChanged(
        address indexed _previousOperator,
        address indexed _newOperator
    );

    /**
     * @dev Throws if called by any account other than the operator or the owner.
     */
    modifier onlyOperatorOrOwner() {
        address currentOwner = owner();
        address currentOperator = s_operator;

        if (currentOperator != _msgSender() && currentOwner != _msgSender()) {
            revert Access__CallerNeitherOperatorNorOwner(_msgSender(), currentOperator, currentOwner);
        }

        _;
    }

    function checkOperatorOrOwner(address _address) public view virtual {
        address currentOwner = owner();
        address currentOperator = s_operator;

        if (_address == address(0) || (currentOperator != _address && currentOwner != _address)) {
            revert Access__AddressNeitherOperatorNorOwner(_address, currentOperator, currentOwner);
        }
    }

    /**
     * @dev Returns the current operator.
     */
    function operator() public view virtual returns (address) {
        return s_operator;
    }

    /**
     * @dev Transfers operator to a new account (`newOperator`).
     * Can only be called by the current owner.
     */
    function changeOperator(address _newOperator) external virtual onlyOwner {
        if (_newOperator == address(0)) {
            revert Access__ZeroNewOperator();
        }
        if (_newOperator == s_operator) {
            revert Access__SameOperator(_newOperator);
        }

        _changeOperator(_newOperator);
    }

    /**
     * @dev Transfers operator to a new account (`newOperator`).
     * Internal function without access restriction.
     */
    function _changeOperator(address _newOperator) internal virtual {
        address oldOperator = s_operator;
        s_operator = _newOperator;
        emit OperatorChanged(oldOperator, _newOperator);
    }

    /**
     * @dev Dismisses the old operator without setting a new one.
     * Can only be called by the current owner.
     */
    function dismissOperator() external virtual onlyOwner {
        _changeOperator(address(0));
    }
}

File 11 of 34 : P2pSsvProxy.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";

import "../constants/P2pConstants.sol";
import "../interfaces/ssv/ISSVNetwork.sol";
import "../interfaces/IDepositContract.sol";
import "../interfaces/p2p/IFeeDistributorFactory.sol";
import "../access/OwnableWithOperator.sol";
import "../assetRecovering/OwnableAssetRecoverer.sol";
import "../structs/P2pStructs.sol";
import "../p2pSsvProxyFactory/IP2pSsvProxyFactory.sol";
import "./IP2pSsvProxy.sol";


/// @notice _referenceFeeDistributor should implement IFeeDistributor interface
/// @param _passedAddress passed address for _referenceFeeDistributor
error P2pSsvProxy__NotFeeDistributor(address _passedAddress);

/// @notice Should be a P2pSsvProxyFactory contract
/// @param _passedAddress passed address that does not support IP2pSsvProxyFactory interface
error P2pSsvProxy__NotP2pSsvProxyFactory(address _passedAddress);

/// @notice Throws if called by any account other than the client.
/// @param _caller address of the caller
/// @param _client address of the client
error P2pSsvProxy__CallerNotClient(address _caller, address _client);

/// @notice The caller was neither operator nor owner
/// @param _caller address of the caller
/// @param _operator address of the operator
/// @param _owner address of the owner
error P2pSsvProxy__CallerNeitherOperatorNorOwner(address _caller, address _operator, address _owner);

/// @notice The caller was neither operator nor owner nor client
/// @param _caller address of the caller
error P2pSsvProxy__CallerNeitherOperatorNorOwnerNorClient(address _caller);

/// @notice Only factory can call `initialize`.
/// @param _msgSender sender address.
/// @param _actualFactory the actual factory address that can call `initialize`.
error P2pSsvProxy__NotP2pSsvProxyFactoryCalled(address _msgSender, IP2pSsvProxyFactory _actualFactory);

/// @notice _pubkeys and _operatorIds arrays should have the same lengths
error P2pSsvProxy__AmountOfParametersError();

/// @notice Selector is not allowed for the caller.
/// @param _caller caller address
/// @param _selector function selector to be called on SSVNetwork
error P2pSsvProxy__SelectorNotAllowed(address _caller, bytes4 _selector);

/// @title Proxy for SSVNetwork calls.
/// @dev Each instance of P2pSsvProxy corresponds to 1 FeeDistributor instance.
/// Thus, client to P2pSsvProxy instances is a 1-to-many relation.
/// SSV tokens are managed by P2P.
/// Clients cover the costs of SSV tokens by EL rewards via FeeDistributor instance.
contract P2pSsvProxy is OwnableAssetRecoverer, ERC165, IP2pSsvProxy {

    /// @notice P2pSsvProxyFactory address
    IP2pSsvProxyFactory private immutable i_p2pSsvProxyFactory;

    /// @notice SSVNetwork address
    ISSVNetwork private immutable i_ssvNetwork;

    /// @notice SSV token (ERC-20) address
    IERC20 private immutable i_ssvToken;

    /// @notice FeeDistributor instance address
    IFeeDistributor private s_feeDistributor;

    /// @notice If caller is not client, revert
    modifier onlyClient() {
        address clientAddress = getClient();

        if (clientAddress != msg.sender) {
            revert P2pSsvProxy__CallerNotClient(msg.sender, clientAddress);
        }
        _;
    }

    /// @notice If caller is neither operator nor owner, revert
    modifier onlyOperatorOrOwner() {
        address currentOwner = owner();
        address currentOperator = operator();

        if (currentOperator != msg.sender && currentOwner != msg.sender) {
            revert P2pSsvProxy__CallerNeitherOperatorNorOwner(msg.sender, currentOperator, currentOwner);
        }

        _;
    }

    /// @notice If caller is neither operator nor owner nor client, revert
    modifier onlyOperatorOrOwnerOrClient() {
        address operator_ = operator();
        address owner_ = owner();
        address client_ = getClient();

        if (operator_ != msg.sender && owner_ != msg.sender && client_ != msg.sender) {
            revert P2pSsvProxy__CallerNeitherOperatorNorOwnerNorClient(msg.sender);
        }
        _;
    }

    /// @notice If caller is not factory, revert
    modifier onlyP2pSsvProxyFactory() {
        if (msg.sender != address(i_p2pSsvProxyFactory)) {
            revert P2pSsvProxy__NotP2pSsvProxyFactoryCalled(msg.sender, i_p2pSsvProxyFactory);
        }
        _;
    }

    /// @dev Set values that are constant, common for all clients, known at the initial deploy time.
    /// @param _p2pSsvProxyFactory address of P2pSsvProxyFactory
    constructor(
        address _p2pSsvProxyFactory
    ) {
        if (!ERC165Checker.supportsInterface(_p2pSsvProxyFactory, type(IP2pSsvProxyFactory).interfaceId)) {
            revert P2pSsvProxy__NotP2pSsvProxyFactory(_p2pSsvProxyFactory);
        }
        i_p2pSsvProxyFactory = IP2pSsvProxyFactory(_p2pSsvProxyFactory);

        i_ssvNetwork = (block.chainid == 1)
            ? ISSVNetwork(0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1)
            : (block.chainid == 5)
                    ? ISSVNetwork(0xC3CD9A0aE89Fff83b71b58b6512D43F8a41f363D)
                    : ISSVNetwork(0x38A4794cCEd47d3baf7370CcC43B560D3a1beEFA);

        i_ssvToken = (block.chainid == 1)
            ? IERC20(0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54)
            : (block.chainid == 5)
                    ? IERC20(0x3a9f01091C446bdE031E39ea8354647AFef091E7)
                    : IERC20(0xad45A78180961079BFaeEe349704F411dfF947C6);
    }

    /// @inheritdoc IP2pSsvProxy
    function initialize(
        address _feeDistributor
    ) external onlyP2pSsvProxyFactory {
        s_feeDistributor = IFeeDistributor(_feeDistributor);

        i_ssvToken.approve(address(i_ssvNetwork), type(uint256).max);

        emit P2pSsvProxy__Initialized(_feeDistributor);
    }

    /// @dev Access any SSVNetwork function as cluster owner (this P2pSsvProxy instance)
    /// Each selector access is managed by P2pSsvProxyFactory roles (owner, operator, client)
    fallback() external {
        address caller = msg.sender;
        bytes4 selector = msg.sig;

        bool isAllowed = msg.sender == owner() ||
            (msg.sender == operator() && i_p2pSsvProxyFactory.isOperatorSelectorAllowed(selector)) ||
            (msg.sender == getClient() && i_p2pSsvProxyFactory.isClientSelectorAllowed(selector));

        if (!isAllowed) {
            revert P2pSsvProxy__SelectorNotAllowed(caller, selector);
        }

        (bool success, bytes memory data) = address(i_ssvNetwork).call(msg.data);
        if (success) {
            emit P2pSsvProxy__SuccessfullyCalledViaFallback(caller, selector);

            assembly {
                return(add(data, 0x20), mload(data))
            }
        } else {
            // Decode the reason from the error data returned from the call and revert with it.
            revert(string(data));
        }
    }

    /// @inheritdoc IP2pSsvProxy
    function registerValidators(
        SsvPayload calldata _ssvPayload
    ) external onlyP2pSsvProxyFactory {
        (
            uint64[] memory operatorIds,
            uint64 clusterIndex
        ) = _getOperatorIdsAndClusterIndex(_ssvPayload.ssvOperators);

        uint256 ssvSlot0 = uint256(_ssvPayload.ssvSlot0);

        // see https://github.com/bloxapp/ssv-network/blob/1e61c35736578d4b03bacbff9da2128ad12a5620/contracts/libraries/ProtocolLib.sol#L15
        uint64 currentNetworkFeeIndex = uint64(ssvSlot0 >> 192) + uint64(block.number - uint32(ssvSlot0)) * uint64(ssvSlot0 >> 128);

        uint256 balance = _getBalance(_ssvPayload.cluster, clusterIndex, currentNetworkFeeIndex, _ssvPayload.tokenAmount);

        i_ssvNetwork.registerValidator(
            _ssvPayload.ssvValidators[0].pubkey,
            operatorIds,
            _ssvPayload.ssvValidators[0].sharesData,
            _ssvPayload.tokenAmount,
            _ssvPayload.cluster
        );

        for (uint256 i = 1; i < _ssvPayload.ssvValidators.length;) {
            _registerValidator(
                i,
                operatorIds,
                _ssvPayload.cluster,
                clusterIndex,
                _ssvPayload.ssvValidators[i].pubkey,
                _ssvPayload.ssvValidators[i].sharesData,
                currentNetworkFeeIndex,
                balance
            );

            unchecked {++i;}
        }

        i_ssvNetwork.setFeeRecipientAddress(address(s_feeDistributor));
    }

    /// @inheritdoc IP2pSsvProxy
    function removeValidators(
        bytes[] calldata _pubkeys,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external onlyOperatorOrOwnerOrClient {
        uint256 validatorCount = _pubkeys.length;

        if (!(
            _clusters.length == validatorCount
        )) {
            revert P2pSsvProxy__AmountOfParametersError();
        }

        for (uint256 i = 0; i < validatorCount;) {
            i_ssvNetwork.removeValidator(_pubkeys[i], _operatorIds, _clusters[i]);

            unchecked {++i;}
        }
    }

    /// @inheritdoc IP2pSsvProxy
    function liquidate(
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external onlyOperatorOrOwner {
        address clusterOwner = address(this);
        uint256 validatorCount = _clusters.length;

        for (uint256 i = 0; i < validatorCount;) {
            i_ssvNetwork.liquidate(clusterOwner, _operatorIds, _clusters[i]);

            unchecked {++i;}
        }
    }

    /// @inheritdoc IP2pSsvProxy
    function reactivate(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external onlyOperatorOrOwner {
        uint256 tokenPerValidator = _tokenAmount / _clusters.length;
        uint256 validatorCount = _clusters.length;

        for (uint256 i = 0; i < validatorCount;) {
            i_ssvNetwork.reactivate(_operatorIds, tokenPerValidator, _clusters[i]);

            unchecked {++i;}
        }
    }

    /// @inheritdoc IP2pSsvProxy
    function depositToSSV(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external {
        address clusterOwner = address(this);
        uint256 validatorCount = _clusters.length;
        uint256 tokenPerValidator = _tokenAmount / validatorCount;

        for (uint256 i = 0; i < validatorCount;) {
            i_ssvNetwork.deposit(clusterOwner, _operatorIds, tokenPerValidator, _clusters[i]);

            unchecked {++i;}
        }
    }

    /// @inheritdoc IP2pSsvProxy
    function withdrawFromSSV(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external onlyOperatorOrOwner {
        uint256 tokenPerValidator = _tokenAmount / _clusters.length;
        uint256 validatorCount = _clusters.length;

        for (uint256 i = 0; i < validatorCount;) {
            i_ssvNetwork.withdraw(_operatorIds, tokenPerValidator, _clusters[i]);

            unchecked {++i;}
        }
    }

    /// @inheritdoc IP2pSsvProxy
    function withdrawSSVTokens(
        address _to,
        uint256 _amount
    ) external onlyOwner {
        i_ssvToken.transfer(_to, _amount);
    }

    /// @inheritdoc IP2pSsvProxy
    function setFeeRecipientAddress(
        address _feeRecipientAddress
    ) external onlyOperatorOrOwner {
        i_ssvNetwork.setFeeRecipientAddress(_feeRecipientAddress);
    }

    /// @notice Extract operatorIds and clusterIndex out of SsvOperator list
    /// @param _ssvOperators list of SSV operator data
    /// @return operatorIds list of SSV operator IDs, clusterIndex updated cluster index
    function _getOperatorIdsAndClusterIndex(
        SsvOperator[] calldata _ssvOperators
    ) private view returns(
        uint64[] memory operatorIds,
        uint64 clusterIndex
    ) {
        // clusterIndex updating logic reflects
        // https://github.com/bloxapp/ssv-network/blob/fe3b9b178344dd723b19792d01ab5010dfd2dcf9/contracts/modules/SSVClusters.sol#L77

        clusterIndex = 0;
        uint256 operatorCount = _ssvOperators.length;
        operatorIds = new uint64[](operatorCount);
        for (uint256 i = 0; i < operatorCount;) {
            operatorIds[i] = _ssvOperators[i].id;

            uint256 snapshot = uint256(_ssvOperators[i].snapshot);

            // see https://github.com/bloxapp/ssv-network/blob/6ae5903a5c99c8d75b59fc0d35574d87f82e5861/contracts/libraries/OperatorLib.sol#L13
            clusterIndex += uint64(snapshot >> 32) + (uint32(block.number) - uint32(snapshot)) * uint64(_ssvOperators[i].fee / 10_000_000);

            unchecked {++i;}
        }
    }

    /// @notice Calculate the balance for the subsequent cluster values in a batch
    /// @param _cluster cluster value before the 1st validator registration
    /// @param _newIndex clusterIndex value after the 1st validator registration
    /// @param _currentNetworkFeeIndex currentNetworkFeeIndex from ssvSlot0
    /// @param _tokenAmount amount of SSV tokens deposited along with the 1st validator registration
    /// @return balance updated balance after the 1st validator registration
    function _getBalance(
        ISSVNetwork.Cluster calldata _cluster,
        uint64 _newIndex,
        uint64 _currentNetworkFeeIndex,
        uint256 _tokenAmount
    ) private pure returns(uint256 balance) {
        uint256 balanceBefore = _cluster.balance + _tokenAmount;

        // see https://github.com/bloxapp/ssv-network/blob/1e61c35736578d4b03bacbff9da2128ad12a5620/contracts/libraries/ClusterLib.sol#L16
        uint64 networkFee = uint64(_currentNetworkFeeIndex - _cluster.networkFeeIndex) * _cluster.validatorCount;
        uint64 usage = (_newIndex - _cluster.index) * _cluster.validatorCount + networkFee;
        uint256 expandedUsage = uint256(usage) * 10_000_000;
        balance = expandedUsage > balanceBefore? 0 : balanceBefore - expandedUsage;
    }

    /// @notice Register subsequent validators after the 1st one
    /// @param i validator index in calldata
    /// @param _operatorIds list of SSV operator IDs
    /// @param _cluster cluster value before the 1st registration
    /// @param _clusterIndex calculated clusterIndex after the 1st registration
    /// @param _pubkey validator pubkey
    /// @param _sharesData validator SSV sharesData
    /// @param _currentNetworkFeeIndex currentNetworkFeeIndex from ssvSlot0
    /// @param _balance cluster balance after the 1st validator registration
    function _registerValidator(
        uint256 i,
        uint64[] memory _operatorIds,
        ISSVNetwork.Cluster calldata _cluster,
        uint64 _clusterIndex,
        bytes calldata _pubkey,
        bytes calldata _sharesData,
        uint64 _currentNetworkFeeIndex,
        uint256 _balance
    ) private {
        ISSVClusters.Cluster memory cluster = ISSVClusters.Cluster({
            validatorCount: uint32(_cluster.validatorCount + i),
            networkFeeIndex: _currentNetworkFeeIndex,
            index: _clusterIndex,
            active: true,
            balance: _balance
        });

        i_ssvNetwork.registerValidator(
            _pubkey,
            _operatorIds,
            _sharesData,
            0,
            cluster
        );
    }

    /// @inheritdoc IP2pSsvProxy
    function getClient() public view returns (address) {
        return s_feeDistributor.client();
    }

    /// @inheritdoc IP2pSsvProxy
    function getFactory() external view returns (address) {
        return address(i_p2pSsvProxyFactory);
    }

    /// @inheritdoc IOwnable
    function owner() public view override(OwnableBase, IOwnable) returns (address) {
        return i_p2pSsvProxyFactory.owner();
    }

    /// @inheritdoc IOwnableWithOperator
    function operator() public view returns (address) {
        return i_p2pSsvProxyFactory.operator();
    }

    /// @inheritdoc IP2pSsvProxy
    function getFeeDistributor() external view returns (address) {
        return address(s_feeDistributor);
    }

    /// @inheritdoc ERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IP2pSsvProxy).interfaceId || super.supportsInterface(interfaceId);
    }
}

File 12 of 34 : P2pStructs.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../interfaces/ssv/ISSVClusters.sol";

/// @dev 256 bit struct
/// @member basisPoints basis points (percent * 100) of EL rewards that should go to the recipient
/// @member recipient address of the recipient
struct FeeRecipient {
    uint96 basisPoints;
    address payable recipient;
}

/// @member pubkey The public key of the new validator
/// @member sharesData Encrypted shares related to the new validator
struct SsvValidator {
    bytes pubkey;
    bytes sharesData;
}

/// @member signatures BLS12-381 signatures
/// @member depositDataRoots SHA-256 hashes of the SSZ-encoded DepositData objects
struct DepositData {
    bytes[] signatures;
    bytes32[] depositDataRoots;
}

/// @dev Data from https://github.com/bloxapp/ssv-network/blob/8c945e82cc063eb8e40c467d314a470121821157/contracts/interfaces/ISSVNetworkCore.sol#L20
/// @member owner SSV operator owner
/// @member id SSV operator ID
/// @member snapshot SSV operator snapshot (should be retrieved from SSVNetwork storage)
/// @member fee SSV operator fee
struct SsvOperator {
    address owner;
    uint64 id;
    bytes32 snapshot;
    uint256 fee;
}

/// @member ssvOperators SSV operators for the cluster
/// @member ssvValidators new SSV validators to be registered in the cluster
/// @member cluster SSV cluster
/// @member tokenAmount amount of ERC-20 SSV tokens for validator registration
/// @member ssvSlot0 Slot # (uint256(keccak256("ssv.network.storage.protocol")) - 1) from SSVNetwork
struct SsvPayload {
    SsvOperator[] ssvOperators;
    SsvValidator[] ssvValidators;
    ISSVClusters.Cluster cluster;
    uint256 tokenAmount;
    bytes32 ssvSlot0;
}

File 13 of 34 : IP2pSsvProxyFactory.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../structs/P2pStructs.sol";
import "../constants/P2pConstants.sol";
import "../interfaces/ssv/ISSVNetwork.sol";
import "../access/IOwnableWithOperator.sol";

/// @dev External interface of P2pSsvProxyFactory
interface IP2pSsvProxyFactory is IOwnableWithOperator, IERC165 {

    /// @notice Emits when batch registration of validator with SSV is completed
    /// @param _proxy address of P2pSsvProxy that was used for registration and became the cluster owner
    event P2pSsvProxyFactory__RegistrationCompleted(
        address indexed _proxy
    );

    /// @notice Emits when a new P2pSsvProxy instance was deployed and initialized
    /// @param _p2pSsvProxy newly deployed P2pSsvProxy instance address
    /// @param _client client address
    /// @param _feeDistributor FeeDistributor instance address
    event P2pSsvProxyFactory__P2pSsvProxyCreated(
        address indexed _p2pSsvProxy,
        address indexed _client,
        address indexed _feeDistributor
    );

    /// @notice Emits when a new reference FeeDistributor has been set
    /// @param _referenceFeeDistributor new reference FeeDistributor address
    event P2pSsvProxyFactory__ReferenceFeeDistributorSet(
        address indexed _referenceFeeDistributor
    );

    /// @notice Emits when a new value for ssvPerEthExchangeRateDividedByWei has been set
    /// @param _ssvPerEthExchangeRateDividedByWei new value for ssvPerEthExchangeRateDividedByWei
    event P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiSet(
        uint112 _ssvPerEthExchangeRateDividedByWei
    );

    /// @notice Emits when a new value for maximum amount of SSV tokens per validator has been set
    /// @param _maxSsvTokenAmountPerValidator new value for maximum amount of SSV tokens per validator
    event P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorSet(
        uint112 _maxSsvTokenAmountPerValidator
    );

    /// @notice Emits when a new reference P2pSsvProxy has been set
    /// @param _referenceP2pSsvProxy new reference P2pSsvProxy address
    event P2pSsvProxyFactory__ReferenceP2pSsvProxySet(
        address indexed _referenceP2pSsvProxy
    );

    /// @notice Emits when new selectors were allowed for clients
    /// @param _selectors newly allowed selectors
    event P2pSsvProxyFactory__AllowedSelectorsForClientSet(
        bytes4[] _selectors
    );

    /// @notice Emits when selectors were disallowed for clients
    /// @param _selectors disallowed selectors
    event P2pSsvProxyFactory__AllowedSelectorsForClientRemoved(
        bytes4[] _selectors
    );

    /// @notice Emits when new selectors were allowed for operator
    /// @param _selectors newly allowed selectors
    event P2pSsvProxyFactory__AllowedSelectorsForOperatorSet(
        bytes4[] _selectors
    );

    /// @notice Emits when selectors were disallowed for operator
    /// @param _selectors disallowed selectors
    event P2pSsvProxyFactory__AllowedSelectorsForOperatorRemoved(
        bytes4[] _selectors
    );

    /// @notice Emits when new SSV operator owner addresses have been allowed
    /// @param _allowedSsvOperatorOwners newly allowed SSV operator owner addresses
    event P2pSsvProxyFactory__AllowedSsvOperatorOwnersSet(
        address[] _allowedSsvOperatorOwners
    );

    /// @notice Emits when some SSV operator owner addresses have been removed from the allowlist
    /// @param _allowedSsvOperatorOwners disallowed SSV operator owner addresses
    event P2pSsvProxyFactory__AllowedSsvOperatorOwnersRemoved(
        address[] _allowedSsvOperatorOwners
    );

    /// @notice Emits when new SSV operator IDs have been set to the given SSV operator owner
    /// @param _ssvOperatorOwner SSV operator owner
    event P2pSsvProxyFactory__SsvOperatorIdsSet(
        address indexed _ssvOperatorOwner,
        uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] _operatorIds
    );

    /// @notice Emits when operator IDs list has been cleared for the given SSV operator owner
    /// @param _ssvOperatorOwner SSV operator owner
    event P2pSsvProxyFactory__SsvOperatorIdsCleared(
        address indexed _ssvOperatorOwner
    );

    /// @notice Set Exchange rate between SSV and ETH set by P2P.
    /// @dev (If 1 SSV = 0.007539 ETH, it should be 0.007539 * 10^18 = 7539000000000000).
    /// @param _ssvPerEthExchangeRateDividedByWei Exchange rate
    function setSsvPerEthExchangeRateDividedByWei(uint112 _ssvPerEthExchangeRateDividedByWei) external;

    /// @notice Set Maximum amount of SSV tokens per validator that is allowed for client to deposit during `depositEthAndRegisterValidators`
    /// @param _maxSsvTokenAmountPerValidator Maximum amount of SSV tokens per validator
    function setMaxSsvTokenAmountPerValidator(uint112 _maxSsvTokenAmountPerValidator) external;

    /// @notice Set template to be used for new P2pSsvProxy instances
    /// @param _referenceP2pSsvProxy template to be used for new P2pSsvProxy instances
    function setReferenceP2pSsvProxy(address _referenceP2pSsvProxy) external;

    /// @notice Allow selectors (function signatures) for clients to call on SSVNetwork via P2pSsvProxy
    /// @param _selectors selectors (function signatures) to allow for clients
    function setAllowedSelectorsForClient(bytes4[] calldata _selectors) external;

    /// @notice Disallow selectors (function signatures) for clients to call on SSVNetwork via P2pSsvProxy
    /// @param _selectors selectors (function signatures) to disallow for clients
    function removeAllowedSelectorsForClient(bytes4[] calldata _selectors) external;

    /// @notice Allow selectors (function signatures) for P2P operator to call on SSVNetwork via P2pSsvProxy
    /// @param _selectors selectors (function signatures) to allow for P2P operator
    function setAllowedSelectorsForOperator(bytes4[] calldata _selectors) external;

    /// @notice Disallow selectors (function signatures) for P2P operator to call on SSVNetwork via P2pSsvProxy
    /// @param _selectors selectors (function signatures) to disallow for P2P operator
    function removeAllowedSelectorsForOperator(bytes4[] calldata _selectors) external;

    /// @notice Set template to be used for new FeeDistributor instances
    /// @param _referenceFeeDistributor template to be used for new FeeDistributor instances
    function setReferenceFeeDistributor(
        address _referenceFeeDistributor
    ) external;

    /// @notice Allow addresses of SSV operator owners (both P2P and partners)
    /// @param _allowedSsvOperatorOwners addresses of SSV operator owners to allow
    function setAllowedSsvOperatorOwners(
        address[] calldata _allowedSsvOperatorOwners
    ) external;

    /// @notice Disallow addresses of SSV operator owners (both P2P and partners)
    /// @param _allowedSsvOperatorOwnersToRemove addresses of SSV operator owners to disallow
    function removeAllowedSsvOperatorOwners(
        address[] calldata _allowedSsvOperatorOwnersToRemove
    ) external;

    /// @notice Set own SSV operator IDs list
    /// @dev To be called by SSV operator owner
    /// @param _operatorIds SSV operator IDs list
    function setSsvOperatorIds(
        uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] calldata _operatorIds
    ) external;

    /// @notice Set SSV operator IDs list for a SSV operator owner
    /// @dev To be called by P2P
    /// @param _operatorIds SSV operator IDs list
    /// @param _ssvOperatorOwner SSV operator owner
    function setSsvOperatorIds(
        uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] calldata _operatorIds,
        address _ssvOperatorOwner
    ) external;

    /// @notice Clear own SSV operator IDs list
    /// @dev To be called by SSV operator owner
    function clearSsvOperatorIds() external;

    /// @notice Clear SSV operator IDs list for a SSV operator owner
    /// @dev To be called by P2P
    /// @param _ssvOperatorOwner SSV operator owner
    function clearSsvOperatorIds(
        address _ssvOperatorOwner
    ) external;

    /// @notice Computes the address of a P2pSsvProxy created by `_createP2pSsvProxy` function
    /// @dev P2pSsvProxy instances are guaranteed to have the same address if _feeDistributorInstance is the same
    /// @param _feeDistributorInstance The address of FeeDistributor instance
    /// @return address client P2pSsvProxy instance that will be or has been deployed
    function predictP2pSsvProxyAddress(
        address _feeDistributorInstance
    ) external view returns (address);

    /// @notice Deploy P2pSsvProxy instance if not deployed before
    /// @param _feeDistributorInstance The address of FeeDistributor instance
    /// @return p2pSsvProxyInstance client P2pSsvProxy instance that has been deployed
    function createP2pSsvProxy(
        address _feeDistributorInstance
    ) external returns(address p2pSsvProxyInstance);

    /// @notice Batch deposit ETH and register validators with SSV (up to 50, calldata size is the limit)
    /// @param _depositData signatures and depositDataRoots from Beacon deposit data
    /// @param _withdrawalCredentialsAddress address for 0x01 withdrawal credentials from Beacon deposit data (1 for the batch)
    /// @param _ssvPayload a stuct with data necessary for SSV registration (see `SsvPayload` struct for details)
    /// @param _clientConfig address and basis points (percent * 100) of the client (for FeeDistributor)
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer (for FeeDistributor)
    /// @return p2pSsvProxy client P2pSsvProxy instance that became the SSV cluster owner
    function depositEthAndRegisterValidators(
        DepositData calldata _depositData,
        address _withdrawalCredentialsAddress,

        SsvPayload calldata _ssvPayload,

        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external payable returns (address p2pSsvProxy);

    /// @notice Register validators with SSV (up to 60, calldata size is the limit) without ETH deposits
    /// @param _ssvPayload a stuct with data necessary for SSV registration (see `SsvPayload` struct for details)
    /// @param _clientConfig address and basis points (percent * 100) of the client (for FeeDistributor)
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer (for FeeDistributor)
    /// @return p2pSsvProxy client P2pSsvProxy instance that became the SSV cluster owner
    function registerValidators(
        SsvPayload calldata _ssvPayload,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external payable returns (address p2pSsvProxy);

    /// @notice Returns the FeeDistributorFactory address
    /// @return FeeDistributorFactory address
    function getFeeDistributorFactory() external view returns (address);

    /// @notice A list of addresses of the deployed client P2pSsvProxy instances by client address
    /// @param _client client address
    /// @return A list of addresses of the deployed client P2pSsvProxy instances
    function getAllClientP2pSsvProxies(
        address _client
    ) external view returns (address[] memory);

    /// @notice Returns a list of all ever deployed client P2pSsvProxy instances
    /// @return a list of all ever deployed client P2pSsvProxy instances
    function getAllP2pSsvProxies() external view returns (address[] memory);

    /// @notice Returns if a certain selector (function signature) is allowed for clients to call on SSVNetwork via P2pSsvProxy
    /// @return True if allowed
    function isClientSelectorAllowed(bytes4 _selector) external view returns (bool);

    /// @notice Returns if a certain selector (function signature) is allowed for a P2P operator to call on SSVNetwork via P2pSsvProxy
    /// @param _selector selector (function signature)
    /// @return True if allowed
    function isOperatorSelectorAllowed(bytes4 _selector) external view returns (bool);

    /// @notice Returns SSV operator IDs list by operator owner address
    /// @param _ssvOperatorOwner operator owner address
    /// @return SSV operator IDs list
    function getAllowedSsvOperatorIds(address _ssvOperatorOwner) external view returns (uint64[MAX_ALLOWED_SSV_OPERATOR_IDS] memory);

    /// @notice Returns a set of addresses of SSV operator owners (both P2P and partners)
    /// @return a set of addresses of SSV operator owners (both P2P and partners)
    function getAllowedSsvOperatorOwners() external view returns (address[] memory);

    /// @notice Returns a template set by P2P to be used for new FeeDistributor instances
    /// @return a template set by P2P to be used for new FeeDistributor instances
    function getReferenceFeeDistributor() external view returns (address);

    /// @notice Returns a template set by P2P to be used for new P2pSsvProxy instances
    /// @return a template set by P2P to be used for new P2pSsvProxy instances
    function getReferenceP2pSsvProxy() external view returns (address);

    /// @notice Returns exchange rate between SSV and ETH set by P2P
    /// @dev (If 1 SSV = 0.007539 ETH, it should be 0.007539 * 10^18 = 7539000000000000).
    /// Only used during validator registration without ETH deposits to cover SSV token costs with client ETH.
    /// SSV tokens exchanged with this rate cannot be withdrawn by the client.
    /// P2P is willing to tolarate potential discrepancies with the market exchange rate for the sake of simplicity.
    /// The client agrees to this rate when calls `registerValidators` function.
    /// @return exchange rate between SSV and ETH set by P2P
    function getSsvPerEthExchangeRateDividedByWei() external view returns (uint112);

    /// @notice Returns the maximum amount of SSV tokens per validator that is allowed for client to deposit during `depositEthAndRegisterValidators`
    /// @return maximum amount of SSV tokens per validator
    function getMaxSsvTokenAmountPerValidator() external view returns (uint112);

    /// @notice Returns needed amount of ETH to cover SSV fees by SSV token amount
    /// @param _tokenAmount SSV token amount
    /// @return needed amount of ETH to cover SSV fees
    function getNeededAmountOfEtherToCoverSsvFees(uint256 _tokenAmount) external view returns (uint256);
}

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

pragma solidity 0.8.18;

/**
 * @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 15 of 34 : IOwnable.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

/**
 * @dev External interface of Ownable.
 */
interface IOwnable {
    /**
     * @dev Returns the address of the current owner.
     */
    function owner() external view returns (address);
}

File 16 of 34 : OwnableTokenRecoverer.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, Lido <[email protected]>
// SPDX-License-Identifier: MIT

// https://github.com/lidofinance/lido-otc-seller/blob/master/contracts/lib/AssetRecoverer.sol
pragma solidity 0.8.18;

import "./TokenRecoverer.sol";
import "../access/OwnableBase.sol";

/// @title Token Recoverer with public functions callable by assetAccessingAddress
/// @notice Recover ERC20, ERC721 and ERC1155 from a derived contract
abstract contract OwnableTokenRecoverer is TokenRecoverer, OwnableBase {
    // Functions

    /**
     * @notice transfer an ERC20 token from this contract
     * @dev `SafeERC20.safeTransfer` doesn't always return a bool
     * as it performs an internal `require` check
     * @param _token address of the ERC20 token
     * @param _recipient address to transfer the tokens to
     * @param _amount amount of tokens to transfer
     */
    function transferERC20(
        address _token,
        address _recipient,
        uint256 _amount
    ) external onlyOwner {
        _transferERC20(_token, _recipient, _amount);
    }

    /**
     * @notice transfer an ERC721 token from this contract
     * @dev `IERC721.safeTransferFrom` doesn't always return a bool
     * as it performs an internal `require` check
     * @param _token address of the ERC721 token
     * @param _recipient address to transfer the token to
     * @param _tokenId id of the individual token
     */
    function transferERC721(
        address _token,
        address _recipient,
        uint256 _tokenId
    ) external onlyOwner {
        _transferERC721(_token, _recipient, _tokenId);
    }

    /**
     * @notice transfer an ERC1155 token from this contract
     * @dev see `AssetRecoverer`
     * @param _token address of the ERC1155 token that is being recovered
     * @param _recipient address to transfer the token to
     * @param _tokenId id of the individual token to transfer
     * @param _amount amount of tokens to transfer
     * @param _data data to transfer along
     */
    function transferERC1155(
        address _token,
        address _recipient,
        uint256 _tokenId,
        uint256 _amount,
        bytes calldata _data
    ) external onlyOwner {
        _transferERC1155(_token, _recipient, _tokenId, _amount, _data);
    }
}

File 17 of 34 : AssetRecoverer.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, Lido <[email protected]>
// SPDX-License-Identifier: MIT

// https://github.com/lidofinance/lido-otc-seller/blob/master/contracts/lib/AssetRecoverer.sol
pragma solidity 0.8.18;

import "./TokenRecoverer.sol";

/**
* @notice could not transfer ether
* @param _recipient address to transfer ether to
* @param _amount amount of ether to transfer
*/
error AssetRecoverer__TransferFailed(address _recipient, uint256 _amount);


/// @title Asset Recoverer
/// @notice Recover ether, ERC20, ERC721 and ERC1155 from a derived contract
abstract contract AssetRecoverer is TokenRecoverer {
    event EtherTransferred(address indexed _recipient, uint256 _amount);

    /**
     * @notice transfers ether from this contract
     * @dev using `address.call` is safer to transfer to other contracts
     * @param _recipient address to transfer ether to
     * @param _amount amount of ether to transfer
     */
    function _transferEther(address _recipient, uint256 _amount) internal virtual burnDisallowed(_recipient) {
        (bool success, ) = _recipient.call{value: _amount}("");
        if (!success) {
            revert AssetRecoverer__TransferFailed(_recipient, _amount);
        }
        emit EtherTransferred(_recipient, _amount);
    }
}

File 18 of 34 : Ownable2Step.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "./Ownable.sol";

/**
* @notice caller must be pendingOwner
*/
error Ownable2Step__CallerNotNewOwner();

/**
* @notice new owner address should be different from the current owner
*/
error Ownable2Step__NewOwnerShouldNotBeCurrentOwner();

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private s_pendingOwner;

    /**
     * @dev Emits in transferOwnership (start of the transfer)
     * @param _previousOwner address of the previous owner
     * @param _newOwner address of the new owner
     */
    event OwnershipTransferStarted(address indexed _previousOwner, address indexed _newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return s_pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        address currentOwner = owner();
        if (newOwner == currentOwner) {
            revert Ownable2Step__NewOwnerShouldNotBeCurrentOwner();
        }

        s_pendingOwner = newOwner;
        emit OwnershipTransferStarted(currentOwner, newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete s_pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() external {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert Ownable2Step__CallerNotNewOwner();
        }
        _transferOwnership(sender);
    }
}

File 19 of 34 : IOwnableWithOperator.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "./IOwnable.sol";

/**
 * @dev Ownable with an additional role of operator
 */
interface IOwnableWithOperator is IOwnable {
    /**
     * @dev Returns the current operator.
     */
    function operator() external view returns (address);
}

File 20 of 34 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity 0.8.18;

/**
 * @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 21 of 34 : ERC165Checker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.2) (utils/introspection/ERC165Checker.sol)

pragma solidity 0.8.18;

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
            _supportsERC165Interface(account, type(IERC165).interfaceId) &&
            !_supportsERC165Interface(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) && _supportsERC165Interface(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] = _supportsERC165Interface(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 (!_supportsERC165Interface(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}.
     * Interface identification is specified in ERC-165.
     */
    function _supportsERC165Interface(address account, bytes4 interfaceId) private 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 22 of 34 : P2pConstants.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

/// @dev Collateral size of 1 validator
uint256 constant COLLATERAL = 32 ether;

/// @dev Maximum number of SSV operator IDs per SSV operator owner address supported simultaniously by P2pSsvProxyFactory
uint256 constant MAX_ALLOWED_SSV_OPERATOR_IDS = 8;

File 23 of 34 : ISSVNetwork.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;
import "./ISSVClusters.sol";

/// @dev https://github.com/bloxapp/ssv-network/blob/8c945e82cc063eb8e40c467d314a470121821157/contracts/interfaces/ISSVNetwork.sol
interface ISSVNetwork is ISSVClusters {
    function setFeeRecipientAddress(address feeRecipientAddress) external;
}

File 24 of 34 : IP2pSsvProxy.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../structs/P2pStructs.sol";
import "../access/IOwnableWithOperator.sol";
import "../interfaces/ssv/ISSVNetwork.sol";

/// @dev External interface of P2pSsvProxy declared to support ERC165 detection.
interface IP2pSsvProxy is IOwnableWithOperator, IERC165 {

    /// @notice Emits when P2pSsvProxy instance is initialized
    /// @param _feeDistributor FeeDistributor instance that determines the identity of this P2pSsvProxy instance
    event P2pSsvProxy__Initialized(
        address indexed _feeDistributor
    );

    /// @notice Emits when the function was called successfully on SSVNetwork via fallback
    /// @param _caller caller of P2pSsvProxy
    /// @param _selector selector of the function from SSVNetwork
    event P2pSsvProxy__SuccessfullyCalledViaFallback(
        address indexed _caller,
        bytes4 indexed _selector
    );

    /// @notice Initialize the P2pSsvProxy instance
    /// @dev Should only be called by P2pSsvProxyFactory
    /// @param _feeDistributor FeeDistributor instance that determines the identity of this P2pSsvProxy instance
    function initialize(
        address _feeDistributor
    ) external;

    /// @notice Register a batch of validators with SSV
    /// @dev Should be called by P2pSsvProxyFactory only
    /// @param _ssvPayload struct with the SSV data required for registration
    function registerValidators(
        SsvPayload calldata _ssvPayload
    ) external;

    /// @notice Remove a batch of validators from SSV
    /// @dev Can be called either by P2P or by the client
    /// @param _pubkeys validator pubkeys
    /// @param _operatorIds SSV operator IDs
    /// @param _clusters SSV clusters, each of which should correspond to pubkey and operator IDs
    function removeValidators(
        bytes[] calldata _pubkeys,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external;

    /// @notice Liquidate SSV clusters
    /// @dev Should be called by P2P only.
    /// This function is just batching calls for convenience. It's always possible to call the same function on SSVNetwork via fallback
    /// @param _operatorIds SSV operator IDs
    /// @param _clusters SSV clusters
    function liquidate(
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external;

    /// @notice Reactivate SSV clusters
    /// @dev Should be called by P2P only
    /// This function is just batching calls for convenience. It's always possible to call the same function on SSVNetwork via fallback
    /// @param _tokenAmount SSV token amount to be deposited for reactivation
    /// @param _operatorIds SSV operator IDs
    /// @param _clusters SSV clusters
    function reactivate(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external;

    /// @notice Deposit SSV tokens to SSV clusters
    /// @dev Can be called by anyone
    /// This function is just batching calls for convenience. It's possible to call the same function on SSVNetwork directly
    /// @param _tokenAmount SSV token amount to be deposited
    /// @param _operatorIds SSV operator IDs
    /// @param _clusters SSV clusters
    function depositToSSV(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external;

    /// @notice Withdraw SSV tokens from SSV clusters to this contract
    /// @dev Should be called by P2P only
    /// This function is just batching calls for convenience. It's always possible to call the same function on SSVNetwork via fallback
    /// @param _tokenAmount SSV token amount to be withdrawn
    /// @param _operatorIds SSV operator IDs
    /// @param _clusters SSV clusters
    function withdrawFromSSV(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external;

    /// @notice Withdraw SSV tokens from this contract to the given address
    /// @dev Should be called by P2P only
    /// @param _to destination address
    /// @param _amount SSV token amount to be withdrawn
    function withdrawSSVTokens(
        address _to,
        uint256 _amount
    ) external;

    /// @notice Set a new fee recipient address for this contract (cluster owner)
    /// @dev Should be called by P2P only.
    /// Another FeeDistributor instance can become the fee recipient (e.g. if service percentages change).
    /// Client address itself can become the fee recipient (e.g. if service percentage becomes zero due to some promo).
    /// It's fine for P2P to determine the fee recipient since P2P is paying SSV tokens and EL rewards are a way to compansate for them.
    /// Other operators are compansated via SSV tokens paid by P2P.
    /// @param _feeRecipientAddress fee recipient address to set
    function setFeeRecipientAddress(
        address _feeRecipientAddress
    ) external;

    /// @notice Returns the client address
    /// @return address client address
    function getClient() external view returns (address);

    /// @notice Returns the factory address
    /// @return address factory address
    function getFactory() external view returns (address);

    /// @notice Returns the address of FeeDistributor instance accociated with this contract
    /// @return FeeDistributor instance address
    function getFeeDistributor() external view returns (address);
}

File 25 of 34 : ISSVClusters.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.18;

/// @dev https://github.com/bloxapp/ssv-network/blob/8c945e82cc063eb8e40c467d314a470121821157/contracts/interfaces/ISSVNetworkCore.sol
interface ISSVClusters {
    /// @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;
    }


    /// @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 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;

    /**************************/
    /* 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;

    /**
     * @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);

    event ClusterLiquidated(address indexed owner, uint64[] operatorIds, Cluster cluster);

    event ClusterReactivated(address indexed owner, uint64[] operatorIds, Cluster cluster);

    event ClusterWithdrawn(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster);

    event ClusterDeposited(address indexed owner, uint64[] operatorIds, uint256 value, Cluster cluster);
}

File 26 of 34 : TokenRecoverer.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, Lido <[email protected]>
// SPDX-License-Identifier: MIT

// https://github.com/lidofinance/lido-otc-seller/blob/master/contracts/lib/AssetRecoverer.sol
pragma solidity 0.8.18;

import {IERC20} from "../@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "../@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC1155} from "../@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {SafeERC20} from "../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
* @notice prevents burn for transfer functions
* @dev _recipient should not be a zero address
*/
error TokenRecoverer__NoBurn();


/// @title Token Recoverer
/// @notice Recover ERC20, ERC721 and ERC1155 from a derived contract
abstract contract TokenRecoverer {
    using SafeERC20 for IERC20;

    event ERC20Transferred(address indexed _token, address indexed _recipient, uint256 _amount);
    event ERC721Transferred(address indexed _token, address indexed _recipient, uint256 _tokenId);
    event ERC1155Transferred(address indexed _token, address indexed _recipient, uint256 _tokenId, uint256 _amount, bytes _data);

    /**
     * @notice prevents burn for transfer functions
     * @dev checks for zero address and reverts if true
     * @param _recipient address of the transfer recipient
     */
    modifier burnDisallowed(address _recipient) {
        if (_recipient == address(0)) {
            revert TokenRecoverer__NoBurn();
        }
        _;
    }

    /**
     * @notice transfer an ERC20 token from this contract
     * @dev `SafeERC20.safeTransfer` doesn't always return a bool
     * as it performs an internal `require` check
     * @param _token address of the ERC20 token
     * @param _recipient address to transfer the tokens to
     * @param _amount amount of tokens to transfer
     */
    function _transferERC20(
        address _token,
        address _recipient,
        uint256 _amount
    ) internal virtual burnDisallowed(_recipient) {
        IERC20(_token).safeTransfer(_recipient, _amount);
        emit ERC20Transferred(_token, _recipient, _amount);
    }

    /**
     * @notice transfer an ERC721 token from this contract
     * @dev `IERC721.safeTransferFrom` doesn't always return a bool
     * as it performs an internal `require` check
     * @param _token address of the ERC721 token
     * @param _recipient address to transfer the token to
     * @param _tokenId id of the individual token
     */
    function _transferERC721(
        address _token,
        address _recipient,
        uint256 _tokenId
    ) internal virtual burnDisallowed(_recipient) {
        IERC721(_token).transferFrom(address(this), _recipient, _tokenId);
        emit ERC721Transferred(_token, _recipient, _tokenId);
    }

    /**
     * @notice transfer an ERC1155 token from this contract
     * @dev `IERC1155.safeTransferFrom` doesn't always return a bool
     * as it performs an internal `require` check
     * @param _token address of the ERC1155 token that is being recovered
     * @param _recipient address to transfer the token to
     * @param _tokenId id of the individual token to transfer
     * @param _amount amount of tokens to transfer
     * @param _data data to transfer along
     */
    function _transferERC1155(
        address _token,
        address _recipient,
        uint256 _tokenId,
        uint256 _amount,
        bytes calldata _data
    ) internal virtual burnDisallowed(_recipient) {
        IERC1155(_token).safeTransferFrom(address(this), _recipient, _tokenId, _amount, _data);
        emit ERC1155Transferred(_token, _recipient, _tokenId, _amount, _data);
    }
}

File 27 of 34 : OwnableBase.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "../@openzeppelin/contracts/utils/Context.sol";
import "./IOwnable.sol";

/**
* @notice Throws if called by any account other than the owner.
* @param _caller address of the caller
* @param _owner address of the owner
*/
error OwnableBase__CallerNotOwner(address _caller, address _owner);

/**
 * @dev minimalistic version of OpenZeppelin's Ownable.
 * The owner is abstract and is not persisted in storage.
 * Needs to be overridden in a child contract.
 */
abstract contract OwnableBase is Context, IOwnable {

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        address caller = _msgSender();
        address currentOwner = owner();

        if (currentOwner != caller) {
            revert OwnableBase__CallerNotOwner(caller, currentOwner);
        }
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     * Needs to be overridden in a child contract.
     */
    function owner() public view virtual override returns (address);
}

File 28 of 34 : Ownable.sol
// SPDX-FileCopyrightText: 2023 P2P Validator <[email protected]>, OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
// SPDX-License-Identifier: MIT

pragma solidity 0.8.18;

import "./OwnableBase.sol";

/**
* @notice _newOwner cannot be a zero address
*/
error Ownable__NewOwnerIsZeroAddress();

/**
 * @dev OpenZeppelin's Ownable with modifier onlyOwner extracted to OwnableBase
 * and removed `renounceOwnership`
 */
abstract contract Ownable is OwnableBase {

    /**
     * @dev Emits when the owner has been changed.
     * @param _previousOwner address of the previous owner
     * @param _newOwner address of the new owner
     */
    event OwnershipTransferred(address indexed _previousOwner, address indexed _newOwner);

    address private s_owner;

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual override returns (address) {
        return s_owner;
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     * @param _newOwner address of the new owner
     */
    function transferOwnership(address _newOwner) external virtual onlyOwner {
        if (_newOwner == address(0)) {
            revert Ownable__NewOwnerIsZeroAddress();
        }
        _transferOwnership(_newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     * @param _newOwner address of the new owner
     */
    function _transferOwnership(address _newOwner) internal virtual {
        address oldOwner = s_owner;
        s_owner = _newOwner;
        emit OwnershipTransferred(oldOwner, _newOwner);
    }
}

File 29 of 34 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity 0.8.18;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 30 of 34 : IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol)

pragma solidity 0.8.18;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

File 31 of 34 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity 0.8.18;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 32 of 34 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity 0.8.18;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 33 of 34 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity 0.8.18;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 34 of 34 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity 0.8.18;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_feeDistributorFactory","type":"address"},{"internalType":"address","name":"_referenceFeeDistributor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"Access__AddressNeitherOperatorNorOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"Access__CallerNeitherOperatorNorOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"Access__SameOperator","type":"error"},{"inputs":[],"name":"Access__ZeroNewOperator","type":"error"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"AssetRecoverer__TransferFailed","type":"error"},{"inputs":[],"name":"Ownable2Step__CallerNotNewOwner","type":"error"},{"inputs":[],"name":"Ownable2Step__NewOwnerShouldNotBeCurrentOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"OwnableBase__CallerNotOwner","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__CannotRemoveZeroAllowedSsvOperatorOwners","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__CannotRemoveZeroSelectors","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__CannotSetZeroAllowedSsvOperatorOwners","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__CannotSetZeroSelectors","type":"error"},{"inputs":[{"internalType":"uint256","name":"_ssvValidatorsLength","type":"uint256"},{"internalType":"uint256","name":"_signaturesLength","type":"uint256"},{"internalType":"uint256","name":"_depositDataRootsLength","type":"uint256"}],"name":"P2pSsvProxyFactory__DepositDataArraysShouldHaveTheSameLength","type":"error"},{"inputs":[{"internalType":"uint64","name":"_ssvOperatorId","type":"uint64"}],"name":"P2pSsvProxyFactory__DuplicateIdsNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"_ssvOperatorOwner","type":"address"},{"internalType":"uint64","name":"_ssvOperatorId1","type":"uint64"},{"internalType":"uint64","name":"_ssvOperatorId2","type":"uint64"}],"name":"P2pSsvProxyFactory__DuplicateOperatorOwnersNotAllowed","type":"error"},{"inputs":[{"internalType":"uint256","name":"_actualEthValue","type":"uint256"}],"name":"P2pSsvProxyFactory__EthValueMustBe32TimesValidatorCount","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorExceeded","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorNotSet","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorOutOfRange","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"}],"name":"P2pSsvProxyFactory__NotAllowedSsvOperatorOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"_needed","type":"uint256"},{"internalType":"uint256","name":"_paid","type":"uint256"}],"name":"P2pSsvProxyFactory__NotEnoughEtherPaidToCoverSsvFees","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"P2pSsvProxyFactory__NotFeeDistributor","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"P2pSsvProxyFactory__NotFeeDistributorFactory","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"P2pSsvProxyFactory__NotP2pSsvProxy","type":"error"},{"inputs":[{"internalType":"uint64","name":"_operatorId","type":"uint64"},{"internalType":"address","name":"_passedOwner","type":"address"},{"internalType":"address","name":"_actualOwner","type":"address"}],"name":"P2pSsvProxyFactory__SsvOperatorIdDoesNotBelongToOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_ssvOperatorOwner","type":"address"},{"internalType":"uint64","name":"_ssvOperatorId","type":"uint64"}],"name":"P2pSsvProxyFactory__SsvOperatorNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"_ssvOperatorOwner","type":"address"}],"name":"P2pSsvProxyFactory__SsvOperatorOwnerAlreadyExists","type":"error"},{"inputs":[{"internalType":"address","name":"_ssvOperatorOwner","type":"address"}],"name":"P2pSsvProxyFactory__SsvOperatorOwnerDoesNotExist","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiNotSet","type":"error"},{"inputs":[],"name":"P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiOutOfRange","type":"error"},{"inputs":[],"name":"TokenRecoverer__NoBurn","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"_data","type":"bytes"}],"name":"ERC1155Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"ERC20Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ERC721Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"EtherTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOperator","type":"address"},{"indexed":true,"internalType":"address","name":"_newOperator","type":"address"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"P2pSsvProxyFactory__AllowedSelectorsForClientRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"P2pSsvProxyFactory__AllowedSelectorsForClientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"P2pSsvProxyFactory__AllowedSelectorsForOperatorRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"P2pSsvProxyFactory__AllowedSelectorsForOperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_allowedSsvOperatorOwners","type":"address[]"}],"name":"P2pSsvProxyFactory__AllowedSsvOperatorOwnersRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"_allowedSsvOperatorOwners","type":"address[]"}],"name":"P2pSsvProxyFactory__AllowedSsvOperatorOwnersSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"_maxSsvTokenAmountPerValidator","type":"uint112"}],"name":"P2pSsvProxyFactory__MaxSsvTokenAmountPerValidatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_p2pSsvProxy","type":"address"},{"indexed":true,"internalType":"address","name":"_client","type":"address"},{"indexed":true,"internalType":"address","name":"_feeDistributor","type":"address"}],"name":"P2pSsvProxyFactory__P2pSsvProxyCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_referenceFeeDistributor","type":"address"}],"name":"P2pSsvProxyFactory__ReferenceFeeDistributorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_referenceP2pSsvProxy","type":"address"}],"name":"P2pSsvProxyFactory__ReferenceP2pSsvProxySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_proxy","type":"address"}],"name":"P2pSsvProxyFactory__RegistrationCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_ssvOperatorOwner","type":"address"}],"name":"P2pSsvProxyFactory__SsvOperatorIdsCleared","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_ssvOperatorOwner","type":"address"},{"indexed":false,"internalType":"uint64[8]","name":"_operatorIds","type":"uint64[8]"}],"name":"P2pSsvProxyFactory__SsvOperatorIdsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"_ssvPerEthExchangeRateDividedByWei","type":"uint112"}],"name":"P2pSsvProxyFactory__SsvPerEthExchangeRateDividedByWeiSet","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOperator","type":"address"}],"name":"changeOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"checkOperatorOrOwner","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ssvOperatorOwner","type":"address"}],"name":"clearSsvOperatorIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearSsvOperatorIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeDistributorInstance","type":"address"}],"name":"createP2pSsvProxy","outputs":[{"internalType":"address","name":"p2pSsvProxyInstance","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"bytes32[]","name":"depositDataRoots","type":"bytes32[]"}],"internalType":"struct DepositData","name":"_depositData","type":"tuple"},{"internalType":"address","name":"_withdrawalCredentialsAddress","type":"address"},{"components":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"bytes32","name":"snapshot","type":"bytes32"},{"internalType":"uint256","name":"fee","type":"uint256"}],"internalType":"struct SsvOperator[]","name":"ssvOperators","type":"tuple[]"},{"components":[{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"sharesData","type":"bytes"}],"internalType":"struct SsvValidator[]","name":"ssvValidators","type":"tuple[]"},{"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 ISSVClusters.Cluster","name":"cluster","type":"tuple"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"bytes32","name":"ssvSlot0","type":"bytes32"}],"internalType":"struct SsvPayload","name":"_ssvPayload","type":"tuple"},{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct FeeRecipient","name":"_clientConfig","type":"tuple"},{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct FeeRecipient","name":"_referrerConfig","type":"tuple"}],"name":"depositEthAndRegisterValidators","outputs":[{"internalType":"address","name":"p2pSsvProxy","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"dismissOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_client","type":"address"}],"name":"getAllClientP2pSsvProxies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllP2pSsvProxies","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ssvOperatorOwner","type":"address"}],"name":"getAllowedSsvOperatorIds","outputs":[{"internalType":"uint64[8]","name":"","type":"uint64[8]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowedSsvOperatorOwners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeDistributorFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxSsvTokenAmountPerValidator","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"getNeededAmountOfEtherToCoverSsvFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReferenceFeeDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReferenceP2pSsvProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSsvPerEthExchangeRateDividedByWei","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"isClientSelectorAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"isOperatorSelectorAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_feeDistributorInstance","type":"address"}],"name":"predictP2pSsvProxyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint64","name":"id","type":"uint64"},{"internalType":"bytes32","name":"snapshot","type":"bytes32"},{"internalType":"uint256","name":"fee","type":"uint256"}],"internalType":"struct SsvOperator[]","name":"ssvOperators","type":"tuple[]"},{"components":[{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"sharesData","type":"bytes"}],"internalType":"struct SsvValidator[]","name":"ssvValidators","type":"tuple[]"},{"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 ISSVClusters.Cluster","name":"cluster","type":"tuple"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"bytes32","name":"ssvSlot0","type":"bytes32"}],"internalType":"struct SsvPayload","name":"_ssvPayload","type":"tuple"},{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct FeeRecipient","name":"_clientConfig","type":"tuple"},{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct FeeRecipient","name":"_referrerConfig","type":"tuple"}],"name":"registerValidators","outputs":[{"internalType":"address","name":"p2pSsvProxy","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"removeAllowedSelectorsForClient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"removeAllowedSelectorsForOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_allowedSsvOperatorOwnersToRemove","type":"address[]"}],"name":"removeAllowedSsvOperatorOwners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"setAllowedSelectorsForClient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"setAllowedSelectorsForOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_allowedSsvOperatorOwners","type":"address[]"}],"name":"setAllowedSsvOperatorOwners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint112","name":"_maxSsvTokenAmountPerValidator","type":"uint112"}],"name":"setMaxSsvTokenAmountPerValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_referenceFeeDistributor","type":"address"}],"name":"setReferenceFeeDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_referenceP2pSsvProxy","type":"address"}],"name":"setReferenceP2pSsvProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[8]","name":"_operatorIds","type":"uint64[8]"},{"internalType":"address","name":"_ssvOperatorOwner","type":"address"}],"name":"setSsvOperatorIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64[8]","name":"_operatorIds","type":"uint64[8]"}],"name":"setSsvOperatorIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint112","name":"_ssvPerEthExchangeRateDividedByWei","type":"uint112"}],"name":"setSsvPerEthExchangeRateDividedByWei","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferERC1155","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"transferERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101006040523480156200001257600080fd5b5060405162004123380380620041238339810160408190526200003591620003ef565b62000040336200026c565b6200005e82631e9abf3f60e01b6200029660201b6200189b1760201c565b6200008c576040516378eb2a3160e11b81526001600160a01b03831660048201526024015b60405180910390fd5b6001600160a01b03821660a052620000b881639967e99b60e01b62000296602090811b6200189b17901c565b620000e25760405163fa5e4a2160e01b81526001600160a01b038216600482015260240162000083565b600380546001600160a01b0319166001600160a01b0383169081179091556040517fe9d395ade7e90aa86b53f2ca7a6af85aa882c2363e96203e90b856a978059ff590600090a246600114620001715746600514620001565773424242424242424242424242424242424242424262000183565b73ff50ed3d0ec03ac01d4c79aad74928bff48a7b2b62000183565b6f219ab540356cbb839cbe05303d7705fa5b6001600160a01b031660805260014614620001d75746600514620001bc5773ad45a78180961079bfaeee349704f411dff947c6620001ed565b733a9f01091c446bde031e39ea8354647afef091e7620001ed565b739d65ff81a3c488d585bbfb0bfe3c7707c7917f545b6001600160a01b031660c05260014614620002415746600514620002265773352a18aee90cdcd825d1e37d9939dca86c00e28162000257565b73ae2c84c48272f5a1746150ef333d5e5b51f6876362000257565b73afe830b6ee262ba11cce5f32fdcd760ffe6a66e45b6001600160a01b031660e05250620004279050565b600180546001600160a01b03191690556200029381620002be602090811b620018be17901c565b50565b6000620002a3836200030e565b8015620002b75750620002b7838362000347565b9392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600062000323826301ffc9a760e01b62000347565b80156200034157506200033f826001600160e01b031962000347565b155b92915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03166301ffc9a760e01b178152825160009392849283928392918391908a617530fa92503d91506000519050828015620003ba575060208210155b8015620003c75750600081115b979650505050505050565b80516001600160a01b0381168114620003ea57600080fd5b919050565b600080604083850312156200040357600080fd5b6200040e83620003d2565b91506200041e60208401620003d2565b90509250929050565b60805160a05160c05160e051613cb46200046f600039600061232001526000611db30152600081816107f7015281816129b60152612a5d015260006126200152613cb46000f3fe60806040526004361061025c5760003560e01c80637860180e11610144578063b9e6b12b116100b6578063e30c39781161007a578063e30c397814610757578063e4eecb3d14610775578063ea1f737314610795578063f1652155146107b3578063f2fde38b146107c8578063f8b442cc146107e857600080fd5b8063b9e6b12b14610690578063cfbe2e78146106ca578063d444b75c146106f7578063dbecc61614610717578063dfb86bb71461073757600080fd5b80638da5cb5b116101085780638da5cb5b146105aa5780639473d869146105c857806395031908146106015780639db5dbe414610621578063b214a7c514610641578063b83802f51461067b57600080fd5b80637860180e1461052457806379ba5097146105375780637c8c9f2e1461054c5780637e1e221b1461056a5780637f01a0f81461058a57600080fd5b8063340d73a7116101dd578063570ca735116101a1578063570ca735146104735780635a8c880e146104915780636494731e146104af57806366552b95146104cf578063696e5fc3146104e457806372616d371461050457600080fd5b8063340d73a7146103c557806343457640146103e557806347e66774146104055780634d5b488114610425578063542d91081461044557600080fd5b80631aca6376116102245780631aca6376146103255780631b54769e146103455780632630ba94146103655780632794e08d14610385578063311a3c17146103a557600080fd5b806301ffc9a71461026157806305b1137b1461029657806306394c9b146102b8578063085d6e42146102d8578063166effcf14610303575b600080fd5b34801561026d57600080fd5b5061028161027c36600461302a565b61081b565b60405190151581526020015b60405180910390f35b3480156102a257600080fd5b506102b66102b136600461305a565b610852565b005b3480156102c457600080fd5b506102b66102d3366004613086565b61089d565b6102eb6102e63660046130ce565b61093d565b6040516001600160a01b03909116815260200161028d565b34801561030f57600080fd5b5061031861095f565b60405161028d919061312d565b34801561033157600080fd5b506102b661034036600461317a565b610970565b34801561035157600080fd5b506102b6610360366004613086565b6109b4565b34801561037157600080fd5b506102b6610380366004613206565b610a40565b34801561039157600080fd5b506102eb6103a0366004613086565b610b7d565b3480156103b157600080fd5b506103186103c0366004613086565b610bce565b3480156103d157600080fd5b506102b66103e0366004613086565b610c44565b3480156103f157600080fd5b506102b6610400366004613206565b610c84565b34801561041157600080fd5b506102b6610420366004613206565b610d74565b34801561043157600080fd5b506102b6610440366004613206565b610e64565b34801561045157600080fd5b50610465610460366004613247565b610f54565b60405190815260200161028d565b34801561047f57600080fd5b506002546001600160a01b03166102eb565b34801561049d57600080fd5b506004546001600160a01b03166102eb565b3480156104bb57600080fd5b506102b66104ca366004613272565b610f81565b3480156104db57600080fd5b506102b6611003565b3480156104f057600080fd5b506102eb6104ff366004613086565b61103e565b34801561051057600080fd5b506102b661051f366004613086565b611065565b6102eb6105323660046132ac565b61111c565b34801561054357600080fd5b506102b6611166565b34801561055857600080fd5b506003546001600160a01b03166102eb565b34801561057657600080fd5b506102b6610585366004613347565b61119c565b34801561059657600080fd5b506102b66105a5366004613086565b611277565b3480156105b657600080fd5b506000546001600160a01b03166102eb565b3480156105d457600080fd5b50600c54600160701b90046001600160701b03165b6040516001600160701b03909116815260200161028d565b34801561060d57600080fd5b506102b661061c366004613206565b611333565b34801561062d57600080fd5b506102b661063c36600461317a565b611431565b34801561064d57600080fd5b5061028161065c36600461302a565b6001600160e01b0319166000908152600a602052604090205460ff1690565b34801561068757600080fd5b506102b661146e565b34801561069c57600080fd5b506102816106ab36600461302a565b6001600160e01b0319166000908152600b602052604090205460ff1690565b3480156106d657600080fd5b506106ea6106e5366004613086565b6114ae565b60405161028d9190613370565b34801561070357600080fd5b506102b6610712366004613206565b611531565b34801561072357600080fd5b506102b66107323660046133ab565b611621565b34801561074357600080fd5b506102b6610752366004613454565b61166b565b34801561076357600080fd5b506001546001600160a01b03166102eb565b34801561078157600080fd5b506102b6610790366004613347565b6116a4565b3480156107a157600080fd5b50600c546001600160701b03166105e9565b3480156107bf57600080fd5b50610318611783565b3480156107d457600080fd5b506102b66107e3366004613086565b6117e5565b3480156107f457600080fd5b507f00000000000000000000000000000000000000000000000000000000000000006102eb565b60006001600160e01b0319821663044022af60e51b148061084c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b60005433906001600160a01b031680821461088d57818160405163078c725960e01b8152600401610884929190613471565b60405180910390fd5b610897848461190e565b50505050565b60005433906001600160a01b03168082146108cf57818160405163078c725960e01b8152600401610884929190613471565b6001600160a01b0383166108f557604051626cca8560e81b815260040160405180910390fd5b6002546001600160a01b039081169084160361092f5760405163b16f970560e01b81526001600160a01b0384166004820152602401610884565b61093883611a05565b505050565b600061094c8460e00135611a57565b610957848484611ad8565b949350505050565b606061096b6005611ec0565b905090565b60005433906001600160a01b03168082146109a257818160405163078c725960e01b8152600401610884929190613471565b6109ad858585611ecd565b5050505050565b6000546002546001600160a01b03918216919081169083161580610a075750826001600160a01b0316816001600160a01b031614158015610a075750826001600160a01b0316826001600160a01b031614155b156109385760405163f886421960e01b81526001600160a01b038085166004830152808316602483015283166044820152606401610884565b6000546002546001600160a01b039182169116338114801590610a6c57506001600160a01b0382163314155b15610aa757335b604051620653ff60e81b81526001600160a01b03918216600482015281831660248201529083166044820152606401610884565b826000819003610aca576040516330f69ae560e01b815260040160405180910390fd5b60005b81811015610b3c576000868683818110610ae957610ae961348b565b9050602002016020810190610afe9190613086565b9050610b0b600582611fb0565b610b33576040516356f314bb60e01b81526001600160a01b0382166004820152602401610884565b50600101610acd565b507ffc6d52677793949e1840b9b6953d4ec3f4942d6e175e46f366e5acd6e5402c748585604051610b6e9291906134a1565b60405180910390a15050505050565b600080610b926000546001600160a01b031690565b6002549091506001600160a01b0316338114801590610bba57506001600160a01b0382163314155b15610bc55733610a73565b61095784611fc5565b6001600160a01b038116600090815260086020908152604091829020805483518184028101840190945280845260609392830182828015610c3857602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c1a575b50505050509050919050565b6000546002546001600160a01b039182169116338114801590610c7057506001600160a01b0382163314155b15610c7b5733610a73565b610938836121b7565b60005433906001600160a01b0316808214610cb657818160405163078c725960e01b8152600401610884929190613471565b826000819003610cd9576040516321bca83160e21b815260040160405180910390fd5b60005b81811015610d42576000600b6000888885818110610cfc57610cfc61348b565b9050602002016020810190610d11919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101610cdc565b507f5a21b3acabeaf7a66d3b7ae828dc70126a2f78e48f2b4d4a4afc740eb110f4348585604051610b6e9291906134ef565b60005433906001600160a01b0316808214610da657818160405163078c725960e01b8152600401610884929190613471565b826000819003610dc957604051630ef7da7760e31b815260040160405180910390fd5b60005b81811015610e32576001600b6000888885818110610dec57610dec61348b565b9050602002016020810190610e01919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101610dcc565b507fdd82d1dff1dbfeff391b03e9ec0a39d28964bf9972ed71ce60e8a7162e43f4198585604051610b6e9291906134ef565b60005433906001600160a01b0316808214610e9657818160405163078c725960e01b8152600401610884929190613471565b826000819003610eb957604051630ef7da7760e31b815260040160405180910390fd5b60005b81811015610f22576001600a6000888885818110610edc57610edc61348b565b9050602002016020810190610ef1919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101610ebc565b507f9af894151314cf5c8f253a71f43eab115ae4b5963967529ec692905a5f5b89ef8585604051610b6e9291906134ef565b600c54600090670de0b6b3a764000090610f77906001600160701b031684613547565b61084c919061355e565b6000546002546001600160a01b039182169116338114801590610fad57506001600160a01b0382163314155b15610fb85733610a73565b826000610fc6600583612201565b905080610ff157604051630f0782ed60e31b81526001600160a01b0383166004820152602401610884565b610ffb8686612223565b505050505050565b6000611010600533612201565b90508061103257604051630f0782ed60e31b8152336004820152602401610884565b61103b336121b7565b50565b60045460009061084c906001600160a01b03166001600160601b0319606085901b16612467565b60005433906001600160a01b031680821461109757818160405163078c725960e01b8152600401610884929190613471565b6110a883631af2a57d60e11b61189b565b6110d0576040516347eceb1760e11b81526001600160a01b0384166004820152602401610884565b600480546001600160a01b0319166001600160a01b0385169081179091556040517f11f0ffbb8f2ed411b4fa661dcaba92bdfe8e4eced3a98a1556336384f906280d90600090a2505050565b600061113a60e08501356111336020870187613580565b90506124cd565b611151868661114c6020880188613580565b612535565b61115c848484611ad8565b9695505050505050565b60015433906001600160a01b0316811461119357604051634b5dfa7d60e01b815260040160405180910390fd5b61103b81612740565b60005433906001600160a01b03168082146111ce57818160405163078c725960e01b8152600401610884929190613471565b64e8d4a51000836001600160701b031610806111fb575068056bc75e2d63100000836001600160701b0316115b156112195760405163592643c560e11b815260040160405180910390fd5b600c80546dffffffffffffffffffffffffffff19166001600160701b0385169081179091556040519081527f5da50833eee5d5726caca4250b6d7d8a972913b02081b430e5c98bdc29e0ca0b906020015b60405180910390a1505050565b6000546002546001600160a01b0391821691163381148015906112a357506001600160a01b0382163314155b156112ae5733610a73565b6112bf83639967e99b60e01b61189b565b6112e75760405163fa5e4a2160e01b81526001600160a01b0384166004820152602401610884565b600380546001600160a01b0319166001600160a01b0385169081179091556040517fe9d395ade7e90aa86b53f2ca7a6af85aa882c2363e96203e90b856a978059ff590600090a2505050565b6000546002546001600160a01b03918216911633811480159061135f57506001600160a01b0382163314155b1561136a5733610a73565b82600081900361138d5760405163430b78e160e01b815260040160405180910390fd5b60005b818110156113ff5760008686838181106113ac576113ac61348b565b90506020020160208101906113c19190613086565b90506113ce600582612759565b6113f6576040516314bf775160e11b81526001600160a01b0382166004820152602401610884565b50600101611390565b507f403cb436b5360f44a015c50aa8b4eb13cff157957347944f8d6d7f9f24c636f28585604051610b6e9291906134a1565b60005433906001600160a01b031680821461146357818160405163078c725960e01b8152600401610884929190613471565b6109ad85858561276e565b60005433906001600160a01b03168082146114a057818160405163078c725960e01b8152600401610884929190613471565b6114aa6000611a05565b5050565b6114b6612f30565b6001600160a01b03821660009081526007602052604080822081516101008101928390529290916008918390855b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116114e4575094979650505050505050565b60005433906001600160a01b031680821461156357818160405163078c725960e01b8152600401610884929190613471565b826000819003611586576040516321bca83160e21b815260040160405180910390fd5b60005b818110156115ef576000600a60008888858181106115a9576115a961348b565b90506020020160208101906115be919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101611589565b507f71e256b211b9990dce5e4eeead09024fc0ac9967e82176c0d64d704e62d65a418585604051610b6e9291906134ef565b60005433906001600160a01b031680821461165357818160405163078c725960e01b8152600401610884929190613471565b6116618888888888886127ef565b5050505050505050565b6000611678600533612201565b90508061169a57604051630f0782ed60e31b8152336004820152602401610884565b6114aa8233612223565b60005433906001600160a01b03168082146116d657818160405163078c725960e01b8152600401610884929190613471565b64e8d4a51000836001600160701b03161080611704575069d3c21bcecceda1000000836001600160701b0316115b1561172257604051632026ab9560e21b815260040160405180910390fd5b600c80546dffffffffffffffffffffffffffff60701b1916600160701b6001600160701b038616908102919091179091556040519081527fe2badc4ee8835bf149d207b80ab81bc05d339496d3aa17f90aa66d168056f7f09060200161126a565b606060098054806020026020016040519081016040528092919081815260200182805480156117db57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116117bd575b5050505050905090565b60005433906001600160a01b031680821461181757818160405163078c725960e01b8152600401610884929190613471565b6000546001600160a01b03908116908416819003611848576040516321886cf960e11b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0386811691821790925560405190918316907f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270090600090a350505050565b60006118a6836128d9565b80156118b757506118b7838361290c565b9392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b816001600160a01b0381166119365760405163a8cefabd60e01b815260040160405180910390fd5b6000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114611983576040519150601f19603f3d011682016040523d82523d6000602084013e611988565b606091505b50509050806119bc57604051637304e92760e01b81526001600160a01b038516600482015260248101849052604401610884565b836001600160a01b03167f2bd8874aee0f667380057c67e3a812157e4b7649b244d6fcbc9094a9a1f7ee1d846040516119f791815260200190565b60405180910390a250505050565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fd58299b712891143e76310d5e664c4203c940a67db37cf856bdaa3c5c76a802c90600090a35050565b600c546001600160701b03166000819003611a85576040516351a28c1560e11b815260040160405180910390fd5b6000670de0b6b3a7640000611aa36001600160701b03841685613547565b611aad919061355e565b9050803414610938576040516310cb217360e21b815260048101829052346024820152604401610884565b6000611ae484806135c9565b8060005b81811015611d6f576000848483818110611b0457611b0461348b565b611b1a9260206080909202019081019150613086565b6001600160a01b0381166000908152600760205260408082208151610100810192839052939450919291906008908285855b82829054906101000a90046001600160401b03166001600160401b031681526020019060080190602082600701049283019260010382029150808411611b4c5750949550600094508493505050505b6008811015611c0d57878786818110611bb657611bb661348b565b9050608002016020016020810190611bce9190613629565b6001600160401b0316838260088110611be957611be961348b565b60200201516001600160401b031603611c055760019150611c0d565b600101611b9b565b5080611c705782878786818110611c2657611c2661348b565b9050608002016020016020810190611c3e9190613629565b6040516296e02760e41b81526001600160a01b0390921660048301526001600160401b03166024820152604401610884565b60005b85811015611d6057808514158015611cc35750878782818110611c9857611c9861348b565b611cae9260206080909202019081019150613086565b6001600160a01b0316846001600160a01b0316145b15611d585783888887818110611cdb57611cdb61348b565b9050608002016020016020810190611cf39190613629565b898984818110611d0557611d0561348b565b9050608002016020016020810190611d1d9190613629565b60405163351c0a5960e21b81526001600160a01b0390931660048401526001600160401b039182166024840152166044820152606401610884565b600101611c73565b50836001019350505050611ae8565b506000611d7c8787612995565b9050611d8781611fc5565b60405163a9059cbb60e01b81526001600160a01b03808316600483015260e08b013560248301529196507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015611dfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e229190613652565b50604051634ad4a41b60e01b81526001600160a01b03861690634ad4a41b90611e4f908b906004016138b2565b600060405180830381600087803b158015611e6957600080fd5b505af1158015611e7d573d6000803e3d6000fd5b50506040516001600160a01b03881692507f5024bc22be69cf6a4b8a15170414e79de44a8853ae021ce695262332ad9846499150600090a2505050509392505050565b606060006118b783612ae2565b816001600160a01b038116611ef55760405163a8cefabd60e01b815260040160405180910390fd5b6040516323b872dd60e01b81523060048201526001600160a01b038481166024830152604482018490528516906323b872dd90606401600060405180830381600087803b158015611f4557600080fd5b505af1158015611f59573d6000803e3d6000fd5b50505050826001600160a01b0316846001600160a01b03167fcd68d836931d28b26c81fd06a68b603542d9b3a2fd1ba1c1bd30c9e2e5f4e6eb84604051611fa291815260200190565b60405180910390a350505050565b60006118b7836001600160a01b038416612b3d565b6000611fd08261103e565b9050806001600160a01b03163b6000036121b257611ff582639967e99b60e01b61189b565b61201d5760405163fa5e4a2160e01b81526001600160a01b0383166004820152602401610884565b600454612041906001600160a01b03166001600160601b0319606085901b16612c30565b60405163189acdbd60e31b81526001600160a01b0384811660048301529192509082169063c4d66de890602401600060405180830381600087803b15801561208857600080fd5b505af115801561209c573d6000803e3d6000fd5b505050506000826001600160a01b031663109e94cf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612104919061396f565b6001600160a01b03808216600081815260086020908152604080832080546001818101835591855292842090920180548987166001600160a01b0319918216811790925560098054948501815585527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af9093018054909316811790925551949550928716939192917f74b819b8f23dff5a79460de77b1c59893078357c408163c9502f71b7b39540d89190a4505b919050565b6001600160a01b038116600081815260076020526040808220828155600101829055517faaeaa052a256956b26324e9642ea99c37516184524168473d5c91d124154ecaf9190a250565b6001600160a01b038116600090815260018301602052604081205415156118b7565b60005b60088110156123fc5760008382600881106122435761224361348b565b6020020160208101906122569190613629565b9050600061226583600161398c565b90505b60088110156122ef578481600881106122835761228361348b565b6020020160208101906122969190613629565b6001600160401b0316826001600160401b03161480156122be57506001600160401b03821615155b156122e7576040516378ffbe6960e11b81526001600160401b0383166004820152602401610884565b600101612268565b506001600160401b038116156123f357604051635f1f82c760e11b81526001600160401b03821660048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063be3f058e9060240160c060405180830381865afa15801561236f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612393919061399f565b50505050509050836001600160a01b0316816001600160a01b0316146123f1576040516001625887f560e11b031981526001600160401b03831660048201526001600160a01b03808616602483015282166044820152606401610884565b505b50600101612226565b506001600160a01b038116600090815260076020526040902061242190836008612f4f565b50806001600160a01b03167fdcb0babe4f0d3603570b9dc8c9f7c10843aae0ec6478971ec4ee989babe6beb98360405161245b9190613a1c565b60405180910390a25050565b60006118b7838330604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b600c54600160701b90046001600160701b03166000819003612502576040516313daf87360e11b815260040160405180910390fd5b612515826001600160701b038316613547565b83111561093857604051631bce861960e11b815260040160405180910390fd5b80612549816801bc16d674ec800000613547565b341461256a57604051637f65afd960e11b8152346004820152602401610884565b806125758680613580565b905014158061259257508061258d6020870187613580565b905014155b156125dc57806125a28680613580565b90506125b16020880188613580565b60405163605e7d7360e01b815260048101949094526024840192909252506044820152606401610884565b60408051600160f81b60208201526001600160601b0319606087901b16602c82015260009101604051602081830303815290604052905060005b82811015612737577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663228951186801bc16d674ec8000008787858181106126695761266961348b565b905060200281019061267b9190613a56565b6126859080613a76565b866126908d80613580565b888181106126a0576126a061348b565b90506020028101906126b29190613a76565b8e80602001906126c29190613580565b8a8181106126d2576126d261348b565b905060200201356040518863ffffffff1660e01b81526004016126fa96959493929190613b0c565b6000604051808303818588803b15801561271357600080fd5b505af1158015612727573d6000803e3d6000fd5b5050505050806001019050612616565b50505050505050565b600180546001600160a01b031916905561103b816118be565b60006118b7836001600160a01b038416612cd0565b816001600160a01b0381166127965760405163a8cefabd60e01b815260040160405180910390fd5b6127aa6001600160a01b0385168484612d1f565b826001600160a01b0316846001600160a01b03167fe8de91d538b06154a2c48315768c5046f47e127d7fd3f726fd85cc723f29b05284604051611fa291815260200190565b846001600160a01b0381166128175760405163a8cefabd60e01b815260040160405180910390fd5b604051637921219560e11b81526001600160a01b0388169063f242432a9061284d9030908a908a908a908a908a90600401613b5b565b600060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b50505050856001600160a01b0316876001600160a01b03167f1c84289b4389ba8251b26974eaec89409eb21a1198ca5eb281c86388da2e778b878787876040516128c89493929190613ba2565b60405180910390a350505050505050565b60006128ec826301ffc9a760e01b61290c565b801561084c5750612905826001600160e01b031961290c565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03166301ffc9a760e01b178152825160009392849283928392918391908a617530fa92503d9150600051905082801561297e575060208210155b801561298a5750600081115b979650505050505050565b6003546040516336642b5760e11b81526000916001600160a01b03908116917f000000000000000000000000000000000000000000000000000000000000000090911690636cc856ae906129f190849088908890600401613c06565b602060405180830381865afa158015612a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a32919061396f565b9150816001600160a01b03163b600003612adb5760405163ea7a8b5160e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063ea7a8b5190612a9690849088908890600401613c06565b6020604051808303816000875af1158015612ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ad9919061396f565b505b5092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015610c3857602002820191906000526020600020905b815481526020019060010190808311612b1e5750505050509050919050565b60008181526001830160205260408120548015612c26576000612b61600183613c30565b8554909150600090612b7590600190613c30565b9050818114612bda576000866000018281548110612b9557612b9561348b565b9060005260206000200154905080876000018481548110612bb857612bb861348b565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612beb57612beb613c43565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061084c565b600091505061084c565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b03811661084c5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401610884565b6000818152600183016020526040812054612d175750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561084c565b50600061084c565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092018352602080830180516001600160e01b031663a9059cbb60e01b17905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65649084015261093892869291600091612daf918516908490612e2c565b8051909150156109385780806020019051810190612dcd9190613652565b6109385760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610884565b60606109578484600085856001600160a01b0385163b612e8e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610884565b600080866001600160a01b03168587604051612eaa9190613c59565b60006040518083038185875af1925050503d8060008114612ee7576040519150601f19603f3d011682016040523d82523d6000602084013e612eec565b606091505b509150915061298a82828660608315612f065750816118b7565b825115612f165782518084602001fd5b8160405162461bcd60e51b81526004016108849190613c6b565b6040518061010001604052806008906020820280368337509192915050565b600283019183908215612fed5791602002820160005b83821115612fb85783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302612f65565b8015612feb5782816101000a8154906001600160401b030219169055600801602081600701049283019260010302612fb8565b505b50612ff9929150612ffd565b5090565b5b80821115612ff95760008155600101612ffe565b80356001600160e01b0319811681146121b257600080fd5b60006020828403121561303c57600080fd5b6118b782613012565b6001600160a01b038116811461103b57600080fd5b6000806040838503121561306d57600080fd5b823561307881613045565b946020939093013593505050565b60006020828403121561309857600080fd5b81356118b781613045565b600061012082840312156130b657600080fd5b50919050565b6000604082840312156130b657600080fd5b600080600060a084860312156130e357600080fd5b83356001600160401b038111156130f957600080fd5b613105868287016130a3565b93505061311585602086016130bc565b915061312485606086016130bc565b90509250925092565b6020808252825182820181905260009190848201906040850190845b8181101561316e5783516001600160a01b031683529284019291840191600101613149565b50909695505050505050565b60008060006060848603121561318f57600080fd5b833561319a81613045565b925060208401356131aa81613045565b929592945050506040919091013590565b60008083601f8401126131cd57600080fd5b5081356001600160401b038111156131e457600080fd5b6020830191508360208260051b85010111156131ff57600080fd5b9250929050565b6000806020838503121561321957600080fd5b82356001600160401b0381111561322f57600080fd5b61323b858286016131bb565b90969095509350505050565b60006020828403121561325957600080fd5b5035919050565b80610100810183101561084c57600080fd5b600080610120838503121561328657600080fd5b6132908484613260565b91506101008301356132a181613045565b809150509250929050565b600080600080600060e086880312156132c457600080fd5b85356001600160401b03808211156132db57600080fd5b6132e789838a016130bc565b9650602088013591506132f982613045565b9094506040870135908082111561330f57600080fd5b5061331c888289016130a3565b93505061332c87606088016130bc565b915061333b8760a088016130bc565b90509295509295909350565b60006020828403121561335957600080fd5b81356001600160701b03811681146118b757600080fd5b6101008101818360005b60088110156133a25781516001600160401b031683526020928301929091019060010161337a565b50505092915050565b60008060008060008060a087890312156133c457600080fd5b86356133cf81613045565b955060208701356133df81613045565b9450604087013593506060870135925060808701356001600160401b038082111561340957600080fd5b818901915089601f83011261341d57600080fd5b81358181111561342c57600080fd5b8a602082850101111561343e57600080fd5b6020830194508093505050509295509295509295565b6000610100828403121561346757600080fd5b6118b78383613260565b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052603260045260246000fd5b60208082528181018390526000908460408401835b868110156134e45782356134c981613045565b6001600160a01b0316825291830191908301906001016134b6565b509695505050505050565b60208082528181018390526000908460408401835b868110156134e4576001600160e01b031961351e84613012565b1682529183019190830190600101613504565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761084c5761084c613531565b60008261357b57634e487b7160e01b600052601260045260246000fd5b500490565b6000808335601e1984360301811261359757600080fd5b8301803591506001600160401b038211156135b157600080fd5b6020019150600581901b36038213156131ff57600080fd5b6000808335601e198436030181126135e057600080fd5b8301803591506001600160401b038211156135fa57600080fd5b6020019150600781901b36038213156131ff57600080fd5b80356001600160401b03811681146121b257600080fd5b60006020828403121561363b57600080fd5b6118b782613612565b801515811461103b57600080fd5b60006020828403121561366457600080fd5b81516118b781613644565b8183526000602080850194508260005b858110156136db57813561369281613045565b6001600160a01b031687526001600160401b036136b0838501613612565b168784015260408281013590880152606080830135908801526080968701969091019060010161367f565b509495945050505050565b6000808335601e198436030181126136fd57600080fd5b83016020810192503590506001600160401b0381111561371c57600080fd5b8060051b36038213156131ff57600080fd5b6000808335601e1984360301811261374557600080fd5b83016020810192503590506001600160401b0381111561376457600080fd5b8036038213156131ff57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b81835260006020808501808196508560051b81019150846000805b8881101561382e578385038a528235603e198936030181126137d7578283fd5b880160406137e5828061372e565b8289526137f5838a018284613773565b925050506138058883018361372e565b925087820389890152613819828483613773565b9c89019c9750505092860192506001016137b7565b509298975050505050505050565b63ffffffff8116811461103b57600080fd5b80356138598161383c565b63ffffffff16825261386d60208201613612565b6001600160401b0380821660208501528061388a60408501613612565b166040850152505060608101356138a081613644565b15156060830152608090810135910152565b6020815260008235601e198436030181126138cc57600080fd5b83016020810190356001600160401b038111156138e857600080fd5b8060071b36038213156138fa57600080fd5b6101208060208601526139126101408601838561366f565b925061392160208701876136e6565b868503601f19016040880152925061393a84848361379c565b93505061394d606086016040880161384e565b610100915060e086013582860152818601358186015250508091505092915050565b60006020828403121561398157600080fd5b81516118b781613045565b8082018082111561084c5761084c613531565b60008060008060008060c087890312156139b857600080fd5b86516139c381613045565b6020880151604089015191975095506139db8161383c565b60608801519094506139ec81613045565b60808801519093506139fd81613644565b60a0880151909250613a0e81613644565b809150509295509295509295565b6101008101818360005b60088110156133a2576001600160401b03613a4083613612565b1683526020928301929190910190600101613a26565b60008235603e19833603018112613a6c57600080fd5b9190910192915050565b6000808335601e19843603018112613a8d57600080fd5b8301803591506001600160401b03821115613aa757600080fd5b6020019150368190038213156131ff57600080fd5b60005b83811015613ad7578181015183820152602001613abf565b50506000910152565b60008151808452613af8816020860160208601613abc565b601f01601f19169290920160200192915050565b608081526000613b2060808301888a613773565b8281036020840152613b328188613ae0565b90508281036040840152613b47818688613773565b915050826060830152979650505050505050565b6001600160a01b03878116825286166020820152604081018590526060810184905260a060808201819052600090613b969083018486613773565b98975050505050505050565b84815283602082015260606040820152600061115c606083018486613773565b80356bffffffffffffffffffffffff8116808214613bdf57600080fd5b8352506020810135613bf081613045565b6001600160a01b03166020929092019190915250565b6001600160a01b038416815260a08101613c236020830185613bc2565b6109576060830184613bc2565b8181038181111561084c5761084c613531565b634e487b7160e01b600052603160045260246000fd5b60008251613a6c818460208701613abc565b6020815260006118b76020830184613ae056fea2646970667358221220b5c06b46468fef2f95b398db2585dab7c7d738cc660d3490b7d8809589c24fce64736f6c63430008120033000000000000000000000000bf6339534b2458aa8fd75a4e5ced45407470953d00000000000000000000000078ce038ac24238c59777f5e4023707a23bb72d22

Deployed Bytecode

0x60806040526004361061025c5760003560e01c80637860180e11610144578063b9e6b12b116100b6578063e30c39781161007a578063e30c397814610757578063e4eecb3d14610775578063ea1f737314610795578063f1652155146107b3578063f2fde38b146107c8578063f8b442cc146107e857600080fd5b8063b9e6b12b14610690578063cfbe2e78146106ca578063d444b75c146106f7578063dbecc61614610717578063dfb86bb71461073757600080fd5b80638da5cb5b116101085780638da5cb5b146105aa5780639473d869146105c857806395031908146106015780639db5dbe414610621578063b214a7c514610641578063b83802f51461067b57600080fd5b80637860180e1461052457806379ba5097146105375780637c8c9f2e1461054c5780637e1e221b1461056a5780637f01a0f81461058a57600080fd5b8063340d73a7116101dd578063570ca735116101a1578063570ca735146104735780635a8c880e146104915780636494731e146104af57806366552b95146104cf578063696e5fc3146104e457806372616d371461050457600080fd5b8063340d73a7146103c557806343457640146103e557806347e66774146104055780634d5b488114610425578063542d91081461044557600080fd5b80631aca6376116102245780631aca6376146103255780631b54769e146103455780632630ba94146103655780632794e08d14610385578063311a3c17146103a557600080fd5b806301ffc9a71461026157806305b1137b1461029657806306394c9b146102b8578063085d6e42146102d8578063166effcf14610303575b600080fd5b34801561026d57600080fd5b5061028161027c36600461302a565b61081b565b60405190151581526020015b60405180910390f35b3480156102a257600080fd5b506102b66102b136600461305a565b610852565b005b3480156102c457600080fd5b506102b66102d3366004613086565b61089d565b6102eb6102e63660046130ce565b61093d565b6040516001600160a01b03909116815260200161028d565b34801561030f57600080fd5b5061031861095f565b60405161028d919061312d565b34801561033157600080fd5b506102b661034036600461317a565b610970565b34801561035157600080fd5b506102b6610360366004613086565b6109b4565b34801561037157600080fd5b506102b6610380366004613206565b610a40565b34801561039157600080fd5b506102eb6103a0366004613086565b610b7d565b3480156103b157600080fd5b506103186103c0366004613086565b610bce565b3480156103d157600080fd5b506102b66103e0366004613086565b610c44565b3480156103f157600080fd5b506102b6610400366004613206565b610c84565b34801561041157600080fd5b506102b6610420366004613206565b610d74565b34801561043157600080fd5b506102b6610440366004613206565b610e64565b34801561045157600080fd5b50610465610460366004613247565b610f54565b60405190815260200161028d565b34801561047f57600080fd5b506002546001600160a01b03166102eb565b34801561049d57600080fd5b506004546001600160a01b03166102eb565b3480156104bb57600080fd5b506102b66104ca366004613272565b610f81565b3480156104db57600080fd5b506102b6611003565b3480156104f057600080fd5b506102eb6104ff366004613086565b61103e565b34801561051057600080fd5b506102b661051f366004613086565b611065565b6102eb6105323660046132ac565b61111c565b34801561054357600080fd5b506102b6611166565b34801561055857600080fd5b506003546001600160a01b03166102eb565b34801561057657600080fd5b506102b6610585366004613347565b61119c565b34801561059657600080fd5b506102b66105a5366004613086565b611277565b3480156105b657600080fd5b506000546001600160a01b03166102eb565b3480156105d457600080fd5b50600c54600160701b90046001600160701b03165b6040516001600160701b03909116815260200161028d565b34801561060d57600080fd5b506102b661061c366004613206565b611333565b34801561062d57600080fd5b506102b661063c36600461317a565b611431565b34801561064d57600080fd5b5061028161065c36600461302a565b6001600160e01b0319166000908152600a602052604090205460ff1690565b34801561068757600080fd5b506102b661146e565b34801561069c57600080fd5b506102816106ab36600461302a565b6001600160e01b0319166000908152600b602052604090205460ff1690565b3480156106d657600080fd5b506106ea6106e5366004613086565b6114ae565b60405161028d9190613370565b34801561070357600080fd5b506102b6610712366004613206565b611531565b34801561072357600080fd5b506102b66107323660046133ab565b611621565b34801561074357600080fd5b506102b6610752366004613454565b61166b565b34801561076357600080fd5b506001546001600160a01b03166102eb565b34801561078157600080fd5b506102b6610790366004613347565b6116a4565b3480156107a157600080fd5b50600c546001600160701b03166105e9565b3480156107bf57600080fd5b50610318611783565b3480156107d457600080fd5b506102b66107e3366004613086565b6117e5565b3480156107f457600080fd5b507f000000000000000000000000bf6339534b2458aa8fd75a4e5ced45407470953d6102eb565b60006001600160e01b0319821663044022af60e51b148061084c57506301ffc9a760e01b6001600160e01b03198316145b92915050565b60005433906001600160a01b031680821461088d57818160405163078c725960e01b8152600401610884929190613471565b60405180910390fd5b610897848461190e565b50505050565b60005433906001600160a01b03168082146108cf57818160405163078c725960e01b8152600401610884929190613471565b6001600160a01b0383166108f557604051626cca8560e81b815260040160405180910390fd5b6002546001600160a01b039081169084160361092f5760405163b16f970560e01b81526001600160a01b0384166004820152602401610884565b61093883611a05565b505050565b600061094c8460e00135611a57565b610957848484611ad8565b949350505050565b606061096b6005611ec0565b905090565b60005433906001600160a01b03168082146109a257818160405163078c725960e01b8152600401610884929190613471565b6109ad858585611ecd565b5050505050565b6000546002546001600160a01b03918216919081169083161580610a075750826001600160a01b0316816001600160a01b031614158015610a075750826001600160a01b0316826001600160a01b031614155b156109385760405163f886421960e01b81526001600160a01b038085166004830152808316602483015283166044820152606401610884565b6000546002546001600160a01b039182169116338114801590610a6c57506001600160a01b0382163314155b15610aa757335b604051620653ff60e81b81526001600160a01b03918216600482015281831660248201529083166044820152606401610884565b826000819003610aca576040516330f69ae560e01b815260040160405180910390fd5b60005b81811015610b3c576000868683818110610ae957610ae961348b565b9050602002016020810190610afe9190613086565b9050610b0b600582611fb0565b610b33576040516356f314bb60e01b81526001600160a01b0382166004820152602401610884565b50600101610acd565b507ffc6d52677793949e1840b9b6953d4ec3f4942d6e175e46f366e5acd6e5402c748585604051610b6e9291906134a1565b60405180910390a15050505050565b600080610b926000546001600160a01b031690565b6002549091506001600160a01b0316338114801590610bba57506001600160a01b0382163314155b15610bc55733610a73565b61095784611fc5565b6001600160a01b038116600090815260086020908152604091829020805483518184028101840190945280845260609392830182828015610c3857602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c1a575b50505050509050919050565b6000546002546001600160a01b039182169116338114801590610c7057506001600160a01b0382163314155b15610c7b5733610a73565b610938836121b7565b60005433906001600160a01b0316808214610cb657818160405163078c725960e01b8152600401610884929190613471565b826000819003610cd9576040516321bca83160e21b815260040160405180910390fd5b60005b81811015610d42576000600b6000888885818110610cfc57610cfc61348b565b9050602002016020810190610d11919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101610cdc565b507f5a21b3acabeaf7a66d3b7ae828dc70126a2f78e48f2b4d4a4afc740eb110f4348585604051610b6e9291906134ef565b60005433906001600160a01b0316808214610da657818160405163078c725960e01b8152600401610884929190613471565b826000819003610dc957604051630ef7da7760e31b815260040160405180910390fd5b60005b81811015610e32576001600b6000888885818110610dec57610dec61348b565b9050602002016020810190610e01919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101610dcc565b507fdd82d1dff1dbfeff391b03e9ec0a39d28964bf9972ed71ce60e8a7162e43f4198585604051610b6e9291906134ef565b60005433906001600160a01b0316808214610e9657818160405163078c725960e01b8152600401610884929190613471565b826000819003610eb957604051630ef7da7760e31b815260040160405180910390fd5b60005b81811015610f22576001600a6000888885818110610edc57610edc61348b565b9050602002016020810190610ef1919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101610ebc565b507f9af894151314cf5c8f253a71f43eab115ae4b5963967529ec692905a5f5b89ef8585604051610b6e9291906134ef565b600c54600090670de0b6b3a764000090610f77906001600160701b031684613547565b61084c919061355e565b6000546002546001600160a01b039182169116338114801590610fad57506001600160a01b0382163314155b15610fb85733610a73565b826000610fc6600583612201565b905080610ff157604051630f0782ed60e31b81526001600160a01b0383166004820152602401610884565b610ffb8686612223565b505050505050565b6000611010600533612201565b90508061103257604051630f0782ed60e31b8152336004820152602401610884565b61103b336121b7565b50565b60045460009061084c906001600160a01b03166001600160601b0319606085901b16612467565b60005433906001600160a01b031680821461109757818160405163078c725960e01b8152600401610884929190613471565b6110a883631af2a57d60e11b61189b565b6110d0576040516347eceb1760e11b81526001600160a01b0384166004820152602401610884565b600480546001600160a01b0319166001600160a01b0385169081179091556040517f11f0ffbb8f2ed411b4fa661dcaba92bdfe8e4eced3a98a1556336384f906280d90600090a2505050565b600061113a60e08501356111336020870187613580565b90506124cd565b611151868661114c6020880188613580565b612535565b61115c848484611ad8565b9695505050505050565b60015433906001600160a01b0316811461119357604051634b5dfa7d60e01b815260040160405180910390fd5b61103b81612740565b60005433906001600160a01b03168082146111ce57818160405163078c725960e01b8152600401610884929190613471565b64e8d4a51000836001600160701b031610806111fb575068056bc75e2d63100000836001600160701b0316115b156112195760405163592643c560e11b815260040160405180910390fd5b600c80546dffffffffffffffffffffffffffff19166001600160701b0385169081179091556040519081527f5da50833eee5d5726caca4250b6d7d8a972913b02081b430e5c98bdc29e0ca0b906020015b60405180910390a1505050565b6000546002546001600160a01b0391821691163381148015906112a357506001600160a01b0382163314155b156112ae5733610a73565b6112bf83639967e99b60e01b61189b565b6112e75760405163fa5e4a2160e01b81526001600160a01b0384166004820152602401610884565b600380546001600160a01b0319166001600160a01b0385169081179091556040517fe9d395ade7e90aa86b53f2ca7a6af85aa882c2363e96203e90b856a978059ff590600090a2505050565b6000546002546001600160a01b03918216911633811480159061135f57506001600160a01b0382163314155b1561136a5733610a73565b82600081900361138d5760405163430b78e160e01b815260040160405180910390fd5b60005b818110156113ff5760008686838181106113ac576113ac61348b565b90506020020160208101906113c19190613086565b90506113ce600582612759565b6113f6576040516314bf775160e11b81526001600160a01b0382166004820152602401610884565b50600101611390565b507f403cb436b5360f44a015c50aa8b4eb13cff157957347944f8d6d7f9f24c636f28585604051610b6e9291906134a1565b60005433906001600160a01b031680821461146357818160405163078c725960e01b8152600401610884929190613471565b6109ad85858561276e565b60005433906001600160a01b03168082146114a057818160405163078c725960e01b8152600401610884929190613471565b6114aa6000611a05565b5050565b6114b6612f30565b6001600160a01b03821660009081526007602052604080822081516101008101928390529290916008918390855b82829054906101000a90046001600160401b03166001600160401b0316815260200190600801906020826007010492830192600103820291508084116114e4575094979650505050505050565b60005433906001600160a01b031680821461156357818160405163078c725960e01b8152600401610884929190613471565b826000819003611586576040516321bca83160e21b815260040160405180910390fd5b60005b818110156115ef576000600a60008888858181106115a9576115a961348b565b90506020020160208101906115be919061302a565b6001600160e01b03191681526020810191909152604001600020805460ff1916911515919091179055600101611589565b507f71e256b211b9990dce5e4eeead09024fc0ac9967e82176c0d64d704e62d65a418585604051610b6e9291906134ef565b60005433906001600160a01b031680821461165357818160405163078c725960e01b8152600401610884929190613471565b6116618888888888886127ef565b5050505050505050565b6000611678600533612201565b90508061169a57604051630f0782ed60e31b8152336004820152602401610884565b6114aa8233612223565b60005433906001600160a01b03168082146116d657818160405163078c725960e01b8152600401610884929190613471565b64e8d4a51000836001600160701b03161080611704575069d3c21bcecceda1000000836001600160701b0316115b1561172257604051632026ab9560e21b815260040160405180910390fd5b600c80546dffffffffffffffffffffffffffff60701b1916600160701b6001600160701b038616908102919091179091556040519081527fe2badc4ee8835bf149d207b80ab81bc05d339496d3aa17f90aa66d168056f7f09060200161126a565b606060098054806020026020016040519081016040528092919081815260200182805480156117db57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116117bd575b5050505050905090565b60005433906001600160a01b031680821461181757818160405163078c725960e01b8152600401610884929190613471565b6000546001600160a01b03908116908416819003611848576040516321886cf960e11b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0386811691821790925560405190918316907f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270090600090a350505050565b60006118a6836128d9565b80156118b757506118b7838361290c565b9392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b816001600160a01b0381166119365760405163a8cefabd60e01b815260040160405180910390fd5b6000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114611983576040519150601f19603f3d011682016040523d82523d6000602084013e611988565b606091505b50509050806119bc57604051637304e92760e01b81526001600160a01b038516600482015260248101849052604401610884565b836001600160a01b03167f2bd8874aee0f667380057c67e3a812157e4b7649b244d6fcbc9094a9a1f7ee1d846040516119f791815260200190565b60405180910390a250505050565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fd58299b712891143e76310d5e664c4203c940a67db37cf856bdaa3c5c76a802c90600090a35050565b600c546001600160701b03166000819003611a85576040516351a28c1560e11b815260040160405180910390fd5b6000670de0b6b3a7640000611aa36001600160701b03841685613547565b611aad919061355e565b9050803414610938576040516310cb217360e21b815260048101829052346024820152604401610884565b6000611ae484806135c9565b8060005b81811015611d6f576000848483818110611b0457611b0461348b565b611b1a9260206080909202019081019150613086565b6001600160a01b0381166000908152600760205260408082208151610100810192839052939450919291906008908285855b82829054906101000a90046001600160401b03166001600160401b031681526020019060080190602082600701049283019260010382029150808411611b4c5750949550600094508493505050505b6008811015611c0d57878786818110611bb657611bb661348b565b9050608002016020016020810190611bce9190613629565b6001600160401b0316838260088110611be957611be961348b565b60200201516001600160401b031603611c055760019150611c0d565b600101611b9b565b5080611c705782878786818110611c2657611c2661348b565b9050608002016020016020810190611c3e9190613629565b6040516296e02760e41b81526001600160a01b0390921660048301526001600160401b03166024820152604401610884565b60005b85811015611d6057808514158015611cc35750878782818110611c9857611c9861348b565b611cae9260206080909202019081019150613086565b6001600160a01b0316846001600160a01b0316145b15611d585783888887818110611cdb57611cdb61348b565b9050608002016020016020810190611cf39190613629565b898984818110611d0557611d0561348b565b9050608002016020016020810190611d1d9190613629565b60405163351c0a5960e21b81526001600160a01b0390931660048401526001600160401b039182166024840152166044820152606401610884565b600101611c73565b50836001019350505050611ae8565b506000611d7c8787612995565b9050611d8781611fc5565b60405163a9059cbb60e01b81526001600160a01b03808316600483015260e08b013560248301529196507f0000000000000000000000009d65ff81a3c488d585bbfb0bfe3c7707c7917f549091169063a9059cbb906044016020604051808303816000875af1158015611dfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e229190613652565b50604051634ad4a41b60e01b81526001600160a01b03861690634ad4a41b90611e4f908b906004016138b2565b600060405180830381600087803b158015611e6957600080fd5b505af1158015611e7d573d6000803e3d6000fd5b50506040516001600160a01b03881692507f5024bc22be69cf6a4b8a15170414e79de44a8853ae021ce695262332ad9846499150600090a2505050509392505050565b606060006118b783612ae2565b816001600160a01b038116611ef55760405163a8cefabd60e01b815260040160405180910390fd5b6040516323b872dd60e01b81523060048201526001600160a01b038481166024830152604482018490528516906323b872dd90606401600060405180830381600087803b158015611f4557600080fd5b505af1158015611f59573d6000803e3d6000fd5b50505050826001600160a01b0316846001600160a01b03167fcd68d836931d28b26c81fd06a68b603542d9b3a2fd1ba1c1bd30c9e2e5f4e6eb84604051611fa291815260200190565b60405180910390a350505050565b60006118b7836001600160a01b038416612b3d565b6000611fd08261103e565b9050806001600160a01b03163b6000036121b257611ff582639967e99b60e01b61189b565b61201d5760405163fa5e4a2160e01b81526001600160a01b0383166004820152602401610884565b600454612041906001600160a01b03166001600160601b0319606085901b16612c30565b60405163189acdbd60e31b81526001600160a01b0384811660048301529192509082169063c4d66de890602401600060405180830381600087803b15801561208857600080fd5b505af115801561209c573d6000803e3d6000fd5b505050506000826001600160a01b031663109e94cf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612104919061396f565b6001600160a01b03808216600081815260086020908152604080832080546001818101835591855292842090920180548987166001600160a01b0319918216811790925560098054948501815585527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af9093018054909316811790925551949550928716939192917f74b819b8f23dff5a79460de77b1c59893078357c408163c9502f71b7b39540d89190a4505b919050565b6001600160a01b038116600081815260076020526040808220828155600101829055517faaeaa052a256956b26324e9642ea99c37516184524168473d5c91d124154ecaf9190a250565b6001600160a01b038116600090815260018301602052604081205415156118b7565b60005b60088110156123fc5760008382600881106122435761224361348b565b6020020160208101906122569190613629565b9050600061226583600161398c565b90505b60088110156122ef578481600881106122835761228361348b565b6020020160208101906122969190613629565b6001600160401b0316826001600160401b03161480156122be57506001600160401b03821615155b156122e7576040516378ffbe6960e11b81526001600160401b0383166004820152602401610884565b600101612268565b506001600160401b038116156123f357604051635f1f82c760e11b81526001600160401b03821660048201526000907f000000000000000000000000afe830b6ee262ba11cce5f32fdcd760ffe6a66e46001600160a01b03169063be3f058e9060240160c060405180830381865afa15801561236f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612393919061399f565b50505050509050836001600160a01b0316816001600160a01b0316146123f1576040516001625887f560e11b031981526001600160401b03831660048201526001600160a01b03808616602483015282166044820152606401610884565b505b50600101612226565b506001600160a01b038116600090815260076020526040902061242190836008612f4f565b50806001600160a01b03167fdcb0babe4f0d3603570b9dc8c9f7c10843aae0ec6478971ec4ee989babe6beb98360405161245b9190613a1c565b60405180910390a25050565b60006118b7838330604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b600c54600160701b90046001600160701b03166000819003612502576040516313daf87360e11b815260040160405180910390fd5b612515826001600160701b038316613547565b83111561093857604051631bce861960e11b815260040160405180910390fd5b80612549816801bc16d674ec800000613547565b341461256a57604051637f65afd960e11b8152346004820152602401610884565b806125758680613580565b905014158061259257508061258d6020870187613580565b905014155b156125dc57806125a28680613580565b90506125b16020880188613580565b60405163605e7d7360e01b815260048101949094526024840192909252506044820152606401610884565b60408051600160f81b60208201526001600160601b0319606087901b16602c82015260009101604051602081830303815290604052905060005b82811015612737577f00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa6001600160a01b031663228951186801bc16d674ec8000008787858181106126695761266961348b565b905060200281019061267b9190613a56565b6126859080613a76565b866126908d80613580565b888181106126a0576126a061348b565b90506020028101906126b29190613a76565b8e80602001906126c29190613580565b8a8181106126d2576126d261348b565b905060200201356040518863ffffffff1660e01b81526004016126fa96959493929190613b0c565b6000604051808303818588803b15801561271357600080fd5b505af1158015612727573d6000803e3d6000fd5b5050505050806001019050612616565b50505050505050565b600180546001600160a01b031916905561103b816118be565b60006118b7836001600160a01b038416612cd0565b816001600160a01b0381166127965760405163a8cefabd60e01b815260040160405180910390fd5b6127aa6001600160a01b0385168484612d1f565b826001600160a01b0316846001600160a01b03167fe8de91d538b06154a2c48315768c5046f47e127d7fd3f726fd85cc723f29b05284604051611fa291815260200190565b846001600160a01b0381166128175760405163a8cefabd60e01b815260040160405180910390fd5b604051637921219560e11b81526001600160a01b0388169063f242432a9061284d9030908a908a908a908a908a90600401613b5b565b600060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b50505050856001600160a01b0316876001600160a01b03167f1c84289b4389ba8251b26974eaec89409eb21a1198ca5eb281c86388da2e778b878787876040516128c89493929190613ba2565b60405180910390a350505050505050565b60006128ec826301ffc9a760e01b61290c565b801561084c5750612905826001600160e01b031961290c565b1592915050565b604080516001600160e01b03198316602480830191909152825180830390910181526044909101909152602080820180516001600160e01b03166301ffc9a760e01b178152825160009392849283928392918391908a617530fa92503d9150600051905082801561297e575060208210155b801561298a5750600081115b979650505050505050565b6003546040516336642b5760e11b81526000916001600160a01b03908116917f000000000000000000000000bf6339534b2458aa8fd75a4e5ced45407470953d90911690636cc856ae906129f190849088908890600401613c06565b602060405180830381865afa158015612a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a32919061396f565b9150816001600160a01b03163b600003612adb5760405163ea7a8b5160e01b81526001600160a01b037f000000000000000000000000bf6339534b2458aa8fd75a4e5ced45407470953d169063ea7a8b5190612a9690849088908890600401613c06565b6020604051808303816000875af1158015612ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ad9919061396f565b505b5092915050565b606081600001805480602002602001604051908101604052809291908181526020018280548015610c3857602002820191906000526020600020905b815481526020019060010190808311612b1e5750505050509050919050565b60008181526001830160205260408120548015612c26576000612b61600183613c30565b8554909150600090612b7590600190613c30565b9050818114612bda576000866000018281548110612b9557612b9561348b565b9060005260206000200154905080876000018481548110612bb857612bb861348b565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612beb57612beb613c43565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061084c565b600091505061084c565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b03811661084c5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401610884565b6000818152600183016020526040812054612d175750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561084c565b50600061084c565b604080516001600160a01b03848116602483015260448083018590528351808403909101815260649092018352602080830180516001600160e01b031663a9059cbb60e01b17905283518085019094528084527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65649084015261093892869291600091612daf918516908490612e2c565b8051909150156109385780806020019051810190612dcd9190613652565b6109385760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610884565b60606109578484600085856001600160a01b0385163b612e8e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610884565b600080866001600160a01b03168587604051612eaa9190613c59565b60006040518083038185875af1925050503d8060008114612ee7576040519150601f19603f3d011682016040523d82523d6000602084013e612eec565b606091505b509150915061298a82828660608315612f065750816118b7565b825115612f165782518084602001fd5b8160405162461bcd60e51b81526004016108849190613c6b565b6040518061010001604052806008906020820280368337509192915050565b600283019183908215612fed5791602002820160005b83821115612fb85783356001600160401b031683826101000a8154816001600160401b0302191690836001600160401b031602179055509260200192600801602081600701049283019260010302612f65565b8015612feb5782816101000a8154906001600160401b030219169055600801602081600701049283019260010302612fb8565b505b50612ff9929150612ffd565b5090565b5b80821115612ff95760008155600101612ffe565b80356001600160e01b0319811681146121b257600080fd5b60006020828403121561303c57600080fd5b6118b782613012565b6001600160a01b038116811461103b57600080fd5b6000806040838503121561306d57600080fd5b823561307881613045565b946020939093013593505050565b60006020828403121561309857600080fd5b81356118b781613045565b600061012082840312156130b657600080fd5b50919050565b6000604082840312156130b657600080fd5b600080600060a084860312156130e357600080fd5b83356001600160401b038111156130f957600080fd5b613105868287016130a3565b93505061311585602086016130bc565b915061312485606086016130bc565b90509250925092565b6020808252825182820181905260009190848201906040850190845b8181101561316e5783516001600160a01b031683529284019291840191600101613149565b50909695505050505050565b60008060006060848603121561318f57600080fd5b833561319a81613045565b925060208401356131aa81613045565b929592945050506040919091013590565b60008083601f8401126131cd57600080fd5b5081356001600160401b038111156131e457600080fd5b6020830191508360208260051b85010111156131ff57600080fd5b9250929050565b6000806020838503121561321957600080fd5b82356001600160401b0381111561322f57600080fd5b61323b858286016131bb565b90969095509350505050565b60006020828403121561325957600080fd5b5035919050565b80610100810183101561084c57600080fd5b600080610120838503121561328657600080fd5b6132908484613260565b91506101008301356132a181613045565b809150509250929050565b600080600080600060e086880312156132c457600080fd5b85356001600160401b03808211156132db57600080fd5b6132e789838a016130bc565b9650602088013591506132f982613045565b9094506040870135908082111561330f57600080fd5b5061331c888289016130a3565b93505061332c87606088016130bc565b915061333b8760a088016130bc565b90509295509295909350565b60006020828403121561335957600080fd5b81356001600160701b03811681146118b757600080fd5b6101008101818360005b60088110156133a25781516001600160401b031683526020928301929091019060010161337a565b50505092915050565b60008060008060008060a087890312156133c457600080fd5b86356133cf81613045565b955060208701356133df81613045565b9450604087013593506060870135925060808701356001600160401b038082111561340957600080fd5b818901915089601f83011261341d57600080fd5b81358181111561342c57600080fd5b8a602082850101111561343e57600080fd5b6020830194508093505050509295509295509295565b6000610100828403121561346757600080fd5b6118b78383613260565b6001600160a01b0392831681529116602082015260400190565b634e487b7160e01b600052603260045260246000fd5b60208082528181018390526000908460408401835b868110156134e45782356134c981613045565b6001600160a01b0316825291830191908301906001016134b6565b509695505050505050565b60208082528181018390526000908460408401835b868110156134e4576001600160e01b031961351e84613012565b1682529183019190830190600101613504565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761084c5761084c613531565b60008261357b57634e487b7160e01b600052601260045260246000fd5b500490565b6000808335601e1984360301811261359757600080fd5b8301803591506001600160401b038211156135b157600080fd5b6020019150600581901b36038213156131ff57600080fd5b6000808335601e198436030181126135e057600080fd5b8301803591506001600160401b038211156135fa57600080fd5b6020019150600781901b36038213156131ff57600080fd5b80356001600160401b03811681146121b257600080fd5b60006020828403121561363b57600080fd5b6118b782613612565b801515811461103b57600080fd5b60006020828403121561366457600080fd5b81516118b781613644565b8183526000602080850194508260005b858110156136db57813561369281613045565b6001600160a01b031687526001600160401b036136b0838501613612565b168784015260408281013590880152606080830135908801526080968701969091019060010161367f565b509495945050505050565b6000808335601e198436030181126136fd57600080fd5b83016020810192503590506001600160401b0381111561371c57600080fd5b8060051b36038213156131ff57600080fd5b6000808335601e1984360301811261374557600080fd5b83016020810192503590506001600160401b0381111561376457600080fd5b8036038213156131ff57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b81835260006020808501808196508560051b81019150846000805b8881101561382e578385038a528235603e198936030181126137d7578283fd5b880160406137e5828061372e565b8289526137f5838a018284613773565b925050506138058883018361372e565b925087820389890152613819828483613773565b9c89019c9750505092860192506001016137b7565b509298975050505050505050565b63ffffffff8116811461103b57600080fd5b80356138598161383c565b63ffffffff16825261386d60208201613612565b6001600160401b0380821660208501528061388a60408501613612565b166040850152505060608101356138a081613644565b15156060830152608090810135910152565b6020815260008235601e198436030181126138cc57600080fd5b83016020810190356001600160401b038111156138e857600080fd5b8060071b36038213156138fa57600080fd5b6101208060208601526139126101408601838561366f565b925061392160208701876136e6565b868503601f19016040880152925061393a84848361379c565b93505061394d606086016040880161384e565b610100915060e086013582860152818601358186015250508091505092915050565b60006020828403121561398157600080fd5b81516118b781613045565b8082018082111561084c5761084c613531565b60008060008060008060c087890312156139b857600080fd5b86516139c381613045565b6020880151604089015191975095506139db8161383c565b60608801519094506139ec81613045565b60808801519093506139fd81613644565b60a0880151909250613a0e81613644565b809150509295509295509295565b6101008101818360005b60088110156133a2576001600160401b03613a4083613612565b1683526020928301929190910190600101613a26565b60008235603e19833603018112613a6c57600080fd5b9190910192915050565b6000808335601e19843603018112613a8d57600080fd5b8301803591506001600160401b03821115613aa757600080fd5b6020019150368190038213156131ff57600080fd5b60005b83811015613ad7578181015183820152602001613abf565b50506000910152565b60008151808452613af8816020860160208601613abc565b601f01601f19169290920160200192915050565b608081526000613b2060808301888a613773565b8281036020840152613b328188613ae0565b90508281036040840152613b47818688613773565b915050826060830152979650505050505050565b6001600160a01b03878116825286166020820152604081018590526060810184905260a060808201819052600090613b969083018486613773565b98975050505050505050565b84815283602082015260606040820152600061115c606083018486613773565b80356bffffffffffffffffffffffff8116808214613bdf57600080fd5b8352506020810135613bf081613045565b6001600160a01b03166020929092019190915250565b6001600160a01b038416815260a08101613c236020830185613bc2565b6109576060830184613bc2565b8181038181111561084c5761084c613531565b634e487b7160e01b600052603160045260246000fd5b60008251613a6c818460208701613abc565b6020815260006118b76020830184613ae056fea2646970667358221220b5c06b46468fef2f95b398db2585dab7c7d738cc660d3490b7d8809589c24fce64736f6c63430008120033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000bf6339534b2458aa8fd75a4e5ced45407470953d00000000000000000000000078ce038ac24238c59777f5e4023707a23bb72d22

-----Decoded View---------------
Arg [0] : _feeDistributorFactory (address): 0xBf6339534B2458Aa8Fd75A4E5CeD45407470953d
Arg [1] : _referenceFeeDistributor (address): 0x78ce038aC24238c59777F5E4023707A23BB72D22

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000bf6339534b2458aa8fd75a4e5ced45407470953d
Arg [1] : 00000000000000000000000078ce038ac24238c59777f5e4023707a23bb72d22


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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.