ETH Price: $3,356.48 (-0.96%)
Gas: 4.95 Gwei

Contract

0xd6781E7F15752d3F5764609a42A803A7345DE25c
 

Overview

ETH Balance

0.096306544071651418 ETH

Eth Value

$323.25 (@ $3,356.48/ETH)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer214595622024-12-22 17:35:117 days ago1734888911IN
0xd6781E7F...7345DE25c
0.02877294 ETH0.000161246.21543628
Transfer214353652024-12-19 8:26:2310 days ago1734596783IN
0xd6781E7F...7345DE25c
0.06753359 ETH0.0002668410.28626204
Withdraw214134672024-12-16 7:04:2313 days ago1734332663IN
0xd6781E7F...7345DE25c
0 ETH0.000698418.80009276
Transfer213999402024-12-14 9:44:4715 days ago1734169487IN
0xd6781E7F...7345DE25c
0.02615483 ETH0.000204737.89200707
Transfer213917602024-12-13 6:20:3516 days ago1734070835IN
0xd6781E7F...7345DE25c
0.02794492 ETH0.0002978511.48167146
Transfer213896672024-12-12 23:19:3516 days ago1734045575IN
0xd6781E7F...7345DE25c
0.05961256 ETH0.000337212.99859833
Transfer213866202024-12-12 13:07:4717 days ago1734008867IN
0xd6781E7F...7345DE25c
0.40004059 ETH0.0005803822.37236422
Transfer213809052024-12-11 17:58:3517 days ago1733939915IN
0xd6781E7F...7345DE25c
0.06588253 ETH0.0005628721.69762372
Transfer213732622024-12-10 16:21:3519 days ago1733847695IN
0xd6781E7F...7345DE25c
0.03512329 ETH0.0016478363.52006533
Transfer213709012024-12-10 8:26:1119 days ago1733819171IN
0xd6781E7F...7345DE25c
0.02378162 ETH0.0003442913.2717394
Withdraw213084352024-12-01 15:03:4728 days ago1733065427IN
0xd6781E7F...7345DE25c
0 ETH0.0015800519.67539648
Transfer212988282024-11-30 6:53:2329 days ago1732949603IN
0xd6781E7F...7345DE25c
0.07609028 ETH0.000187497.22744958
Transfer212853862024-11-28 9:44:4731 days ago1732787087IN
0xd6781E7F...7345DE25c
0.03961774 ETH0.000172136.63531478
Withdraw211956722024-11-15 21:07:3543 days ago1731704855IN
0xd6781E7F...7345DE25c
0 ETH0.0023205829.24423161
Transfer211623412024-11-11 5:26:3548 days ago1731302795IN
0xd6781E7F...7345DE25c
0.08210409 ETH0.0004016515.48274711
Transfer211258682024-11-06 3:16:5953 days ago1730863019IN
0xd6781E7F...7345DE25c
0.10884951 ETH0.0017928369.1094162
Withdraw210853752024-10-31 11:39:3559 days ago1730374775IN
0xd6781E7F...7345DE25c
0 ETH0.000566067.133604
Transfer210821342024-10-31 0:47:5959 days ago1730335679IN
0xd6781E7F...7345DE25c
0.02456412 ETH0.000248239.56893958
Transfer210562132024-10-27 9:56:5963 days ago1730023019IN
0xd6781E7F...7345DE25c
0.03622178 ETH0.000181036.97855653
Transfer209776932024-10-16 11:01:5974 days ago1729076519IN
0xd6781E7F...7345DE25c
0.05481268 ETH0.0003183212.27079661
Withdraw209704212024-10-15 10:38:3575 days ago1728988715IN
0xd6781E7F...7345DE25c
0 ETH0.0009411.70097014
Transfer209081062024-10-06 17:48:4784 days ago1728236927IN
0xd6781E7F...7345DE25c
0.038557 ETH0.000199587.69336689
Transfer208847832024-10-03 11:46:4787 days ago1727956007IN
0xd6781E7F...7345DE25c
0.06620274 ETH0.000114344.40783983
Transfer208723702024-10-01 18:15:1188 days ago1727806511IN
0xd6781E7F...7345DE25c
0.1254364 ETH0.0006878426.51465569
Transfer208671582024-10-01 0:48:1189 days ago1727743691IN
0xd6781E7F...7345DE25c
0.01832453 ETH0.000135375.21838993
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
214134672024-12-16 7:04:2313 days ago1734332663
0xd6781E7F...7345DE25c
0.31927019 ETH
214134672024-12-16 7:04:2313 days ago1734332663
0xd6781E7F...7345DE25c
0.31927019 ETH
213084352024-12-01 15:03:4728 days ago1733065427
0xd6781E7F...7345DE25c
0.05785401 ETH
213084352024-12-01 15:03:4728 days ago1733065427
0xd6781E7F...7345DE25c
0.05785401 ETH
211956722024-11-15 21:07:3543 days ago1731704855
0xd6781E7F...7345DE25c
0.0954768 ETH
211956722024-11-15 21:07:3543 days ago1731704855
0xd6781E7F...7345DE25c
0.0954768 ETH
210853752024-10-31 11:39:3559 days ago1730374775
0xd6781E7F...7345DE25c
0.05779929 ETH
210853752024-10-31 11:39:3559 days ago1730374775
0xd6781E7F...7345DE25c
0.05779929 ETH
209704212024-10-15 10:38:3575 days ago1728988715
0xd6781E7F...7345DE25c
0.12426034 ETH
209704212024-10-15 10:38:3575 days ago1728988715
0xd6781E7F...7345DE25c
0.12426034 ETH
208645562024-09-30 16:06:2390 days ago1727712383
0xd6781E7F...7345DE25c
0.0262934 ETH
208645562024-09-30 16:06:2390 days ago1727712383
0xd6781E7F...7345DE25c
0.0262934 ETH
207622052024-09-16 9:10:59104 days ago1726477859
0xd6781E7F...7345DE25c
0.08386411 ETH
207622052024-09-16 9:10:59104 days ago1726477859
0xd6781E7F...7345DE25c
0.08386411 ETH
206555932024-09-01 12:02:59119 days ago1725192179
0xd6781E7F...7345DE25c
0.02636901 ETH
206555932024-09-01 12:02:59119 days ago1725192179
0xd6781E7F...7345DE25c
0.02636901 ETH
205364652024-08-15 20:38:47135 days ago1723754327
0xd6781E7F...7345DE25c
0.11817611 ETH
205364652024-08-15 20:38:47135 days ago1723754327
0xd6781E7F...7345DE25c
0.11817611 ETH
204291542024-07-31 21:15:47150 days ago1722460547
0xd6781E7F...7345DE25c
0.11049551 ETH
204291542024-07-31 21:15:47150 days ago1722460547
0xd6781E7F...7345DE25c
0.11049551 ETH
203104142024-07-15 7:27:11167 days ago1721028431
0xd6781E7F...7345DE25c
0.15755632 ETH
203104142024-07-15 7:27:11167 days ago1721028431
0xd6781E7F...7345DE25c
0.15755632 ETH
202043122024-06-30 11:50:11182 days ago1719748211
0xd6781E7F...7345DE25c
0.05982872 ETH
202043122024-06-30 11:50:11182 days ago1719748211
0xd6781E7F...7345DE25c
0.05982872 ETH
201068012024-06-16 20:40:35195 days ago1718570435
0xd6781E7F...7345DE25c
0.07721102 ETH
View All Internal Transactions
Loading...
Loading

Minimal Proxy Contract for 0xc4aed615614f26c8df4eaaf4a1caea9184aeb3de

Contract Name:
FeeDistributor

Compiler Version
v0.8.10+commit.fc410830

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

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

pragma solidity 0.8.10;

import "../@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import "../feeDistributorFactory/IFeeDistributorFactory.sol";
import "../assetRecovering/OwnableTokenRecoverer.sol";
import "./IFeeDistributor.sol";
import "../oracle/IOracle.sol";

/**
* @notice Should be a Oracle contract
* @param _passedAddress passed address that does not support IOracle interface
*/
error FeeDistributor__NotOracle(address _passedAddress);

/**
* @notice Initial client-only CL rewards must be zero
* @param _firstValidatorId passed value for firstValidatorId
*/
error FeeDistributor__InvalidFirstValidatorId(uint64 _firstValidatorId);

/**
* @notice Initial client-only CL rewards must be zero
* @param _validatorCount passed value for validatorCount
*/
error FeeDistributor__InvalidValidatorCount(uint16 _validatorCount);

/**
* @notice Should be a FeeDistributorFactory contract
* @param _passedAddress passed address that does not support IFeeDistributorFactory interface
*/
error FeeDistributor__NotFactory(address _passedAddress);

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

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

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

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

/**
* @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 FeeDistributor__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 FeeDistributor__ReferrerAddressEqualsService(address _passedAddress);

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

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

/**
* @notice `initialize` should only be called once.
* @param _existingClient address of the client with which the contact has already been initialized.
*/
error FeeDistributor__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 FeeDistributor__ClientNotSet();

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

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

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

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

/**
* @notice zero ether balance
*/
error FeeDistributor__NothingToWithdraw();

/**
* @notice cannot withdraw until rewards (CL+EL) are enough to be split
*/
error FeeDistributor__WaitForEnoughRewardsToWithdraw();

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

/**
* @title Contract receiving MEV and priority fees
* and distributing them to the service and the client.
*/
contract FeeDistributor is OwnableTokenRecoverer, ReentrancyGuard, ERC165, IFeeDistributor {
    // State variables

    /**
    * @notice address of FeeDistributorFactory
    */
    IFeeDistributorFactory private immutable i_factory;

    /**
    * @notice address of Oracle
    */
    IOracle private immutable i_oracle;

    /**
    * @notice address of the service (P2P) fee recipient
    */
    address payable private immutable i_service;

    /**
    * @notice client config (address of the client, client basis points)
    */
    FeeRecipient private s_clientConfig;

    /**
    * @notice referrer config (address of the referrer, referrer basis points)
    */
    FeeRecipient private s_referrerConfig;

    /**
    * @notice amount of CL rewards (in Wei) that should belong to the client only
    * and should not be considered for splitting between the service and the referrer
    */
    ValidatorData private s_validatorData;

    /**
    * @dev Set values that are constant, common for all the clients, known at the initial deploy time.
    * @param _factory address of FeeDistributorFactory
    * @param _service address of the service (P2P) fee recipient
    */
    constructor(
        address _oracle,
        address _factory,
        address payable _service
    ) {
        if (!ERC165Checker.supportsInterface(_oracle, type(IOracle).interfaceId)) {
            revert FeeDistributor__NotOracle(_factory);
        }
        if (!ERC165Checker.supportsInterface(_factory, type(IFeeDistributorFactory).interfaceId)) {
            revert FeeDistributor__NotFactory(_factory);
        }
        if (_service == address(0)) {
            revert FeeDistributor__ZeroAddressService();
        }

        i_oracle = IOracle(_oracle);
        i_factory = IFeeDistributorFactory(_factory);
        i_service = _service;

        bool serviceCanReceiveEther = _sendValue(_service, 0);
        if (!serviceCanReceiveEther) {
            revert FeeDistributor__ServiceCannotReceiveEther(_service);
        }
    }

    /**
    * @dev Throws if called by any account other than the client.
    */
    modifier onlyClient() {
        address caller = _msgSender();
        address clientAddress = s_clientConfig.recipient;

        if (clientAddress != caller) {
            revert FeeDistributor__CallerNotClient(caller, clientAddress);
        }
        _;
    }

    // Functions

    /**
    * @notice Accept ether from transactions
    */
    receive() external payable {
        // only accept ether in an instance, not in a template
        if (s_clientConfig.recipient == address(0)) {
            revert FeeDistributor__ClientNotSet();
        }
    }

    /**
    * @notice Set client address.
    * @dev Could not be in the constructor since it is different for different clients.
    * @dev _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 _validatorData clientOnlyClRewards, firstValidatorId, and validatorCount
    */
    function initialize(
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig,
        ValidatorData calldata _validatorData
    ) external {
        if (msg.sender != address(i_factory)) {
            revert FeeDistributor__NotFactoryCalled(msg.sender, i_factory);
        }
        if (_clientConfig.recipient == address(0)) {
            revert FeeDistributor__ZeroAddressClient();
        }
        if (_clientConfig.recipient == i_service) {
            revert FeeDistributor__ClientAddressEqualsService(_clientConfig.recipient);
        }
        if (s_clientConfig.recipient != address(0)) {
            revert FeeDistributor__ClientAlreadySet(s_clientConfig.recipient);
        }
        if (_clientConfig.basisPoints > 10000) {
            revert FeeDistributor__InvalidClientBasisPoints(_clientConfig.basisPoints);
        }
        if (_validatorData.firstValidatorId == 0) {
            revert FeeDistributor__InvalidFirstValidatorId(_validatorData.firstValidatorId);
        }
        if (_validatorData.validatorCount == 0) {
            revert FeeDistributor__InvalidValidatorCount(_validatorData.validatorCount);
        }

        if (_referrerConfig.recipient != address(0)) {// if there is a referrer
            if (_referrerConfig.recipient == i_service) {
                revert FeeDistributor__ReferrerAddressEqualsService(_referrerConfig.recipient);
            }
            if (_referrerConfig.recipient == _clientConfig.recipient) {
                revert FeeDistributor__ReferrerAddressEqualsClient(_referrerConfig.recipient);
            }
            if (_clientConfig.basisPoints + _referrerConfig.basisPoints > 10000) {
                revert FeeDistributor__ClientPlusReferralBasisPointsExceed10000(_clientConfig.basisPoints, _referrerConfig.basisPoints);
            }

            // set referrer config
            s_referrerConfig = _referrerConfig;

        } else {// if there is no referrer
            if (_referrerConfig.basisPoints != 0) {
                revert FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero(_referrerConfig.basisPoints);
            }
        }

        // set client config
        s_clientConfig = _clientConfig;

        // set validator data
        s_validatorData = _validatorData;

        emit Initialized(
            _clientConfig.recipient,
            _clientConfig.basisPoints,
            _referrerConfig.recipient,
            _referrerConfig.basisPoints
        );

        bool clientCanReceiveEther = _sendValue(_clientConfig.recipient, 0);
        if (!clientCanReceiveEther) {
            revert FeeDistributor__ClientCannotReceiveEther(_clientConfig.recipient);
        }
        if (_referrerConfig.recipient != address(0)) {// if there is a referrer
            bool referrerCanReceiveEther = _sendValue(_referrerConfig.recipient, 0);
            if (!referrerCanReceiveEther) {
                revert FeeDistributor__ReferrerCannotReceiveEther(_referrerConfig.recipient);
            }
        }
    }

    /**
    * @notice Withdraw the whole balance of the contract according to the pre-defined basis points.
    * @dev In case someone (either service, or client, or referrer) fails to accept ether,
    * the owner will be able to recover some of their share.
    * This scenario is very unlikely. It can only happen if that someone is a contract
    * whose receive function changed its behavior since FeeDistributor's initialization.
    * It can never happen unless the receiving party themselves wants it to happen.
    * We strongly recommend against intentional reverts in the receive function
    * because the remaining parties might call `withdraw` again multiple times without waiting
    * for the owner to recover ether for the reverting party.
    * In fact, as a punishment for the reverting party, before the recovering,
    * 1 more regular `withdraw` will happen, rewarding the non-reverting parties again.
    * `recoverEther` function is just an emergency backup plan and does not replace `withdraw`.
    *
    * @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes)
    * @param _amountInGwei total CL rewards earned by all validators in GWei (see _validatorCount)
    */
    function withdraw(
        bytes32[] calldata _proof,
        uint256 _amountInGwei
    ) external nonReentrant {
        if (s_clientConfig.recipient == address(0)) {
            revert FeeDistributor__ClientNotSet();
        }

        // get the contract's balance
        uint256 balance = address(this).balance;

        if (balance == 0) {
            // revert if there is no ether to withdraw
            revert FeeDistributor__NothingToWithdraw();
        }

        // read from storage once
        ValidatorData memory vd = s_validatorData;

        // verify the data from the caller against the oracle
        i_oracle.verify(_proof, vd.firstValidatorId, vd.validatorCount, _amountInGwei);

        // Gwei to Wei
        uint256 amount = _amountInGwei * (10 ** 9);

        if (balance + amount < vd.clientOnlyClRewards) {
            // Can happen if the client has called emergencyEtherRecoveryWithoutOracleData before
            // but the actual rewards amount now appeared to be lower than the already split.
            // Should happen rarely.

            revert FeeDistributor__WaitForEnoughRewardsToWithdraw();
        }

        // total to split = EL + CL - already split part of CL (should be OK unless halfBalance < serviceAmount)
        uint256 totalAmountToSplit = balance + amount - vd.clientOnlyClRewards;

        // set client basis points to value from storage config
        uint256 clientBp = s_clientConfig.basisPoints;

        // how much should service get
        uint256 serviceAmount = totalAmountToSplit - ((totalAmountToSplit * clientBp) / 10000);

        uint256 halfBalance = balance / 2;

        // how much should client get
        uint256 clientAmount;

        // if a half of the available balance is not enough to cover service (and referrer) shares
        // can happen when CL rewards (only accessible by client) are way much than EL rewards
        if (serviceAmount > halfBalance) {
            // client gets 50% of EL rewards
            clientAmount = halfBalance;

            // service (and referrer) get 50% of EL rewards combined (+1 wei in case balance is odd)
            serviceAmount = balance - halfBalance;

            // update the total amount being split to a smaller value to fit the actual balance of this contract
            totalAmountToSplit = (halfBalance * 10000) / (10000 - clientBp);
        } else {
            // send the remaining balance to client
            clientAmount = balance - serviceAmount;
        }

        // client gets the rest from CL as not split anymore amount
        s_validatorData.clientOnlyClRewards = uint176(vd.clientOnlyClRewards + (totalAmountToSplit - balance));

        // how much should referrer get
        uint256 referrerAmount;

        if (s_referrerConfig.recipient != address(0)) {
            // if there is a referrer

            referrerAmount = (totalAmountToSplit * s_referrerConfig.basisPoints) / 10000;

            serviceAmount -= referrerAmount;

            // Send ETH to referrer. Ignore the possible yet unlikely revert in the receive function.
            _sendValue(s_referrerConfig.recipient, referrerAmount);
        }

        // Send ETH to service. Ignore the possible yet unlikely revert in the receive function.
        _sendValue(i_service, serviceAmount);

        // Send ETH to client. Ignore the possible yet unlikely revert in the receive function.
        _sendValue(s_clientConfig.recipient, clientAmount);

        emit Withdrawn(serviceAmount, clientAmount, referrerAmount);
    }

    /**
    * @notice Recover ether in a rare case when either service, or client, or referrer
    * refuse to accept ether.
    * @param _to receiver address
    * @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes)
    * @param _amountInGwei total CL rewards earned by all validators in GWei (see _validatorCount)
    */
    function recoverEther(
        address payable _to,
        bytes32[] calldata _proof,
        uint256 _amountInGwei
    ) external onlyOwner {
        this.withdraw(_proof, _amountInGwei);

        // get the contract's balance
        uint256 balance = address(this).balance;

        if (balance > 0) { // only happens if at least 1 party reverted in their receive
            bool success = _sendValue(_to, balance);

            if (success) {
                emit EtherRecovered(_to, balance);
            } else {
                emit EtherRecoveryFailed(_to, balance);
            }
        }
    }

    /**
    * @notice SHOULD NEVER BE CALLED NORMALLY!!!! Recover ether if oracle data (Merkle proof) is not available for some reason.
    */
    function emergencyEtherRecoveryWithoutOracleData() external onlyClient nonReentrant {
        // get the contract's balance
        uint256 balance = address(this).balance;

        if (balance == 0) {
            // revert if there is no ether to withdraw
            revert FeeDistributor__NothingToWithdraw();
        }

        uint256 halfBalance = balance / 2;

        // client gets 50% of EL rewards
        uint256 clientAmount = halfBalance;

        // service (and referrer) get 50% of EL rewards combined (+1 wei in case balance is odd)
        uint256 serviceAmount = balance - halfBalance;

        // the total amount being split fits the actual balance of this contract
        uint256 totalAmountToSplit = (halfBalance * 10000) / (10000 - s_clientConfig.basisPoints);

        // client gets the rest from CL as not split anymore amount
        s_validatorData.clientOnlyClRewards = uint176(s_validatorData.clientOnlyClRewards + (totalAmountToSplit - balance));

        // how much should referrer get
        uint256 referrerAmount;

        if (s_referrerConfig.recipient != address(0)) {
            // if there is a referrer

            referrerAmount = (totalAmountToSplit * s_referrerConfig.basisPoints) / 10000;

            serviceAmount -= referrerAmount;

            // Send ETH to referrer. Ignore the possible yet unlikely revert in the receive function.
            _sendValue(s_referrerConfig.recipient, referrerAmount);
        }

        // Send ETH to service. Ignore the possible yet unlikely revert in the receive function.
        _sendValue(i_service, serviceAmount);

        // Send ETH to client. Ignore the possible yet unlikely revert in the receive function.
        _sendValue(s_clientConfig.recipient, clientAmount);

        emit Withdrawn(serviceAmount, clientAmount, referrerAmount);
    }

    /**
     * @dev Returns the factory address
     */
    function factory() external view returns (address) {
        return address(i_factory);
    }

    /**
     * @dev Returns the service address
     */
    function service() external view returns (address) {
        return i_service;
    }

    /**
     * @dev Returns the client address
     */
    function client() external view returns (address) {
        return s_clientConfig.recipient;
    }

    /**
     * @dev Returns the client basis points
     */
    function clientBasisPoints() external view returns (uint256) {
        return s_clientConfig.basisPoints;
    }

    /**
    * @dev Returns the referrer address
     */
    function referrer() external view returns (address) {
        return s_referrerConfig.recipient;
    }

    /**
     * @dev Returns the referrer basis points
     */
    function referrerBasisPoints() external view returns (uint256) {
        return s_referrerConfig.basisPoints;
    }

    /**
    * @dev Returns First Validator Id
    */
    function firstValidatorId() external view returns (uint256) {
        return s_validatorData.firstValidatorId;
    }

    /**
    * @dev Returns a portion of CL rewards that should not be counted during withdraw (belongs to client only)
    */
    function clientOnlyClRewards() external view returns (uint256) {
        return s_validatorData.clientOnlyClRewards;
    }

    /**
    * @dev Returns validator count
    */
    function validatorCount() external view returns (uint256) {
        return s_validatorData.validatorCount;
    }

    /**
    * @dev See {IERC165-supportsInterface}.
    */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IFeeDistributor).interfaceId || super.supportsInterface(interfaceId);
    }

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

    function _sendValue(address payable _recipient, uint256 _amount) internal returns (bool) {
        (bool success, ) = _recipient.call{
            value: _amount,
            gas: gasleft() / 4 // to prevent DOS, should be enough in normal cases
        }("");

        return success;
    }
}

File 2 of 19 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity 0.8.10;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

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

pragma solidity 0.8.10;

import "./IERC165.sol";

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

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

pragma solidity 0.8.10;

import "./IERC165.sol";

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

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

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

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

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

        return interfaceIdsSupported;
    }

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

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

        // all interfaces supported
        return true;
    }

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

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

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

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

pragma solidity 0.8.10;

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

/**
 * @dev External interface of FeeDistributorFactory declared to support ERC165 detection.
 */
interface IFeeDistributorFactory is IOwnable, IERC165 {
    // Events

    /**
    * @notice Emits when a new FeeDistributor instance has been created for a client
    * @param _newFeeDistributorAddress address of the newly created FeeDistributor contract instance
    * @param _clientAddress address of the client for whom the new instance was created
    */
    event FeeDistributorCreated(address indexed _newFeeDistributorAddress, address indexed _clientAddress);

    /**
    * @notice Emits when a new FeeDistributor contract address has been set as a reference instance.
    * @param _referenceFeeDistributor the address of the new reference implementation contract
    */
    event ReferenceInstanceSet(address indexed _referenceFeeDistributor);

    /**
    * @notice Emits when a new P2pEth2Depositor contract address has been set.
    * @param _p2pEth2Depositor the address of the new P2pEth2Depositor contract
    */
    event P2pEth2DepositorSet(address indexed _p2pEth2Depositor);

    // Functions

    /**
    * @notice Set a new reference implementation of FeeDistributor contract
    * @param _referenceFeeDistributor the address of the new reference implementation contract
    */
    function setReferenceInstance(address _referenceFeeDistributor) external;

    /**
    * @notice Creates a FeeDistributor instance for a client
    * @dev Emits `FeeDistributorCreated` event with the address of the newly created instance
    * @dev _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 _validatorData clientOnlyClRewards, firstValidatorId, and validatorCount
    * @return newFeeDistributorAddress user FeeDistributor instance that has just been deployed
    */
    function createFeeDistributor(
        IFeeDistributor.FeeRecipient calldata _clientConfig,
        IFeeDistributor.FeeRecipient calldata _referrerConfig,
        IFeeDistributor.ValidatorData calldata _validatorData
    ) external returns (address newFeeDistributorAddress);

    /**
     * @dev Returns the reference FeeDistributor contract address
     */
    function getReferenceFeeDistributor() external view returns (address);
}

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

    /**
     * @dev 256bits-wide structure to store recipient address along with its basisPoints
     */
    struct FeeRecipient {
        /**
        * @notice basis points (percent * 100) of EL rewards that should go to the recipient
        */
        uint96 basisPoints;

        /**
        * @notice address of the recipient
        */
        address payable recipient;
    }

    /**
    * @dev 256bits-wide structure to store clientOnlyClRewards, firstValidatorId, and validatorCount
     */
    struct ValidatorData {
        /**
        * @notice amount of CL rewards (in Wei) that should belong to the client only
        * and should not be considered for splitting between the service and the referrer
        */
        uint176 clientOnlyClRewards;

        /**
        * @notice Validator Id (number of all deposits previously made to ETH2 DepositContract plus 1)
        */
        uint64 firstValidatorId;

        /**
        * @notice Number of validators corresponding to a given FeeDistributor instance, equal to the number of ETH2 deposits made with 1 P2pEth2Depositor's deposit
        */
        uint16 validatorCount;
    }

    // Events

    /**
    * @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 Emits once the client and the optional referrer have been set.
    * @param _client address of the client.
    * @param _clientBasisPoints basis points (percent * 100) of EL rewards that should go to the client
    * @param _referrer address of the referrer.
    * @param _referrerBasisPoints basis points (percent * 100) of EL rewards that should go to the referrer
    */
    event Initialized(
        address indexed _client,
        uint96 _clientBasisPoints,
        address indexed _referrer,
        uint96 _referrerBasisPoints
    );

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

    /**
    * @notice Emits if 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.
    */
    event EtherRecoveryFailed(address indexed _to, uint256 _amount);

    // Functions

    /**
    * @notice Set client address.
    * @dev Could not be in the constructor since it is different for different clients.
    * @dev _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 _validatorData clientOnlyClRewards, firstValidatorId, and validatorCount
    */
    function initialize(
        FeeRecipient calldata _clientConfig,
        FeeRecipient calldata _referrerConfig,
        ValidatorData calldata _validatorData
    ) external;

    /**
    * @notice Withdraw the whole balance of the contract according to the pre-defined basis points.
    * @dev In case someone (either service, or client, or referrer) fails to accept ether,
    * the owner will be able to recover some of their share.
    * This scenario is very unlikely. It can only happen if that someone is a contract
    * whose receive function changed its behavior since FeeDistributor's initialization.
    * It can never happen unless the receiving party themselves wants it to happen.
    * We strongly recommend against intentional reverts in the receive function
    * because the remaining parties might call `withdraw` again multiple times without waiting
    * for the owner to recover ether for the reverting party.
    * In fact, as a punishment for the reverting party, before the recovering,
    * 1 more regular `withdraw` will happen, rewarding the non-reverting parties again.
    * `recoverEther` function is just an emergency backup plan and does not replace `withdraw`.
    *
    * @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes)
    * @param _amount total CL rewards earned by all validators (see _validatorCount)
    */
    function withdraw(
        bytes32[] calldata _proof,
        uint256 _amount
    ) external;

    /**
     * @dev Returns the factory address
     */
    function factory() external view returns (address);

    /**
     * @dev Returns the service address
     */
    function service() external view returns (address);

    /**
     * @dev Returns the client address
     */
    function client() external view returns (address);

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

    /**
    * @dev Returns the referrer address
     */
    function referrer() external view returns (address);

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

    /**
    * @dev Returns First Validator Id
    */
    function firstValidatorId() external view returns (uint256);

    /**
    * @dev Returns a portion of CL rewards that should not be counted during withdraw (belongs to client only)
    */
    function clientOnlyClRewards() external view returns (uint256);

    /**
    * @dev Returns validator count
    */
    function validatorCount() external view returns (uint256);
}

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

pragma solidity 0.8.10;

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

/**
 * @dev External interface of Oracle declared to support ERC165 detection.
 */
interface IOracle is IOwnable, IERC165 {
    // Events

    /**
    * @notice Emits when a new oracle report (Merkle root) recorded
    * @param _root Merkle root
    */
    event Reported(bytes32 indexed _root);

    // Functions

    /**
    * @notice Set a new oracle report (Merkle root)
    * @param _root Merkle root
    */
    function report(bytes32 _root) external;

    /**
    * @notice Verify Merkle proof (that the leaf belongs to the tree)
    * @param _proof Merkle proof (the leaf's sibling, and each non-leaf hash that could not otherwise be calculated without additional leaf nodes)
    * @param _firstValidatorId Validator Id (number of all deposits previously made to ETH2 DepositContract plus 1)
    * @param _validatorCount (number of validators corresponding to a given FeeDistributor instance, equal to the number of ETH2 deposits made with 1 P2pEth2Depositor's deposit)
    * @param _amountInGwei total CL rewards earned by all validators in GWei (see _validatorCount)
    */
    function verify(
        bytes32[] calldata _proof,
        uint64 _firstValidatorId,
        uint16 _validatorCount,
        uint256 _amountInGwei
    ) external view;
}

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

pragma solidity 0.8.10;

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

pragma solidity 0.8.10;

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

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

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

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

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


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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

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

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

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

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

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

pragma solidity 0.8.10;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity 0.8.10;

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

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

Contract ABI

[{"inputs":[{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address payable","name":"_service","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_client","type":"address"}],"name":"FeeDistributor__CallerNotClient","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__ClientAddressEqualsService","type":"error"},{"inputs":[{"internalType":"address","name":"_existingClient","type":"address"}],"name":"FeeDistributor__ClientAlreadySet","type":"error"},{"inputs":[{"internalType":"address","name":"_client","type":"address"}],"name":"FeeDistributor__ClientCannotReceiveEther","type":"error"},{"inputs":[],"name":"FeeDistributor__ClientNotSet","type":"error"},{"inputs":[{"internalType":"uint96","name":"_clientBasisPoints","type":"uint96"},{"internalType":"uint96","name":"_referralBasisPoints","type":"uint96"}],"name":"FeeDistributor__ClientPlusReferralBasisPointsExceed10000","type":"error"},{"inputs":[{"internalType":"uint96","name":"_clientBasisPoints","type":"uint96"}],"name":"FeeDistributor__InvalidClientBasisPoints","type":"error"},{"inputs":[{"internalType":"uint64","name":"_firstValidatorId","type":"uint64"}],"name":"FeeDistributor__InvalidFirstValidatorId","type":"error"},{"inputs":[{"internalType":"uint16","name":"_validatorCount","type":"uint16"}],"name":"FeeDistributor__InvalidValidatorCount","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__NotFactory","type":"error"},{"inputs":[{"internalType":"address","name":"_msgSender","type":"address"},{"internalType":"contract IFeeDistributorFactory","name":"_actualFactory","type":"address"}],"name":"FeeDistributor__NotFactoryCalled","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__NotOracle","type":"error"},{"inputs":[],"name":"FeeDistributor__NothingToWithdraw","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__ReferrerAddressEqualsClient","type":"error"},{"inputs":[{"internalType":"address","name":"_passedAddress","type":"address"}],"name":"FeeDistributor__ReferrerAddressEqualsService","type":"error"},{"inputs":[{"internalType":"uint96","name":"_referrerBasisPoints","type":"uint96"}],"name":"FeeDistributor__ReferrerBasisPointsMustBeZeroIfAddressIsZero","type":"error"},{"inputs":[{"internalType":"address","name":"_referrer","type":"address"}],"name":"FeeDistributor__ReferrerCannotReceiveEther","type":"error"},{"inputs":[{"internalType":"address","name":"_service","type":"address"}],"name":"FeeDistributor__ServiceCannotReceiveEther","type":"error"},{"inputs":[],"name":"FeeDistributor__WaitForEnoughRewardsToWithdraw","type":"error"},{"inputs":[],"name":"FeeDistributor__ZeroAddressClient","type":"error"},{"inputs":[],"name":"FeeDistributor__ZeroAddressService","type":"error"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"OwnableBase__CallerNotOwner","type":"error"},{"inputs":[],"name":"TokenRecoverer__NoBurn","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"_data","type":"bytes"}],"name":"ERC1155Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"ERC20Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"_data","type":"bytes"}],"name":"ERC721Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"EtherRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"EtherRecoveryFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_client","type":"address"},{"indexed":false,"internalType":"uint96","name":"_clientBasisPoints","type":"uint96"},{"indexed":true,"internalType":"address","name":"_referrer","type":"address"},{"indexed":false,"internalType":"uint96","name":"_referrerBasisPoints","type":"uint96"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_serviceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_clientAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_referrerAmount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"client","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clientBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clientOnlyClRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyEtherRecoveryWithoutOracleData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstValidatorId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct IFeeDistributor.FeeRecipient","name":"_clientConfig","type":"tuple"},{"components":[{"internalType":"uint96","name":"basisPoints","type":"uint96"},{"internalType":"address payable","name":"recipient","type":"address"}],"internalType":"struct IFeeDistributor.FeeRecipient","name":"_referrerConfig","type":"tuple"},{"components":[{"internalType":"uint176","name":"clientOnlyClRewards","type":"uint176"},{"internalType":"uint64","name":"firstValidatorId","type":"uint64"},{"internalType":"uint16","name":"validatorCount","type":"uint16"}],"internalType":"struct IFeeDistributor.ValidatorData","name":"_validatorData","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_to","type":"address"},{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"uint256","name":"_amountInGwei","type":"uint256"}],"name":"recoverEther","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"referrer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referrerBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"service","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"validatorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_proof","type":"bytes32[]"},{"internalType":"uint256","name":"_amountInGwei","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

 Latest 13 blocks produced

Block Transaction Difficulty Gas Used Reward
205214802024-08-13 18:25:23137 days ago17235735231480.00 TH13,360,938 (44.54%)
0.024976781913924748 ETH
204252672024-07-31 8:13:47151 days ago17224136271740.00 TH17,860,320 (59.53%)
0.055064249569702148 ETH
195832062024-04-04 15:11:11269 days ago17122434713890.00 TH28,327,041 (94.42%)
0.048456557924526557 ETH
194247292024-03-13 7:41:59291 days ago17103157192050.00 TH17,495,671 (58.32%)
0.045547039904501863 ETH
187104182023-12-04 3:47:47391 days ago17016616671120.00 TH10,830,478 (36.10%)
0.024544625293885498 ETH
186818462023-11-30 3:49:35395 days ago17013161751420.00 TH12,512,933 (41.71%)
0.325249584000047428 ETH
185098922023-11-06 2:12:59419 days ago16992367791140.00 TH11,436,453 (38.12%)
0.050265499846175894 ETH
177690622023-07-25 8:56:35523 days ago16902753951360.00 TH21,355,252 (71.18%)
0.057155001267869102 ETH
177217692023-07-18 18:03:59529 days ago16897034391660.00 TH11,920,782 (39.74%)
0.033601659286578915 ETH
176256332023-07-05 5:34:11543 days ago16885352511030.00 TH12,748,601 (42.50%)
0.06113453983730037 ETH
176212722023-07-04 14:53:35544 days ago16884824152080.00 TH14,347,352 (47.82%)
0.043264698130801526 ETH
175932262023-06-30 16:17:35548 days ago16881418552830.00 TH20,926,935 (69.76%)
0.077146889370020654 ETH
174755542023-06-14 3:34:47564 days ago16867136871120.00 TH10,652,206 (35.51%)
0.039519024794787848 ETH

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]
[ 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.