Hoodi Testnet

Contract

0x41d77C33097DA5aa4050cd815beE535A2534Dd07

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount

There are no matching entries

4 Internal Transactions found.

Latest 4 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Amount
Approve17693842025-12-06 16:58:2439 days ago1765040304
0x41d77C33...A2534Dd07
0 ETH
Initialize17693842025-12-06 16:58:2439 days ago1765040304
0x41d77C33...A2534Dd07
0 ETH
Initialize17693842025-12-06 16:58:2439 days ago1765040304
0x41d77C33...A2534Dd07
0 ETH
0x3d602d8017693842025-12-06 16:58:2439 days ago1765040304  Contract Creation0 ETH
Loading...
Loading

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Minimal Proxy Contract for 0x1aa02c569c3d19f4c400c191ba581d692cd521c0

Contract Name:
SSVProxy

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

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

import "../access/OwnableWithOperator.sol";
import "../assetRecover/OwnableAssetRecover.sol";
import "../constants/StakingConstants.sol";
import "../interfaces/ssv-network/ISSVNetwork.sol";
import "../interfaces/ssvProxy/ISSVProxy.sol";
import "../interfaces/IDepositContract.sol";
import "../interfaces/feeManager/IFeeManagerFactory.sol";
import "../libraries/DepositStruct.sol";

/// @title Proxy for SSVNetwork calls.
/// @dev Each instance of SSVProxy corresponds to 1 FeeManager instance.
/// Thus, client to SSVProxy instances is a 1-to-many relation.
/// SSV tokens are managed by Operator.
/// Clients cover the costs of SSV tokens by EL rewards via FeeManager instance.
contract SSVProxy is OwnableAssetRecover, ERC165, ISSVProxy {
    /// @notice SSVProxyFactory address
    ISSVProxyFactory private immutable _ssvProxyFactory;

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

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

    /// @notice FeeManager instance address
    IFeeManager private _feeManager;

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

        if (clientAddress != msg.sender) {
            revert 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 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 CallerNeitherOperatorNorOwnerNorClient(msg.sender);
        }
        _;
    }

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

    /// @dev Set values that are constant, common for all clients, known at the initial deploy time.
    /// @param ssvProxyFactory_ address of SSVProxyFactory
    /// @param ssvNetwork_ address of SSV Network
    /// @param ssvToken_ address of SSV Token
    constructor(
        address ssvProxyFactory_,
        address ssvNetwork_,
        address ssvToken_
    ) {
        if (
            !ERC165Checker.supportsInterface(
                ssvProxyFactory_,
                type(ISSVProxyFactory).interfaceId
            )
        ) {
            revert NotSSVProxyFactory(ssvProxyFactory_);
        }

        _ssvProxyFactory = ISSVProxyFactory(ssvProxyFactory_);
        _ssvNetwork = ISSVNetwork(ssvNetwork_);
        _ssvToken = IERC20(ssvToken_);
    }

    /// @inheritdoc ISSVProxy
    function initialize(address feeManager_) external onlySSVProxyFactory {
        _feeManager = IFeeManager(feeManager_);

        _ssvToken.approve(address(_ssvNetwork), type(uint256).max);

        emit Initialized(feeManager_);
    }

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

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

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

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

            assembly ("memory-safe") {
                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 ISSVProxy
    function callAnyContract(
        address _contract,
        bytes calldata _calldata
    ) external onlyOwner {
        (bool success, bytes memory data) = address(_contract).call(_calldata);
        if (success) {
            emit SuccessfullyCalledExternalContract(
                _contract,
                bytes4(_calldata)
            );

            assembly ("memory-safe") {
                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 ISSVProxy
    function bulkRegisterValidators(
        bytes[] calldata publicKeys,
        uint64[] calldata operatorIds,
        bytes[] calldata sharesData,
        uint256 amount,
        ISSVNetwork.Cluster calldata cluster
    ) external onlySSVProxyFactory {
        _ssvNetwork.bulkRegisterValidator(
            publicKeys,
            operatorIds,
            sharesData,
            amount,
            cluster
        );
        _ssvNetwork.setFeeRecipientAddress(address(_feeManager));
    }

    /// @inheritdoc ISSVProxy
    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]
            );
        }
    }

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

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

    /// @inheritdoc ISSVProxy
    function withdrawSSVTokens(
        address _to,
        uint256 _amount
    ) external onlyOperatorOrOwner {
        _ssvToken.transfer(_to, _amount);
    }

    /// @inheritdoc ISSVProxy
    function withdrawAllSSVTokensToFactory() public onlyOperatorOrOwner {
        uint256 balance = _ssvToken.balanceOf(address(this));
        _ssvToken.transfer(address(_ssvProxyFactory), balance);
    }

    function withdrawFromSSVToFactory(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external {
        withdrawFromSSV(_tokenAmount, _operatorIds, _clusters);
        withdrawAllSSVTokensToFactory();
    }

    /// @inheritdoc ISSVProxy
    function setFeeRecipientAddress(
        address _feeRecipientAddress
    ) external onlyOperatorOrOwner {
        _ssvNetwork.setFeeRecipientAddress(_feeRecipientAddress);
    }

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

    /// @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; ++i) {
            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);
        }
    }

    /// @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 {
        ISSVNetworkCore.Cluster memory cluster = ISSVNetworkCore.Cluster({
            validatorCount: uint32(_cluster.validatorCount + i),
            networkFeeIndex: _currentNetworkFeeIndex,
            index: _clusterIndex,
            active: true,
            balance: _balance
        });

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

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

    /// @inheritdoc ISSVProxy
    function getFactory() external view returns (address) {
        return address(_ssvProxyFactory);
    }

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

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

    /// @inheritdoc ISSVProxy
    function getFeeManager() external view returns (address) {
        return address(_feeManager);
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` amount of tokens of 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 value 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 a `value` amount of tokens of type `id` from `from` to `to`.
     *
     * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
     * to an untrusted contract, when invoking {onERC1155Received} on the receiver.
     * Ensure to follow the checks-effects-interactions pattern and consider employing
     * reentrancy guards when interacting with untrusted contracts.
     *
     * 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 `value` 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 value, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
     * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
     * Ensure to follow the checks-effects-interactions pattern and consider employing
     * reentrancy guards when interacting with untrusted contracts.
     *
     * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
     *
     * Requirements:
     *
     * - `ids` and `values` 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 values,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../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: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * 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 address zero.
     *
     * 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // 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 FailedInnerCall();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./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);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

        return interfaceIdsSupported;
    }

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

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

        // all interfaces supported
        return true;
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @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 12 of 32 : BaseOwnable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

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

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

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "./BaseOwnable.sol";

/**
 * @dev OpenZeppelin's Ownable with modifier onlyOwner extracted to BaseOwnable
 */
abstract contract Ownable is BaseOwnable {
    /**
     * @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 _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 _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 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 = _owner;
        _owner = _newOwner;
        emit OwnershipTransferred(oldOwner, _newOwner);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "../access/Ownable.sol";
import "../interfaces/access/IOwnableWithOperator.sol";

/**
 * @dev Ownable with an additional role of operator
 */
abstract contract OwnableWithOperator is Ownable, IOwnableWithOperator {
    address private _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 = _operator;

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

        _;
    }

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

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

    /**
     * @dev Returns the current operator.
     */
    function operator() public view virtual returns (address) {
        return _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 ZeroNewOperator();
        }
        if (_newOperator == _operator) {
            revert 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 = _operator;
        _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 15 of 32 : BaseAssetRecover.sol
// SPDX-License-Identifier: MIT

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

import "./BaseTokenRecover.sol";

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

    /**
     * @notice could not transfer ether
     * @param _recipient address to transfer ether to
     * @param _amount amount of ether to transfer
     */
    error TransferFailed(address _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 TransferFailed(_recipient, _amount);
        }
        emit EtherTransferred(_recipient, _amount);
    }
}

// SPDX-License-Identifier: MIT

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

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

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

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

    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 CanNotBurn();
        }
        _;
    }

    /**
     * @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 17 of 32 : OwnableAssetRecover.sol
// SPDX-License-Identifier: MIT

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

import "./OwnableTokenRecover.sol";
import "./BaseAssetRecover.sol";

/// @title Public Asset Recoverer with public functions callable by assetAccessingAddress
/// @notice Recover ether, ERC20, ERC721 and ERC1155 from a derived contract
abstract contract OwnableAssetRecover is
    OwnableTokenRecover,
    BaseAssetRecoverer
{
    // 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);
    }
}

// SPDX-License-Identifier: MIT

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

import "./BaseTokenRecover.sol";
import "../access/BaseOwnable.sol";

/// @title Token Recoverer with public functions callable by assetAccessingAddress
/// @notice Recover ERC20, ERC721 and ERC1155 from a derived contract
abstract contract OwnableTokenRecover is BaseTokenRecover, BaseOwnable {
    /**
     * @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 19 of 32 : StakingConstants.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

/// @dev Maximal number of ETH Deposit deposits within a single transaction
/// @dev 400 deposits (12800 ETH) is determined by calldata size limit of 128 kb
/// https://ethereum.stackexchange.com/questions/144120/maximum-calldata-size-per-block
uint256 constant VALIDATORS_MAX_AMOUNT = 400;

/// @dev EIP-7251 MIN_ACTIVATION_BALANCE
uint96 constant MIN_ACTIVATION_BALANCE = 32 ether;

/// @dev EIP-7251 MAX_EFFECTIVE_BALANCE
uint96 constant MAX_EFFECTIVE_BALANCE = 2048 ether;

/// @dev Minimal 1 time deposit
uint256 constant MIN_DEPOSIT = 1 ether;

/// @dev Lockup time to allow Services to make ETH Deposit deposits
/// @dev If there is leftover ETH after this time, it can be refunded
uint40 constant TIMEOUT = 1 minutes;

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

File 20 of 32 : IOwnable.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

/**
 * @dev External interface of Ownable.
 */
interface IOwnable {
    /**
     * @notice Throws if called by any account other than the owner.
     * @param _caller address of the caller
     * @param _owner address of the owner
     */
    error CallerNotOwner(address _caller, address _owner);

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

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() external view returns (address);
}

File 21 of 32 : IOwnableWithOperator.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "./IOwnable.sol";

/**
 * @dev Ownable with an additional role of operator
 */
interface IOwnableWithOperator is IOwnable {
    /**
     * @notice newOperator is the zero address
     */
    error ZeroNewOperator();

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

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

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

    /**
     * @dev Returns the current operator.
     */
    function operator() external view returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../../libraries/DepositStruct.sol";

/// @dev External interface of FeeManager declared to support ERC165 detection.
interface IFeeManager 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
    /// @param _ssvProxyFactory address of the SSVProxyFactory
    event Initialized(
        address indexed _client,
        uint96 _clientBasisPoints,
        address indexed _referrer,
        uint96 _referrerBasisPoints,
        address _ssvProxyFactory
    );

    /// @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 Withdrawn(
        uint256 _serviceAmount,
        uint256 _clientAmount,
        uint256 _referrerAmount
    );

    /// @notice Should be a FeeManagerFactory contract
    /// @param _passedAddress passed address that does not support IFeeManagerFactory interface
    error NotFactory(address _passedAddress);

    /// @notice Service address should be a secure address, not zero.
    error ZeroAddressService();

    /// @notice Client address should be different from service address.
    /// @param _passedAddress passed client address that equals to the service address
    error ClientAddressEqualsService(address _passedAddress);

    /// @notice Client address should be an actual client address, not zero.
    error ZeroAddressClient();

    /// @notice Client basis points should be >= 0 and <= 10000
    /// @param _clientBasisPoints passed incorrect client basis points
    error InvalidClientBasisPoints(uint96 _clientBasisPoints);

    /// @notice Referrer basis points should be > 0 if the referrer exists
    error ZeroReferrerBasisPointsForNonZeroReferrer();

    /// @notice The sum of (Client basis points + Referral basis points) should be >= 0 and <= 10000
    /// @param _clientBasisPoints passed client basis points
    /// @param _referralBasisPoints passed referral basis points
    error ClientPlusReferralBasisPointsExceed10000(
        uint96 _clientBasisPoints,
        uint96 _referralBasisPoints
    );

    /// @notice Referrer address should be different from service address.
    /// @param _passedAddress passed referrer address that equals to the service address
    error ReferrerAddressEqualsService(address _passedAddress);

    /// @notice Referrer address should be different from client address.
    /// @param _passedAddress passed referrer address that equals to the client address
    error ReferrerAddressEqualsClient(address _passedAddress);

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

    /// @notice `initialize` should only be called once.
    /// @param _existingClient address of the client with which the contact has already been initialized.
    error ClientAlreadySet(address _existingClient);

    /// @notice Cannot call `withdraw` if the client address is not set yet.
    /// @dev The client address is supposed to be set by the factory.
    error ClientNotSet();

    /// @notice basisPoints of the referrer must be zero if referrer address is empty.
    /// @param _referrerBasisPoints basisPoints of the referrer.
    error ReferrerBasisPointsMustBeZeroIfAddressIsZero(
        uint96 _referrerBasisPoints
    );

    /// @notice service should be able to receive ether.
    /// @param _service address of the service.
    error ServiceCannotReceiveEther(address _service);

    /// @notice client should be able to receive ether.
    /// @param _client address of the client.
    error ClientCannotReceiveEther(address _client);

    /// @notice referrer should be able to receive ether.
    /// @param _referrer address of the referrer.
    error ReferrerCannotReceiveEther(address _referrer);

    /// @notice zero ether balance
    error NothingToWithdraw();

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

    /// @notice Throws in case there was some ether left after `withdraw` and it has failed to recover.
    /// @param _to destination address for ether.
    /// @param _amount how much wei the destination address should have received, but didn't.
    error EtherRecoveryFailed(address _to, uint256 _amount);

    /// @notice ETH receiver should not be a zero address
    error ZeroAddressEthReceiver();

    /// @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.
    /// @param _ssvProxyFactory address of the SSVProxyFactory
    function initialize(
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig,
        address _ssvProxyFactory
    ) 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);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../access/IOwnable.sol";
import "./IFeeManager.sol";
import "../../libraries/DepositStruct.sol";

/// @dev External interface of FeeManagerFactory declared to support ERC165 detection.
interface IFeeManagerFactory is IOwnable, IERC165 {
    /// @notice Emits when a new FeeManager instance has been created for a client
    /// @param _newFeeManagerAddress address of the newly created FeeManager contract instance
    /// @param _clientAddress address of the client for whom the new instance was created
    /// @param _referenceFeeManager The address of the reference implementation of FeeManager used as the basis for clones
    /// @param _clientBasisPoints client basis points (percent * 100)
    event FeeManagerCreated(
        address indexed _newFeeManagerAddress,
        address indexed _clientAddress,
        address indexed _referenceFeeManager,
        uint96 _clientBasisPoints
    );

    /// @notice Emits when a new GatewayEth2Deposit contract address has been set.
    /// @param _GatewayEth2Deposit the address of the new GatewayEth2Deposit contract
    event GatewayEth2DepositSet(address indexed _GatewayEth2Deposit);

    /// @notice Emits when a new value of defaultClientBasisPoints has been set.
    /// @param _defaultClientBasisPoints new value of defaultClientBasisPoints
    event DefaultClientBasisPointsSet(uint96 _defaultClientBasisPoints);

    /// @notice Should be a FeeManager contract
    /// @param _passedAddress passed address that does not support IFeeManager interface
    error NotFeeManager(address _passedAddress);

    /// @notice Should be a GatewayEth2Deposit contract
    /// @param _passedAddress passed address that does not support IGatewayEth2Deposit interface
    error NotGatewayEth2Deposit(address _passedAddress);

    /// @notice Reference FeeManager should be set before calling `createFeeManager`
    error ReferenceFeeManagerNotSet();

    /// @notice caller should be owner, operator, or GatewayEth2Deposit contract
    /// @param _caller calling address
    error CallerNotAuthorized(address _caller);

    /// @notice Default client basis points should be >= 0 and <= 10000
    /// @param _defaultClientBasisPoints passed incorrect default client basis points
    error InvalidDefaultClientBasisPoints(uint96 _defaultClientBasisPoints);

    /// @notice Creates a FeeManager instance for a client
    /// @dev _referrerConfig can be zero if there is no referrer.
    ///
    /// @param _referenceFeeManager The address of the reference implementation of FeeManager 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 newFeeManagerAddress user FeeManager instance that has just been deployed
    function createFeeManager(
        address _referenceFeeManager,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external returns (address newFeeManagerAddress);

    /// @notice Computes the address of a FeeManager created by `createFeeManager` function
    /// @dev FeeManager instances are guaranteed to have the same address if all of
    /// 1) referenceFeeManager 2) clientConfig 3) referrerConfig
    /// are the same
    /// @param _referenceFeeManager The address of the reference implementation of FeeManager 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 FeeManager instance that will be or has been deployed
    function predictFeeManagerAddress(
        address _referenceFeeManager,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external view returns (address);

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

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

    /// @notice The address of GatewayEth2Deposit
    /// @return address of GatewayEth2Deposit
    function gatewayEth2Deposit() 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 gatewayEth2Deposit
    /// @param _address passed address
    function checkGatewayEth2Deposit(address _address) external view;

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

File 24 of 32 : IDepositContract.sol
// SPDX-License-Identifier: CC0-1.0

pragma solidity 0.8.24;

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

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

/// @dev https://github.com/ssvlabs/ssv-network/blob/2e90a0cc44ae2645ea06ef9c0fcd2369bbf3c277/contracts/interfaces/external/ISSVWhitelistingContract.sol
interface ISSVWhitelistingContract {
    /// @notice Checks if the caller is whitelisted
    /// @param account The account that is being checked for whitelisting
    /// @param operatorId The SSV Operator Id which is being checked
    function isWhitelisted(address account, uint256 operatorId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

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

/// @dev https://github.com/ssvlabs/ssv-network/blob/2e90a0cc44ae2645ea06ef9c0fcd2369bbf3c277/contracts/interfaces/ISSVClusters.sol
interface ISSVClusters is ISSVNetworkCore {
    /// @notice Registers a new validator on the SSV Network
    /// @param publicKey The public key of the new validator
    /// @param operatorIds Array of IDs of operators managing this validator
    /// @param sharesData Encrypted shares related to the new validator
    /// @param amount Amount of SSV tokens to be deposited
    /// @param cluster Cluster to be used with the new validator
    function registerValidator(
        bytes calldata publicKey,
        uint64[] memory operatorIds,
        bytes calldata sharesData,
        uint256 amount,
        Cluster memory cluster
    ) external;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

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

/// @dev https://github.com/ssvlabs/ssv-network/blob/2e90a0cc44ae2645ea06ef9c0fcd2369bbf3c277/contracts/interfaces/ISSVNetwork.sol
interface ISSVNetwork is ISSVNetworkCore, ISSVOperators, ISSVClusters {
    function setFeeRecipientAddress(address feeRecipientAddress) external;
}

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

/// @dev https://github.com/ssvlabs/ssv-network/blob/2e90a0cc44ae2645ea06ef9c0fcd2369bbf3c277/contracts/interfaces/ISSVNetworkCore.sol
interface ISSVNetworkCore {
    /***********/
    /* Structs */
    /***********/

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

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

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

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

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

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

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

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

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

/// @dev https://github.com/ssvlabs/ssv-network/blob/2e90a0cc44ae2645ea06ef9c0fcd2369bbf3c277/contracts/interfaces/ISSVOperators.sol
interface ISSVOperators is ISSVNetworkCore {
    /// @notice Registers a new operator
    /// @dev For backward compatibility testing
    /// @param publicKey The public key of the operator
    /// @param fee The operator's fee (SSV)
    function registerOperator(bytes calldata publicKey, uint256 fee) external returns (uint64);

    /// @notice Registers a new operator
    /// @param publicKey The public key of the operator
    /// @param fee The operator's fee (SSV)
    /// @param setPrivate Flag indicating whether the operator should be set as private or not
    function registerOperator(bytes calldata publicKey, uint256 fee, bool setPrivate) external returns (uint64);

    /// @notice Removes an existing operator
    /// @param operatorId The ID of the operator to be removed
    function removeOperator(uint64 operatorId) external;

    /// @notice Sets the whitelist for an operator
    /// @dev For backward compatibility testing
    /// @param operatorId The ID of the operator
    /// @param whitelisted The address to be whitelisted
    function setOperatorWhitelist(uint64 operatorId, address whitelisted) external;

    /// @notice Declares the operator's fee
    /// @param operatorId The ID of the operator
    /// @param fee The fee to be declared (SSV)
    function declareOperatorFee(uint64 operatorId, uint256 fee) external;

    /// @notice Executes the operator's fee
    /// @param operatorId The ID of the operator
    function executeOperatorFee(uint64 operatorId) external;

    /// @notice Cancels the declared operator's fee
    /// @param operatorId The ID of the operator
    function cancelDeclaredOperatorFee(uint64 operatorId) external;

    /// @notice Reduces the operator's fee
    /// @param operatorId The ID of the operator
    /// @param fee The new Operator's fee (SSV)
    function reduceOperatorFee(uint64 operatorId, uint256 fee) external;

    /// @notice Withdraws operator earnings
    /// @param operatorId The ID of the operator
    /// @param tokenAmount The amount of tokens to withdraw (SSV)
    function withdrawOperatorEarnings(uint64 operatorId, uint256 tokenAmount) external;

    /// @notice Withdraws all operator earnings
    /// @param operatorId The ID of the operator
    function withdrawAllOperatorEarnings(uint64 operatorId) external;

    /// @notice Set the list of operators as private without checking for any whitelisting address
    /// @notice The operators are considered private when registering validators
    /// @param operatorIds The operator IDs to set as private
    function setOperatorsPrivateUnchecked(uint64[] calldata operatorIds) external;

    /// @notice Set the list of operators as public without removing any whitelisting address
    /// @notice The operators still keep its adresses whitelisted (external contract or EOAs/generic contracts)
    /// @notice The operators are considered public when registering validators
    /// @param operatorIds The operator IDs to set as public
    function setOperatorsPublicUnchecked(uint64[] calldata operatorIds) external;

    /**
     * @dev Emitted when a new operator has been added.
     * @param operatorId operator's ID.
     * @param owner Operator's ethereum address that can collect fees.
     * @param publicKey Operator's public key. Will be used to encrypt secret shares of validators keys.
     * @param fee Operator's fee.
     */
    event OperatorAdded(uint64 indexed operatorId, address indexed owner, bytes publicKey, uint256 fee);

    /**
     * @dev Emitted when operator has been removed.
     * @param operatorId operator's ID.
     */
    event OperatorRemoved(uint64 indexed operatorId);

    event OperatorFeeDeclared(address indexed owner, uint64 indexed operatorId, uint256 blockNumber, uint256 fee);

    event OperatorFeeDeclarationCancelled(address indexed owner, uint64 indexed operatorId);
    /**
     * @dev Emitted when an operator's fee is updated.
     * @param owner Operator's owner.
     * @param blockNumber from which block number.
     * @param fee updated fee value.
     */
    event OperatorFeeExecuted(address indexed owner, uint64 indexed operatorId, uint256 blockNumber, uint256 fee);
    event OperatorWithdrawn(address indexed owner, uint64 indexed operatorId, uint256 value);
    event FeeRecipientAddressUpdated(address indexed owner, address recipientAddress);

    /**
     * @dev Emitted when the operators changed its privacy status
     * @param operatorIds operators' IDs.
     * @param toPrivate Flag that indicates if the operators are being set to private (true) or public (false).
     */
    event OperatorPrivacyStatusUpdated(uint64[] operatorIds, bool toPrivate);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../../interfaces/ssvProxy/ISSVProxyFactory.sol";
import "../../interfaces/ssv-network/ISSVNetwork.sol";
import "../../libraries/DepositStruct.sol";
import "../access/IOwnableWithOperator.sol";

/// @dev External interface of SSVProxy declared to support ERC165 detection.
interface ISSVProxy is IOwnableWithOperator, IERC165 {
    /// @notice Emits when SSVProxy instance is initialized
    /// @param _feeManager FeeManager instance that determines the identity of this SSVProxy instance
    event Initialized(address indexed _feeManager);

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

    /// @notice Emits when an arbitrary external contract has been called by owner via SSVProxy
    /// @param _contract external contract address
    /// @param _selector selector of the called function
    event SuccessfullyCalledExternalContract(
        address indexed _contract,
        bytes4 indexed _selector
    );

    /// @notice _referenceFeeManager should implement IFeeManager interface
    /// @param _passedAddress passed address for _referenceFeeManager
    error NotFeeManager(address _passedAddress);

    /// @notice Should be a SSVProxyFactory contract
    /// @param _passedAddress passed address that does not support ISSVProxyFactory interface
    error NotSSVProxyFactory(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 CallerNotClient(address _caller, address _client);

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

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

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

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

    /// @notice Initialize the SSVProxy instance
    /// @dev Should only be called by SSVProxyFactory
    /// @param _feeManager FeeManager instance that determines the identity of this SSVProxy instance
    function initialize(address _feeManager) external;

    /// @notice Call an arbitrary external contract with SSVProxy as a msg.sender
    /// @dev Should be called by owner only
    /// @dev This function can help e.g. in claiming airdrops
    /// @param _contract external contract address
    /// @param _calldata calldata for the external contract
    function callAnyContract(
        address _contract,
        bytes calldata _calldata
    ) external;

    /// @notice Registers new validators on the SSV Network
    /// @dev Should be called by SSVProxyFactory only
    /// @param publicKeys The public keys of the new validators
    /// @param operatorIds Array of IDs of operators managing this validator
    /// @param sharesData Encrypted shares related to the new validators
    /// @param amount Amount of SSV tokens to be deposited
    /// @param cluster Cluster to be used with the new validator
    function bulkRegisterValidators(
        bytes[] calldata publicKeys,
        uint64[] calldata operatorIds,
        bytes[] calldata sharesData,
        uint256 amount,
        ISSVNetwork.Cluster calldata cluster
    ) 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 Operator 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 Operator only
    /// @param _to destination address
    /// @param _amount SSV token amount to be withdrawn
    function withdrawSSVTokens(address _to, uint256 _amount) external;

    /// @notice Withdraw all SSV tokens from this contract to SSVProxyFactory
    /// @dev Should be called by Operator only
    function withdrawAllSSVTokensToFactory() external;

    /// @notice Withdraw SSV tokens from SSV clusters to SSVProxyFactory
    /// @dev Should be called by Operator only
    /// @param _tokenAmount SSV token amount to be withdrawn
    /// @param _operatorIds SSV operator IDs
    /// @param _clusters SSV clusters
    function withdrawFromSSVToFactory(
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster[] calldata _clusters
    ) external;

    /// @notice Set a new fee recipient address for this contract (cluster owner)
    /// @dev Should be called by Operator only.
    /// Another FeeManager 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 Operator to determine the fee recipient since Operator is paying SSV tokens and EL rewards are a way to compansate for them.
    /// Other operators are compansated via SSV tokens paid by Operator.
    /// @param _feeRecipientAddress fee recipient address to set
    function setFeeRecipientAddress(address _feeRecipientAddress) external;

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

    /// @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 FeeManager instance accociated with this contract
    /// @return FeeManager instance address
    function getFeeManager() external view returns (address);
}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

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

import "../../interfaces/ssv-network/ISSVNetwork.sol";
import "../../interfaces/ssv-network/external/ISSVWhitelistingContract.sol";
import "../access/IOwnableWithOperator.sol";
import "../../libraries/DepositStruct.sol";
import "../../constants/StakingConstants.sol";

/// @dev External interface of SSVProxyFactory
interface ISSVProxyFactory is
    ISSVWhitelistingContract,
    IOwnableWithOperator,
    IERC165
{
    /// @notice Emits when batch registration of validator with SSV is completed
    /// @param _proxy address of SSVProxy that was used for registration and became the cluster owner
    event RegistrationCompleted(address indexed _proxy);

    /// @notice Emits when a new SSVProxy instance was deployed and initialized
    /// @param _ssvProxy newly deployed SSVProxy instance address
    /// @param _client client address
    /// @param _feeManager FeeManager instance address
    event SSVProxyCreated(
        address indexed _ssvProxy,
        address indexed _client,
        address indexed _feeManager
    );

    /// @notice Emits when a new reference FeeManager has been set
    /// @param _referenceFeeManager new reference FeeManager address
    event ReferenceFeeManagerSet(address indexed _referenceFeeManager);

    /// @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 MaxSsvTokenAmountPerValidatorSet(
        uint112 _maxSsvTokenAmountPerValidator
    );

    /// @notice Emits when a new reference SSVProxy has been set
    /// @param _referenceSSVProxy new reference SSVProxy address
    event ReferenceSSVProxySet(address indexed _referenceSSVProxy);

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

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

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

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

    /// @notice Emits when client deposited their ETH for SSV staking
    /// @param _depositId ID of client deposit (derived from ETH Deposit WithdrawalCredentials, ETH amount per validator in wei, fee distributor instance address)
    /// @param _operator address who sent ETH
    /// @param _ssvProxy address of the client instance of SSVProxy
    /// @param _eth2WithdrawalCredentials ETH Deposit WithdrawalCredentials
    /// @param _clientAddress address of the client
    /// @param _referrerAddress address of the referrer
    /// @param _feeManagerInstance address of the client instance of FeeManager
    /// @param _ethAmountInWei amount of deposited ETH in wei
    event EthForSsvStakingDeposited(
        bytes32 indexed _depositId,
        address indexed _operator,
        address indexed _ssvProxy,
        bytes32 _eth2WithdrawalCredentials,
        address _clientAddress,
        address _referrerAddress,
        address _feeManagerInstance,
        uint256 _ethAmountInWei
    );

    /// @notice Passed address is not a valid GatewayDepositor
    /// @param _passedAddress Passed address
    error NotGatewayEth2Deposit(address _passedAddress);

    /// @notice Passed address is not a valid FeeManagerFactory
    /// @param _passedAddress Passed address
    error NotFeeManagerFactory(address _passedAddress);

    /// @notice Passed address is not a valid FeeManager
    /// @param _passedAddress Passed address
    error NotFeeManager(address _passedAddress);

    /// @notice Passed address is not a valid SSVProxy
    /// @param _passedAddress Passed address
    error NotSSVProxy(address _passedAddress);

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

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

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

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

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

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

    /// @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 DepositDataArraysShouldHaveTheSameLength(
        uint256 _ssvValidatorsLength,
        uint256 _signaturesLength,
        uint256 _depositDataRootsLength
    );

    /// @notice SSVProxy should have already been deployed for the given FeeManager instance
    /// @param _feeManagerInstance client FeeManager instance
    error SSVProxyDoesNotExist(address _feeManagerInstance);

    /// @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 SSVProxy instances
    /// @param _referenceSSVProxy template to be used for new SSVProxy instances
    function setReferenceSSVProxy(address _referenceSSVProxy) external;

    /// @notice Allow selectors (function signatures) for clients to call on SSVNetwork via SSVProxy
    /// @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 SSVProxy
    /// @param _selectors selectors (function signatures) to disallow for clients
    function removeAllowedSelectorsForClient(
        bytes4[] calldata _selectors
    ) external;

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

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

    /// @notice Set template to be used for new FeeManager instances
    /// @param _referenceFeeManager template to be used for new FeeManager instances
    function setReferenceFeeManager(address _referenceFeeManager) external;

    /// @notice Computes the address of a SSVProxy created by `_createSSVProxy` function
    /// @dev SSVProxy instances are guaranteed to have the same address if _feeManagerInstance is the same
    /// @param _feeManagerInstance The address of FeeManager instance
    /// @return address client SSVProxy instance that will be or has been deployed
    function predictSSVProxyAddress(
        address _feeManagerInstance
    ) external view returns (address);

    /// @notice Computes the address of a SSVProxy created by `_createSSVProxy` function
    /// @param _referenceFeeManager The address of the reference implementation of FeeManager 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 client SSVProxy instance that will be or has been deployed
    function predictSSVProxyAddress(
        address _referenceFeeManager,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external view returns (address);

    /// @notice Computes the address of a SSVProxy for the default referenceFeeManager
    /// @param _clientConfig address and basis points (percent * 100) of the client
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer.
    /// @return address client SSVProxy instance that will be or has been deployed
    function predictSSVProxyAddress(
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig
    ) external view returns (address);

    /// @notice Computes the address of a SSVProxy for the default referenceFeeManager and referrerConfig
    /// @param _clientConfig address and basis points (percent * 100) of the client
    /// @return address client SSVProxy instance that will be or has been deployed
    function predictSSVProxyAddress(
        FeeRecipient calldata _clientConfig
    ) external view returns (address);

    /// @notice Deploy SSVProxy instance if not deployed before
    /// @param _feeManagerInstance The address of FeeManager instance
    /// @return ssvProxyInstance client SSVProxy instance that has been deployed
    function createSSVProxy(
        address _feeManagerInstance
    ) external returns (address ssvProxyInstance);

    /// @notice Deposit unlimited amount of ETH for SSV staking
    /// @dev Callable by clients
    /// @param _eth2WithdrawalCredentials ETH Deposit withdrawal credentials
    /// @param _ethAmountPerValidatorInWei amount of ETH to deposit per 1 validator (should be >= 32 and <= 2048)
    /// @param _clientConfig address and basis points (percent * 100) of the client
    /// @param _referrerConfig address and basis points (percent * 100) of the referrer.
    /// @param _extraData any other data to pass to the event listener
    /// @return depositId ID of client deposit (derived from ETH Deposit WithdrawalCredentials, ETH amount per validator in wei, fee distributor instance address)
    /// @return feeManagerInstance client FeeManager instance
    /// @return ssvProxy client SSVProxy instance that became the SSV cluster owner
    function addEth(
        bytes32 _eth2WithdrawalCredentials,
        uint96 _ethAmountPerValidatorInWei,
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig,
        bytes calldata _extraData
    )
        external
        payable
        returns (
            bytes32 depositId,
            address feeManagerInstance,
            address ssvProxy
        );

    /// @notice Send ETH to ETH Deposit DepositContract on behalf of the client and register validators with SSV (up to 60, calldata size is the limit)
    /// @dev Callable by Operator only.
    /// @param _eth2WithdrawalCredentials ETH Deposit withdrawal credentials
    /// @param _ethAmountPerValidatorInWei amount of ETH to deposit per 1 validator (should be >= 32 and <= 2048)
    /// @param _feeManagerInstance user FeeManager instance that determines the terms of staking service
    /// @param _operatorAddress operator address
    /// @param _depositData signatures and depositDataRoots from Beacon deposit data
    /// @param _operatorIds SSV operator IDs
    /// @param _publicKeys validator public keys
    /// @param _sharesData encrypted shares related to the validator
    /// @param _amount amount of ERC-20 SSV tokens to deposit into the cluster
    /// @param _cluster SSV cluster
    /// @return ssvProxy client SSVProxy instance that became the SSV cluster owner
    function makeBeaconDepositsAndRegisterValidators(
        bytes32 _eth2WithdrawalCredentials,
        uint96 _ethAmountPerValidatorInWei,
        address _feeManagerInstance,
        address _operatorAddress,
        DepositData calldata _depositData,
        uint64[] calldata _operatorIds,
        bytes[] calldata _publicKeys,
        bytes[] calldata _sharesData,
        uint256 _amount,
        ISSVNetwork.Cluster calldata _cluster
    ) external returns (address ssvProxy);

    /// @notice Deposit SSV tokens from SSVProxyFactory to SSV cluster
    /// @dev Can only be called by SSVProxyFactory owner
    /// @param _clusterOwner SSV cluster owner (usually, SSVProxy instance)
    /// @param _tokenAmount SSV token amount to be deposited
    /// @param _operatorIds SSV operator IDs
    /// @param _cluster SSV cluster
    function depositToSSV(
        address _clusterOwner,
        uint256 _tokenAmount,
        uint64[] calldata _operatorIds,
        ISSVNetwork.Cluster calldata _cluster
    ) external;

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

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

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

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

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

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

    /// @notice Returns a template set by Operator to be used for new FeeManager instances
    /// @return a template set by Operator to be used for new FeeManager instances
    function getReferenceFeeManager() external view returns (address);

    /// @notice Returns a template set by Operator to be used for new SSVProxy instances
    /// @return a template set by Operator to be used for new SSVProxy instances
    function getReferenceSSVProxy() external view returns (address);

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

File 32 of 32 : DepositStruct.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import "../interfaces/ssv-network/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;
    ISSVNetworkCore.Cluster cluster;
    uint256 tokenAmount;
    bytes32 ssvSlot0;
}

/// @member depositId ID of the deposit
/// @member operator address of the operator
/// @member feeManagerInstance address of the FeeManager instance
/// @member ssvProxy address of the SSVProxy instance
/// @member ethAmount amount of ETH deposited
/// @member blockNumber block number of the deposit
struct AddEthData {
    bytes32 depositId;
    address operator;
    address feeManagerInstance;
    address ssvProxy;
    uint256 ethAmount;
    uint256 blockNumber;
}

/// @dev status of the client deposit
/// @member None default status indicating that no ETH is waiting to be forwarded to Beacon DepositContract
/// @member EthAdded client added ETH
/// @member BeaconDepositInProgress Service has forwarded some (but not all) ETH to Beacon DepositContract
/// If all ETH has been forwarded, the status will be None.
/// @member ServiceRejected Service has rejected the service for a given FeeManager instance
// The client can get a refund immediately.
enum ClientDepositStatus {
    None,
    EthAdded,
    BeaconDepositInProgress,
    ServiceRejected
}

/// @dev 256 bit struct
/// @member ethDepositOperator address of the operator
/// @member amount ETH in wei to be used for an ETH Deposit deposit corresponding to a particular FeeManager instance
/// @member expiration block timestamp after which the client will be able to get a refund
/// @member status deposit status
/// @member ethAmountPerValidatorInWei amount of ETH to deposit per 1 validator (should be >= 32 and <= 2048)
struct ClientDeposit {
    address ethDepositOperator;
    uint112 amount;
    uint40 expiration;
    ClientDepositStatus status;
    uint96 ethAmountPerValidatorInWei;
}

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

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"ssvProxyFactory_","type":"address"},{"internalType":"address","name":"ssvNetwork_","type":"address"},{"internalType":"address","name":"ssvToken_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"AddressNeitherOperatorNorOwner","type":"error"},{"inputs":[],"name":"AmountOfParametersError","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"CallerNeitherOperatorNorOwner","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"}],"name":"CallerNeitherOperatorNorOwnerNorClient","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_client","type":"address"}],"name":"CallerNotClient","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"CallerNotOwner","type":"error"},{"inputs":[],"name":"CanNotBurn","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"NotFeeManager","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"NotSSVProxyFactory","type":"error"},{"inputs":[{"internalType":"address","name":"_msgSender","type":"address"},{"internalType":"contract ISSVProxyFactory","name":"_actualFactory","type":"address"}],"name":"NotSSVProxyFactoryCalled","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"SameOperator","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"SelectorNotAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"ZeroNewOperator","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":"_feeManager","type":"address"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_contract","type":"address"},{"indexed":true,"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"SuccessfullyCalledExternalContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_caller","type":"address"},{"indexed":true,"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"SuccessfullyCalledViaFallback","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"}],"name":"bulkExitValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"publicKeys","type":"bytes[]"},{"internalType":"uint64[]","name":"operatorIds","type":"uint64[]"},{"internalType":"bytes[]","name":"sharesData","type":"bytes[]"},{"internalType":"uint256","name":"amount","type":"uint256"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster","name":"cluster","type":"tuple"}],"name":"bulkRegisterValidators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"callAnyContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenAmount","type":"uint256"},{"internalType":"uint64[]","name":"_operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster[]","name":"_clusters","type":"tuple[]"}],"name":"depositToSSV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getClient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"feeManager_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"_feeRecipientAddress","type":"address"}],"name":"setFeeRecipientAddress","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":[],"name":"withdrawAllSSVTokensToFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenAmount","type":"uint256"},{"internalType":"uint64[]","name":"_operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster[]","name":"_clusters","type":"tuple[]"}],"name":"withdrawFromSSV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenAmount","type":"uint256"},{"internalType":"uint64[]","name":"_operatorIds","type":"uint64[]"},{"components":[{"internalType":"uint32","name":"validatorCount","type":"uint32"},{"internalType":"uint64","name":"networkFeeIndex","type":"uint64"},{"internalType":"uint64","name":"index","type":"uint64"},{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"balance","type":"uint256"}],"internalType":"struct ISSVNetworkCore.Cluster[]","name":"_clusters","type":"tuple[]"}],"name":"withdrawFromSSVToFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawSSVTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
0x41d77C33097DA5aa4050cd815beE535A2534Dd07
Loading...
Loading
Loading...
Loading
[ 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.