Contract 0xdc746b0a57adc495c7ef5d9a64f5b4190be28736 1

Contract Overview

Balance:
0 BNB
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x2e0e66d457d1fecff82ec99a20455260c0b3fb43be297476c6001697d0ad33cc0x60806040106912182021-07-18 6:10:2390 days 15 hrs ago0xbee397129374d0b4db7bf1654936951e5bdfe5a6 IN  Contract Creation0 BNB0.03058494
[ Download CSV Export 
Latest 5 internal transactions
Parent Txn Hash Block From To Value
0xda83bd2b5b4015ad224b591383d9f22221858492f74239eb62792980461804a4107822542021-07-21 10:05:5887 days 11 hrs ago 0x5ba1b272d60f46371279ae7a1c13227fb93f99c1 0xdc746b0a57adc495c7ef5d9a64f5b4190be287360 BNB
0x30ce3b2fc0d61a0f5c30d14027bbe85319a19e56d6b45eb311b333c6752b026d106919852021-07-18 6:48:4490 days 14 hrs ago 0x5ba1b272d60f46371279ae7a1c13227fb93f99c1 0xdc746b0a57adc495c7ef5d9a64f5b4190be287360 BNB
0x3aead0573ceec2f21d64ba4fa53cc8394b0e5d2ef6dca72054b3d77d93dca1e1106919852021-07-18 6:48:4490 days 14 hrs ago 0x5ba1b272d60f46371279ae7a1c13227fb93f99c1 0xdc746b0a57adc495c7ef5d9a64f5b4190be287360 BNB
0xd9631cd38d8e07aebf73d77d857d63ffda4e92971c2b8eaf6aa78f3002ad1d6d106919852021-07-18 6:48:4490 days 14 hrs ago 0x5ba1b272d60f46371279ae7a1c13227fb93f99c1 0xdc746b0a57adc495c7ef5d9a64f5b4190be287360 BNB
0x0c1cc8bd7e3f4a6682915ffccac4a4572562e60394ba0f23a198d911a288647f106912202021-07-18 6:10:2990 days 15 hrs ago 0x5ba1b272d60f46371279ae7a1c13227fb93f99c1 0xdc746b0a57adc495c7ef5d9a64f5b4190be287360 BNB
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
DashboardBSC

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 57 : PriceCalculatorBSC.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";

import "../interfaces/AggregatorV3Interface.sol";
import "../interfaces/IPriceCalculator.sol";
import "../interfaces/IQToken.sol";
import "../interfaces/IPancakePair.sol";
import "../interfaces/IPancakeFactory.sol";
import "../library/HomoraMath.sol";


contract PriceCalculatorBSC is IPriceCalculator, OwnableUpgradeable {
    using SafeMath for uint;
    using HomoraMath for uint;

    address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;

    IPancakeFactory private constant factory = IPancakeFactory(0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73);

    /* ========== STATE VARIABLES ========== */

    address public keeper;
    mapping(address => ReferenceData) public references;
    mapping(address => address) private tokenFeeds;

    /* ========== Event ========== */

    event MarketListed(address qToken);
    event MarketEntered(address qToken, address account);
    event MarketExited(address qToken, address account);

    event CloseFactorUpdated(uint newCloseFactor);
    event CollateralFactorUpdated(address qToken, uint newCollateralFactor);
    event LiquidationIncentiveUpdated(uint newLiquidationIncentive);
    event BorrowCapUpdated(address indexed qToken, uint newBorrowCap);

    /* ========== MODIFIERS ========== */

    modifier onlyKeeper {
        require(msg.sender == keeper || msg.sender == owner(), 'Qore: caller is not the owner or keeper');
        _;
    }

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __Ownable_init();
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setKeeper(address _keeper) external onlyKeeper {
        require(_keeper != address(0), 'PriceCalculatorBSC: invalid keeper address');
        keeper = _keeper;
    }

    function setTokenFeed(address asset, address feed) external onlyKeeper {
        tokenFeeds[asset] = feed;
    }

    function setPrices(address[] memory assets, uint[] memory prices) external onlyKeeper {
        for (uint i = 0; i < assets.length; i++) {
            references[assets[i]] = ReferenceData({lastData : prices[i], lastUpdated : block.timestamp});
        }
    }

    /* ========== VIEWS ========== */

    function priceOf(address asset) public view override returns (uint priceInUSD) {
        (, priceInUSD) = _oracleValueOf(asset, 1e18);
        return priceInUSD;
    }

    function pricesOf(address[] memory assets) public view override returns (uint[] memory) {
        uint[] memory prices = new uint[](assets.length);
        for (uint i = 0; i < assets.length; i++) {
            prices[i] = priceOf(assets[i]);
        }
        return prices;
    }

    function getUnderlyingPrice(address qToken) public view override returns (uint) {
        return priceOf(IQToken(qToken).underlying());
    }

    function getUnderlyingPrices(address[] memory qTokens) public view override returns (uint[] memory) {
        uint[] memory prices = new uint[](qTokens.length);
        for (uint i = 0; i < qTokens.length; i++) {
            prices[i] = priceOf(IQToken(qTokens[i]).underlying());
        }
        return prices;
    }

    function priceOfBNB() public view returns (uint) {
        (, int price, , ,) = AggregatorV3Interface(tokenFeeds[WBNB]).latestRoundData();
        return uint(price).mul(1e10);
    }

    function valueOfAsset(address asset, uint amount) public view override returns (uint valueInBNB, uint valueInUSD) {
        if (asset == address(0) || asset == WBNB) {
            return _oracleValueOf(asset, amount);
        } else if (keccak256(abi.encodePacked(IPancakePair(asset).symbol())) == keccak256("Cake-LP")) {
            return _getPairPrice(asset, amount);
        } else {
            return _oracleValueOf(asset, amount);
        }
    }

    function unsafeValueOfAsset(address asset, uint amount) public view returns (uint valueInBNB, uint valueInUSD) {
        valueInBNB = 0;
        valueInUSD = 0;

        if (asset == address(0) || asset == WBNB) {
            valueInBNB = amount;
            valueInUSD = amount.mul(priceOfBNB()).div(1e18);
        }
        else if (keccak256(abi.encodePacked(IPancakePair(asset).symbol())) == keccak256("Cake-LP")) {
            if (IPancakePair(asset).totalSupply() == 0) return (0, 0);

            (uint reserve0, uint reserve1,) = IPancakePair(asset).getReserves();
            if (IPancakePair(asset).token0() == WBNB) {
                valueInBNB = amount.mul(reserve0).mul(2).div(IPancakePair(asset).totalSupply());
                valueInUSD = valueInBNB.mul(priceOfBNB()).div(1e18);
            } else if (IPancakePair(asset).token1() == WBNB) {
                valueInBNB = amount.mul(reserve1).mul(2).div(IPancakePair(asset).totalSupply());
                valueInUSD = valueInBNB.mul(priceOfBNB()).div(1e18);
            } else {
                (uint token0PriceInBNB,) = valueOfAsset(IPancakePair(asset).token0(), 1e18);
                valueInBNB = amount.mul(reserve0).mul(2).mul(token0PriceInBNB).div(1e18).div(IPancakePair(asset).totalSupply());
                valueInUSD = valueInBNB.mul(priceOfBNB()).div(1e18);
            }
        } else {
            address pair = factory.getPair(asset, WBNB);
            if (IBEP20(asset).balanceOf(pair) == 0) return (0, 0);

            (uint reserve0, uint reserve1,) = IPancakePair(pair).getReserves();
            if (IPancakePair(pair).token0() == WBNB) {
                valueInBNB = reserve0.mul(amount).div(reserve1);
            } else if (IPancakePair(pair).token1() == WBNB) {
                valueInBNB = reserve1.mul(amount).div(reserve0);
            } else {
                return (0, 0);
            }
            valueInUSD = valueInBNB.mul(priceOfBNB()).div(1e18);
        }
    }


    /* ========== PRIVATE FUNCTIONS ========== */

    function _getPairPrice(address pair, uint amount) private view returns (uint valueInBNB, uint valueInUSD) {
        address token0 = IPancakePair(pair).token0();
        address token1 = IPancakePair(pair).token1();
        uint totalSupply = IPancakePair(pair).totalSupply();
        (uint r0, uint r1,) = IPancakePair(pair).getReserves();

        uint sqrtK = HomoraMath.sqrt(r0.mul(r1)).fdiv(totalSupply);
        (uint px0,) = _oracleValueOf(token0, 1e18);
        (uint px1,) = _oracleValueOf(token1, 1e18);
        uint fairPriceInBNB = sqrtK.mul(2).mul(HomoraMath.sqrt(px0)).div(2 ** 56).mul(HomoraMath.sqrt(px1)).div(2 ** 56);

        valueInBNB = fairPriceInBNB.mul(amount).div(1e18);
        valueInUSD = valueInBNB.mul(priceOfBNB()).div(1e18);
    }

    function _oracleValueOf(address asset, uint amount) private view returns (uint valueInBNB, uint valueInUSD) {
        valueInUSD = 0;
        if (tokenFeeds[asset] != address(0)) {
            (, int price, , ,) = AggregatorV3Interface(tokenFeeds[asset]).latestRoundData();
            valueInUSD = uint(price).mul(1e10).mul(amount).div(1e18);
        } else if (references[asset].lastUpdated > block.timestamp.sub(1 days)) {
            valueInUSD = references[asset].lastData.mul(amount).div(1e18);
        }
        valueInBNB = valueInUSD.mul(1e18).div(priceOfBNB());
    }
}

File 2 of 57 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 3 of 57 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    uint256[49] private __gap;
}

File 4 of 57 : IBEP20.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.4.0;

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

    /**
     * @dev Returns the token decimals.
     */
    function decimals() external view returns (uint8);

    /**
     * @dev Returns the token symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the token name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the bep token owner.
     */
    function getOwner() external view returns (address);

    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

File 5 of 57 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;

interface AggregatorV3Interface {

    function decimals() external view returns (uint8);
    function description() external view returns (string memory);
    function version() external view returns (uint256);

    // getRoundData and latestRoundData should both raise "No data present"
    // if they do not have data to report, instead of returning unset values
    // which could be misinterpreted as actual reported values.
    function getRoundData(uint80 _roundId)
    external
    view
    returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
    function latestRoundData()
    external
    view
    returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );

}

File 6 of 57 : IPriceCalculator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/


interface IPriceCalculator {
    struct ReferenceData {
        uint lastData;
        uint lastUpdated;
    }

    function priceOf(address asset) external view returns (uint);
    function pricesOf(address[] memory assets) external view returns (uint[] memory);
    function getUnderlyingPrice(address qToken) external view returns (uint);
    function getUnderlyingPrices(address[] memory qTokens) external view returns (uint[] memory);
    function valueOfAsset(address asset, uint amount) external view returns (uint valueInBNB, uint valueInUSD);
}

File 7 of 57 : IQToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "../library/QConstant.sol";


interface IQToken {
    function underlying() external view returns (address);
    function totalSupply() external view returns (uint);

    function accountSnapshot(address account) external view returns (QConstant.AccountSnapshot memory);
    function underlyingBalanceOf(address account) external view returns (uint);
    function borrowBalanceOf(address account) external view returns (uint);
    function borrowRatePerSec() external view returns (uint);
    function supplyRatePerSec() external view returns (uint);
    function totalBorrow() external view returns (uint);
    function exchangeRate() external view returns (uint);
    function getCash() external view returns (uint);
    function getAccInterestIndex() external view returns (uint);

    function accruedAccountSnapshot(address account) external returns (QConstant.AccountSnapshot memory);
    function accruedUnderlyingBalanceOf(address account) external returns (uint);
    function accruedBorrowBalanceOf(address account) external returns (uint);
    function accruedTotalBorrow() external returns (uint);
    function accruedExchangeRate() external returns (uint);

    function approve(address spender, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function balanceOf(address account) external view returns (uint);
    function transfer(address dst, uint amount) external returns (bool);
    function transferFrom(address src, address dst, uint amount) external returns (bool);

    function supply(address account, uint underlyingAmount) external payable returns (uint);
    function redeemToken(address account, uint qTokenAmount) external returns (uint);
    function redeemUnderlying(address account, uint underlyingAmount) external returns (uint);

    function borrow(address account, uint amount) external returns (uint);
    function repayBorrow(address account, uint amount) external payable returns (uint);
    function repayBorrowBehalf(address payer, address borrower, uint amount) external payable returns (uint);

    function liquidateBorrow(address qTokenCollateral, address liquidator, address borrower, uint amount) payable external returns (uint qAmountToSeize);
    function seize(address liquidator, address borrower, uint qTokenAmount) external;
}

File 8 of 57 : IPancakePair.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

interface IPancakePair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

File 9 of 57 : IPancakeFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

interface IPancakeFactory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}

File 10 of 57 : HomoraMath.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "@openzeppelin/contracts/math/SafeMath.sol";


library HomoraMath {
    using SafeMath for uint;

    function divCeil(uint lhs, uint rhs) internal pure returns (uint) {
        return lhs.add(rhs).sub(1) / rhs;
    }

    function fmul(uint lhs, uint rhs) internal pure returns (uint) {
        return lhs.mul(rhs) / (2**112);
    }

    function fdiv(uint lhs, uint rhs) internal pure returns (uint) {
        return lhs.mul(2**112) / rhs;
    }

    // implementation from https://github.com/Uniswap/uniswap-lib/commit/99f3f28770640ba1bb1ff460ac7c5292fb8291a0
    // original implementation: https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.sol#L687
    function sqrt(uint x) internal pure returns (uint) {
        if (x == 0) return 0;
        uint xx = x;
        uint r = 1;

        if (xx >= 0x100000000000000000000000000000000) {
            xx >>= 128;
            r <<= 64;
        }

        if (xx >= 0x10000000000000000) {
            xx >>= 64;
            r <<= 32;
        }
        if (xx >= 0x100000000) {
            xx >>= 32;
            r <<= 16;
        }
        if (xx >= 0x10000) {
            xx >>= 16;
            r <<= 8;
        }
        if (xx >= 0x100) {
            xx >>= 8;
            r <<= 4;
        }
        if (xx >= 0x10) {
            xx >>= 4;
            r <<= 2;
        }
        if (xx >= 0x8) {
            r <<= 1;
        }

        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1; // Seven iterations should be enough
        uint r1 = x / r;
        return (r < r1 ? r : r1);
    }
}

File 11 of 57 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";

/*
 * @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 GSN 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 ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
    uint256[50] private __gap;
}

File 12 of 57 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;

import "../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {

    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /// @dev Returns true if and only if the function is running in the constructor
    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 13 of 57 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

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

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

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (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");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 14 of 57 : QConstant.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/


library QConstant {
    uint public constant CLOSE_FACTOR_MIN = 5e16;
    uint public constant CLOSE_FACTOR_MAX = 9e17;
    uint public constant COLLATERAL_FACTOR_MAX = 9e17;

    struct MarketInfo {
        bool isListed;
        uint borrowCap;
        uint collateralFactor;
    }

    struct BorrowInfo {
        uint borrow;
        uint interestIndex;
    }

    struct AccountSnapshot {
        uint qTokenBalance;
        uint borrowBalance;
        uint exchangeRate;
    }

    struct AccrueSnapshot {
        uint totalBorrow;
        uint totalReserve;
        uint accInterestIndex;
    }
}

File 15 of 57 : QValidatorTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "../interfaces/IPriceCalculator.sol";
import "../interfaces/IQValidator.sol";
import "../interfaces/IQToken.sol";
import "../interfaces/IQore.sol";
import "../library/QConstant.sol";


contract QValidatorTester is IQValidator, OwnableUpgradeable {
    using SafeMath for uint;

    /* ========== CONSTANT VARIABLES ========== */

    address public constant USDT = 0x55d398326f99059fF775485246999027B3197955;
    address public constant BUSD = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;
    address public constant BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51;
    address public constant CAKE = 0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82;
    address public constant BNB = 0x0000000000000000000000000000000000000000;
    address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;

    /* ========== STATE VARIABLES ========== */

    IQore public qore;
    IPriceCalculator oracle;

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __Ownable_init();
    }

    /* ========== VIEWS ========== */

    function getAccountLiquidity(address account, address qToken, uint redeemAmount, uint borrowAmount) external view override returns (uint liquidity, uint shortfall) {
        uint accCollateralValueInUSD;
        uint accBorrowValueInUSD;

        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = oracle.getUnderlyingPrices(assets);
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            accCollateralValueInUSD = accCollateralValueInUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            accBorrowValueInUSD = accBorrowValueInUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));

            if (assets[i] == qToken) {
                accBorrowValueInUSD = accBorrowValueInUSD.add(redeemAmount.mul(collateralValuePerShareInUSD).div(1e18));
                accBorrowValueInUSD = accBorrowValueInUSD.add(borrowAmount.mul(prices[i]).div(1e18));
            }
        }

        liquidity = accCollateralValueInUSD > accBorrowValueInUSD ? accCollateralValueInUSD.sub(accBorrowValueInUSD) : 0;
        shortfall = accCollateralValueInUSD > accBorrowValueInUSD ? 0 : accBorrowValueInUSD.sub(accCollateralValueInUSD);
    }

    function getAccountLiquidityValue(address account) external view override returns (uint collateralUSD, uint borrowUSD) {
        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = oracle.getUnderlyingPrices(assets);
        collateralUSD = 0;
        borrowUSD = 0;
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            collateralUSD = collateralUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            borrowUSD = borrowUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));
        }
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setQore(address _qore) external onlyOwner {
        require(_qore != address(0), "QValidator: invalid qore address");
        qore = IQore(_qore);
    }

    function setOracle(address _oracle) external onlyOwner {
        require(_oracle != address(0), "QValidator: invalid qore address");
        oracle = IPriceCalculator(_oracle);
    }

    /* ========== ALLOWED FUNCTIONS ========== */

    function redeemAllowed(address qToken, address redeemer, uint redeemAmount) external override returns (bool) {
        (, uint shortfall) = _getAccountLiquidityInternal(redeemer, qToken, redeemAmount, 0);
        return shortfall == 0;
    }

    function borrowAllowed(address qToken, address borrower, uint borrowAmount) external override returns (bool) {
        require(oracle.getUnderlyingPrice(address(qToken)) > 0, "QValidator: Underlying price error");

        // Borrow cap of 0 corresponds to unlimited borrowing
        uint borrowCap = qore.marketInfoOf(qToken).borrowCap;
        if (borrowCap != 0) {
            uint totalBorrows = IQToken(payable(qToken)).accruedTotalBorrow();
            uint nextTotalBorrows = totalBorrows.add(borrowAmount);
            require(nextTotalBorrows < borrowCap, "QValidator: market borrow cap reached");
        }

        (, uint shortfall) = _getAccountLiquidityInternal(borrower, qToken, 0, borrowAmount);
        return shortfall == 0;
    }

    function liquidateAllowed(address qToken, address borrower, uint repayAmount, uint closeFactor) external override returns (bool) {
        // The borrower must have shortfall in order to be liquidate
        (, uint shortfall) = _getAccountLiquidityInternal(borrower, address(0), 0, 0);
        require(shortfall != 0, "QValidator: Insufficient shortfall");

        // The liquidator may not repay more than what is allowed by the closeFactor
        uint borrowBalance = IQToken(payable(qToken)).accruedBorrowBalanceOf(borrower);
        uint maxClose = closeFactor.mul(borrowBalance).div(1e18);
        return repayAmount <= maxClose;
    }

    function qTokenAmountToSeize(address qTokenBorrowed, address qTokenCollateral, uint amount) external override returns (uint seizeQAmount) {
        uint priceBorrowed = oracle.getUnderlyingPrice(qTokenBorrowed);
        uint priceCollateral = oracle.getUnderlyingPrice(qTokenCollateral);
        require(priceBorrowed != 0 && priceCollateral != 0, "QValidator: price error");

        uint exchangeRate = IQToken(payable(qTokenCollateral)).accruedExchangeRate();
        require(exchangeRate != 0, "QValidator: exchangeRate of qTokenCollateral is zero");

        // seizeQTokenAmount = amount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)
        return amount.mul(qore.liquidationIncentive()).mul(priceBorrowed).div(priceCollateral.mul(exchangeRate));
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _getAccountLiquidityInternal(address account, address qToken, uint redeemAmount, uint borrowAmount) private returns (uint liquidity, uint shortfall) {
        uint accCollateralValueInUSD;
        uint accBorrowValueInUSD;

        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = oracle.getUnderlyingPrices(assets);
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accruedAccountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            accCollateralValueInUSD = accCollateralValueInUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            accBorrowValueInUSD = accBorrowValueInUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));

            if (assets[i] == qToken) {
                accBorrowValueInUSD = accBorrowValueInUSD.add(redeemAmount.mul(collateralValuePerShareInUSD).div(1e18));
                accBorrowValueInUSD = accBorrowValueInUSD.add(borrowAmount.mul(prices[i]).div(1e18));
            }
        }

        liquidity = accCollateralValueInUSD > accBorrowValueInUSD ? accCollateralValueInUSD.sub(accBorrowValueInUSD) : 0;
        shortfall = accCollateralValueInUSD > accBorrowValueInUSD ? 0 : accBorrowValueInUSD.sub(accCollateralValueInUSD);
    }

    function getAccountLiquidityTester(address account, address qToken, uint redeemAmount, uint borrowAmount) external returns (uint liquidity, uint shortfall) {
        uint accCollateralValueInUSD;
        uint accBorrowValueInUSD;

        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = getUnderlyingPricesTester(assets);
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accruedAccountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            accCollateralValueInUSD = accCollateralValueInUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            accBorrowValueInUSD = accBorrowValueInUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));

            if (assets[i] == qToken) {
                accBorrowValueInUSD = accBorrowValueInUSD.add(redeemAmount.mul(collateralValuePerShareInUSD).div(1e18));
                accBorrowValueInUSD = accBorrowValueInUSD.add(borrowAmount.mul(prices[i]).div(1e18));
            }
        }

        liquidity = accCollateralValueInUSD > accBorrowValueInUSD ? accCollateralValueInUSD.sub(accBorrowValueInUSD) : 0;
        shortfall = accCollateralValueInUSD > accBorrowValueInUSD ? 0 : accBorrowValueInUSD.sub(accCollateralValueInUSD);
    }

    function getUnderlyingPricesTester(address[] memory assets) public view returns (uint[] memory)  {
        uint[] memory returnValue = new uint[](assets.length);
        for (uint i = 0; i < assets.length; i++) {
            IQToken qToken = IQToken(payable(assets[i]));
            address addr = qToken.underlying();
            if (addr == USDT) {
                returnValue[i] = 1e18;
            } else if (addr == BUNNY) {
                returnValue[i] = 25e18;
            } else if (addr == CAKE) {
                returnValue[i] = 20e18;
            } else if (addr == BUSD) {
                returnValue[i] = 1e18;
            } else if (addr == USDT) {
                returnValue[i] = 1e18;
            } else if (addr == BNB || addr == WBNB) {
                returnValue[i] = 400e18;
            } else {
                returnValue[i] = 0;
            }
        }
        return returnValue;
    }
}

File 16 of 57 : IQValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/


interface IQValidator {
    function redeemAllowed(address qToken, address redeemer, uint redeemAmount) external returns (bool);
    function borrowAllowed(address qToken, address borrower, uint borrowAmount) external returns (bool);
    function liquidateAllowed(address qTokenBorrowed, address borrower, uint repayAmount, uint closeFactor) external returns (bool);
    function qTokenAmountToSeize(address qTokenBorrowed, address qTokenCollateral, uint actualRepayAmount) external returns (uint qTokenAmount);
    function getAccountLiquidity(address account, address qToken, uint redeemAmount, uint borrowAmount) external view returns (uint liquidity, uint shortfall);
    function getAccountLiquidityValue(address account) external view returns (uint collateralUSD, uint borrowUSD);
}

File 17 of 57 : IQore.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../library/QConstant.sol";


interface IQore {
    function qValidator() external view returns (address);
    function getTotalUserList() external view returns (address[] memory);

    function allMarkets() external view returns (address[] memory);
    function marketListOf(address account) external view returns (address[] memory);
    function marketInfoOf(address qToken) external view returns (QConstant.MarketInfo memory);
    function liquidationIncentive() external view returns (uint);
    function checkMembership(address account, address qToken) external view returns (bool);

    function enterMarkets(address[] memory qTokens) external;
    function exitMarket(address qToken) external;

    function supply(address qToken, uint underlyingAmount) external payable returns (uint);
    function redeemToken(address qToken, uint qTokenAmount) external returns (uint);
    function redeemUnderlying(address qToken, uint underlyingAmount) external returns (uint);

    function borrow(address qToken, uint amount) external;

    function repayBorrow(address qToken, uint amount) external payable;
    function repayBorrowBehalf(address qToken, address borrower, uint amount) external payable;

    function liquidateBorrow(address qTokenBorrowed, address qTokenCollateral, address borrower, uint amount) external payable;
}

File 18 of 57 : SimpleQTokenTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

import "../library/QConstant.sol";
pragma experimental ABIEncoderV2;


contract SimpleQTokenTester {
    address public underlying;
    uint public qTokenBalance;
    uint public borrowBalance;
    uint public exchangeRate;
    uint public exchangeRateStored;
    uint public totalBorrow;

    constructor(address _underlying) public {
        underlying = _underlying;
        exchangeRateStored = 50000000000000000;
    }

    function getAccountSnapshot(address ) public view returns (uint, uint, uint) {
        return (qTokenBalance, borrowBalance, exchangeRate);
    }

    function setAccountSnapshot(uint _qTokenBalance, uint _borrowBalance, uint _exchangeRate) public {
        qTokenBalance = _qTokenBalance;
        borrowBalance = _borrowBalance;
        exchangeRate = _exchangeRate;
        totalBorrow = _borrowBalance;
    }

    function borrowBalanceOf(address ) public view returns (uint){
        return borrowBalance;
    }

    function accruedAccountSnapshot(address ) external view returns (QConstant.AccountSnapshot memory) {
        QConstant.AccountSnapshot memory snapshot;
        snapshot.qTokenBalance = qTokenBalance;
        snapshot.borrowBalance = borrowBalance;
        snapshot.exchangeRate = exchangeRate;
        return snapshot;
    }

    function accruedTotalBorrow() public view returns (uint){
        return totalBorrow;
    }

    function accruedBorrowBalanceOf(address ) public view returns (uint) {
        return borrowBalance;
    }

    function accruedExchangeRate() public view returns (uint) {
        return exchangeRate;
    }
}

File 19 of 57 : QTokenTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";

import "../interfaces/IPancakeRouter02.sol";
import "../interfaces/IQDistributor.sol";
import "../library/QConstant.sol";
import "../library/SafeToken.sol";
import "../markets/QMarket.sol";

import "hardhat/console.sol";


contract QTokenTester is QMarket {
    using SafeMath for uint;
    using SafeToken for address;

    // for test
    function setQDistributor(address _qDistributor) external onlyOwner {
        require(_qDistributor != address(0), "QTokenTester: invalid qDistributor address");
        qDistributor = IQDistributor(_qDistributor);
    }

    /* ========== CONSTANT ========== */

    IQDistributor public qDistributor;

    /* ========== STATE VARIABLES ========== */

    string public name;
    string public symbol;
    uint8 public decimals;

    mapping(address => mapping(address => uint)) private _transferAllowances;

    /* ========== EVENT ========== */

    event Mint(address minter, uint mintAmount);
    event Redeem(address account, uint underlyingAmount, uint qTokenAmount);

    event Borrow(address account, uint ammount, uint accountBorrow);
    event RepayBorrow(address payer, address borrower, uint amount, uint accountBorrow);
    event LiquidateBorrow(address liquidator, address borrower, uint amount, address qTokenCollateral, uint seizeAmount);

    event Transfer(address indexed from, address indexed to, uint amount);
    event Approval(address indexed owner, address indexed spender, uint amount);

    /* ========== INITIALIZER ========== */

    function initialize(string memory _name, string memory _symbol, uint8 _decimals) external initializer {
        __QMarket_init();

        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }

    /* ========== VIEWS ========== */

    function allowance(address account, address spender) external view override returns (uint) {
        return _transferAllowances[account][spender];
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function transfer(address dst, uint amount) external override accrue nonReentrant returns (bool) {
        _transferTokens(msg.sender, msg.sender, dst, amount);
        return true;
    }

    function transferFrom(address src, address dst, uint amount) external override accrue nonReentrant returns (bool) {
        _transferTokens(msg.sender, src, dst, amount);
        return true;
    }

    function approve(address spender, uint amount) external override returns (bool) {
        _transferAllowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function supply(address account, uint uAmount) payable external override accrue onlyQore nonReentrant returns (uint) {
        uint exchangeRate = exchangeRate();
        uAmount = underlying == address(WBNB) ? msg.value : uAmount;
        uAmount = _doTransferIn(account, uAmount);
        uint qAmount = uAmount.mul(1e18).div(exchangeRate);

        totalSupply = totalSupply.add(qAmount);
        accountBalances[account] = accountBalances[account].add(qAmount);

        emit Mint(account, qAmount);
        emit Transfer(address(this), account, qAmount);
        return qAmount;
    }

    function redeemToken(address redeemer, uint qAmount) external override accrue onlyQore nonReentrant returns (uint) {
        return _redeem(redeemer, qAmount, 0);
    }

    function redeemUnderlying(address redeemer, uint uAmount) external override accrue onlyQore nonReentrant returns (uint) {
        return _redeem(redeemer, 0, uAmount);
    }

    function borrow(address account, uint amount) external override accrue onlyQore nonReentrant returns (uint) {
        require(getCash() >= amount, "QToken: borrow amount exceeds cash");
        updateBorrowInfo(account, amount, 0);
        _doTransferOut(account, amount);

        emit Borrow(account, amount, borrowBalanceOf(account));
        return amount;
    }

    function repayBorrow(address account, uint amount) external payable override accrue onlyQore nonReentrant returns (uint) {
        return _repay(account, account, underlying == address(WBNB) ? msg.value : amount);
    }

    function repayBorrowBehalf(address payer, address borrower, uint amount) external payable override accrue onlyQore nonReentrant returns (uint) {
        return _repay(payer, borrower, underlying == address(WBNB) ? msg.value : amount);
    }

    function liquidateBorrow(address qTokenCollateral, address liquidator, address borrower, uint amount) external payable override accrue onlyQore nonReentrant returns (uint qAmountToSeize) {
        require(borrower != liquidator, "QToken: cannot liquidate yourself");

        amount = underlying == address(WBNB) ? msg.value : amount;
        amount = _repay(liquidator, borrower, amount);
        require(amount > 0 && amount < uint(- 1), "QToken: invalid repay amount");

        qAmountToSeize = IQValidator(qore.qValidator()).qTokenAmountToSeize(address(this), qTokenCollateral, amount);
        require(IQToken(payable(qTokenCollateral)).balanceOf(borrower) >= qAmountToSeize, "QToken: too much seize amount");
        emit LiquidateBorrow(liquidator, borrower, amount, qTokenCollateral, qAmountToSeize);
    }

    function seize(address liquidator, address borrower, uint qAmount) external override accrue onlyQore nonReentrant {
        accountBalances[borrower] = accountBalances[borrower].sub(qAmount);
        accountBalances[liquidator] = accountBalances[liquidator].add(qAmount);
        emit Transfer(borrower, liquidator, qAmount);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _transferTokens(address spender, address src, address dst, uint amount) private {
        require(src != dst && IQValidator(qore.qValidator()).redeemAllowed(address(this), src, amount), "QToken: cannot transfer");

        uint _allowance = spender == src ? uint(- 1) : _transferAllowances[src][spender];
        uint _allowanceNew = _allowance.sub(amount, 'QToken: transfer amount exceeds allowance');

        accountBalances[src] = accountBalances[src].sub(amount);
        accountBalances[dst] = accountBalances[dst].add(amount);

        qDistributor.notifyTransferred(address(this), src, dst);

        if (_allowance != uint(- 1)) {
            _transferAllowances[src][msg.sender] = _allowanceNew;
        }
        emit Transfer(src, dst, amount);
    }

    function _doTransferIn(address from, uint amount) private returns (uint) {
        if (underlying == address(WBNB)) {
            require(msg.value >= amount, "QToken: value mismatch");
            return Math.min(msg.value, amount);
        } else {
            uint balanceBefore = IBEP20(underlying).balanceOf(address(this));
            underlying.safeTransferFrom(from, address(this), amount);
            return IBEP20(underlying).balanceOf(address(this)).sub(balanceBefore);
        }
    }

    function _doTransferOut(address to, uint amount) private {
        if (underlying == address(WBNB)) {
            SafeToken.safeTransferETH(to, amount);
        } else {
            underlying.safeTransfer(to, amount);
        }
    }

    function _redeem(address account, uint qAmountIn, uint uAmountIn) private returns (uint) {
        require(qAmountIn == 0 || uAmountIn == 0, "QToken: one of qAmountIn or uAmountIn must be zero");
        require(totalSupply >= qAmountIn, "QToken: not enough total supply");
        require(getCash() >= uAmountIn || uAmountIn == 0, "QToken: not enough underlying");
        require(getCash() >= qAmountIn.mul(exchangeRate()).div(1e18) || qAmountIn == 0, "QToken: not enough underlying");

        uint qAmountToRedeem = qAmountIn > 0 ? qAmountIn : uAmountIn.mul(1e18).div(exchangeRate());
        uint uAmountToRedeem = qAmountIn > 0 ? qAmountIn.mul(exchangeRate()).div(1e18) : uAmountIn;

        require(IQValidator(qore.qValidator()).redeemAllowed(address(this), account, qAmountToRedeem), "QToken: cannot redeem");

        totalSupply = totalSupply.sub(qAmountToRedeem);
        accountBalances[account] = accountBalances[account].sub(qAmountToRedeem);
        _doTransferOut(account, uAmountToRedeem);

        emit Transfer(account, address(this), qAmountToRedeem);
        emit Redeem(account, uAmountToRedeem, qAmountToRedeem);
        return uAmountToRedeem;
    }

    function _repay(address payer, address borrower, uint amount) private returns (uint) {
        uint borrowBalance = borrowBalanceOf(borrower);
        uint repayAmount = Math.min(borrowBalance, amount);
        repayAmount = _doTransferIn(payer, repayAmount);
        updateBorrowInfo(borrower, 0, repayAmount);

        uint refundAmount = amount > repayAmount ? amount.sub(repayAmount) : 0;
        if (refundAmount > 0) {
            _doTransferOut(payer, refundAmount);
        }

        emit RepayBorrow(payer, borrower, repayAmount, borrowBalanceOf(borrower));
        return repayAmount;
    }
}

File 20 of 57 : Math.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow, so we distribute
        return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
    }
}

File 21 of 57 : IPancakeRouter02.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2;

import './IPancakeRouter01.sol';

interface IPancakeRouter02 is IPancakeRouter01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

File 22 of 57 : IQDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

interface IQDistributor {

    struct UserInfo {
        uint accruedQubit;
        uint boostedSupply;    // effective(boosted) supply balance of user  (since last_action)
        uint boostedBorrow;    // effective(boosted) borrow balance of user  (since last_action)
        uint accPerShareSupply;  // Last integral value of Qubit rewards per share. ∫(qubitRate(t) / totalShare(t) dt) from 0 till (last_action)
        uint accPerShareBorrow;  // Last integral value of Qubit rewards per share. ∫(qubitRate(t) / totalShare(t) dt) from 0 till (last_action)
    }

    struct DistributionInfo {
        uint supplyRate;
        uint borrowRate;
        uint totalBoostedSupply;
        uint totalBoostedBorrow;
        uint accPerShareSupply;
        uint accPerShareBorrow;
        uint accruedAt;
    }

    function accruedQubit(address market, address user) external view returns (uint);
    function qubitRatesOf(address market) external view returns (uint supplyRate, uint borrowRate);
    function totalBoosted(address market) external view returns (uint boostedSupply, uint boostedBorrow);
    function boostedBalanceOf(address market, address account) external view returns (uint boostedSupply, uint boostedBorrow);

    function notifySupplyUpdated(address market, address user) external;
    function notifyBorrowUpdated(address market, address user) external;
    function notifyTransferred(address qToken, address sender, address receiver) external;
    function claimQubit(address user) external;
    function kick(address user) external;
}

File 23 of 57 : SafeToken.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.12;

interface ERC20Interface {
    function balanceOf(address user) external view returns (uint256);
}

library SafeToken {
    function myBalance(address token) internal view returns (uint256) {
        return ERC20Interface(token).balanceOf(address(this));
    }

    function balanceOf(address token, address user) internal view returns (uint256) {
        return ERC20Interface(token).balanceOf(user);
    }

    function safeApprove(address token, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove");
    }

    function safeTransfer(address token, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer");
    }

    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom");
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{ value: value }(new bytes(0));
        require(success, "!safeTransferETH");
    }
}

File 24 of 57 : QMarket.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";

import "../interfaces/IQValidator.sol";
import "../interfaces/IRateModel.sol";
import "../interfaces/IQToken.sol";
import "../interfaces/IQore.sol";
import "../library/QConstant.sol";


abstract contract QMarket is IQToken, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMath for uint;

    /* ========== CONSTANT VARIABLES ========== */

    uint internal constant RESERVE_FACTOR_MAX = 1e18;
    uint internal constant DUST = 1000;

    address internal constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
    address internal constant BUSD = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;

    /* ========== STATE VARIABLES ========== */

    IQore public qore;
    IRateModel public rateModel;
    address public override underlying;

    uint public override totalSupply;
    uint public totalReserve;
    uint private _totalBorrow;

    mapping(address => uint) internal accountBalances;
    mapping(address => QConstant.BorrowInfo) internal accountBorrows;

    uint public reserveFactor;
    uint private lastAccruedTime;
    uint private accInterestIndex;

    /* ========== INITIALIZER ========== */

    receive() payable external {}

    function __QMarket_init() internal initializer {
        __Ownable_init();
        __ReentrancyGuard_init();

        lastAccruedTime = block.timestamp;
        accInterestIndex = 1e18;
    }

    /* ========== MODIFIERS ========== */

    modifier accrue {
        if (block.timestamp > lastAccruedTime && address(rateModel) != address(0)) {
            uint borrowRate = rateModel.getBorrowRate(getCashPrior(), _totalBorrow, totalReserve);
            uint interestFactor = borrowRate.mul(block.timestamp.sub(lastAccruedTime));
            uint pendingInterest = _totalBorrow.mul(interestFactor).div(1e18);

            _totalBorrow = _totalBorrow.add(pendingInterest);
            totalReserve = totalReserve.add(pendingInterest.mul(reserveFactor).div(1e18));
            accInterestIndex = accInterestIndex.add(interestFactor.mul(accInterestIndex).div(1e18));
            lastAccruedTime = block.timestamp;
        }
        _;
    }

    modifier onlyQore {
        require(msg.sender == address(qore), "QToken: only Qore Contract");
        _;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setQore(address _qore) public onlyOwner {
        require(_qore != address(0), "QMarket: invalid qore address");
        qore = IQore(_qore);
    }

    function setUnderlying(address _underlying) public onlyOwner {
        require(_underlying != address(0), "QMarket: invalid underlying address");
        require(underlying == address(0), "QMarket: set underlying already");
        underlying = _underlying;
    }

    function setRateModel(address _rateModel) public accrue onlyOwner {
        require(_rateModel != address(0), "QMarket: invalid rate model address");
        rateModel = IRateModel(_rateModel);
    }

    function setReserveFactor(uint _reserveFactor) public accrue onlyOwner {
        require(_reserveFactor <= RESERVE_FACTOR_MAX, "QMarket: invalid reserve factor");
        reserveFactor = _reserveFactor;
    }

    /* ========== VIEWS ========== */

    function balanceOf(address account) external view override returns (uint) {
        return accountBalances[account];
    }

    function accountSnapshot(address account) external view override returns (QConstant.AccountSnapshot memory) {
        QConstant.AccountSnapshot memory snapshot;
        snapshot.qTokenBalance = accountBalances[account];
        snapshot.borrowBalance = borrowBalanceOf(account);
        snapshot.exchangeRate = exchangeRate();
        return snapshot;
    }

    function underlyingBalanceOf(address account) external view override returns (uint) {
        return accountBalances[account].mul(exchangeRate()).div(1e18);
    }

    function borrowBalanceOf(address account) public view override returns (uint) {
        QConstant.AccrueSnapshot memory snapshot = pendingAccrueSnapshot();
        QConstant.BorrowInfo storage info = accountBorrows[account];

        if (info.borrow == 0) return 0;
        return info.borrow.mul(snapshot.accInterestIndex).div(info.interestIndex);
    }

    function borrowRatePerSec() external view override returns (uint) {
        QConstant.AccrueSnapshot memory snapshot = pendingAccrueSnapshot();
        return rateModel.getBorrowRate(getCashPrior(), snapshot.totalBorrow, snapshot.totalReserve);
    }

    function supplyRatePerSec() external view override returns (uint) {
        QConstant.AccrueSnapshot memory snapshot = pendingAccrueSnapshot();
        return rateModel.getSupplyRate(getCashPrior(), snapshot.totalBorrow, snapshot.totalReserve, reserveFactor);
    }

    function totalBorrow() public view override returns (uint) {
        QConstant.AccrueSnapshot memory snapshot = pendingAccrueSnapshot();
        return snapshot.totalBorrow;
    }

    function exchangeRate() public view override returns (uint) {
        if (totalSupply == 0) return 1e18;
        QConstant.AccrueSnapshot memory snapshot = pendingAccrueSnapshot();
        return getCashPrior().add(snapshot.totalBorrow).sub(snapshot.totalReserve).mul(1e18).div(totalSupply);
    }

    function getCash() public view override returns (uint) {
        return getCashPrior();
    }

    function getAccInterestIndex() public view override returns (uint) {
        QConstant.AccrueSnapshot memory snapshot = pendingAccrueSnapshot();
        return snapshot.accInterestIndex;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function accruedAccountSnapshot(address account) external override accrue returns (QConstant.AccountSnapshot memory) {
        QConstant.AccountSnapshot memory snapshot;
        snapshot.qTokenBalance = accountBalances[account];
        snapshot.borrowBalance = accountBorrows[account].borrow;
        snapshot.exchangeRate = exchangeRate();
        return snapshot;
    }

    function accruedUnderlyingBalanceOf(address account) external override accrue returns (uint) {
        return accountBalances[account].mul(exchangeRate()).div(1e18);
    }

    function accruedBorrowBalanceOf(address account) external override accrue returns (uint) {
        QConstant.BorrowInfo storage info = accountBorrows[account];
        info.borrow = info.borrow.mul(accInterestIndex).div(info.interestIndex);
        info.interestIndex = accInterestIndex;
        return info.borrow;
    }

    function accruedTotalBorrow() external override accrue returns (uint) {
        return _totalBorrow;
    }

    function accruedExchangeRate() external override accrue returns (uint) {
        return exchangeRate();
    }

    /* ========== INTERNAL FUNCTIONS ========== */

    function updateBorrowInfo(address account, uint addAmount, uint subAmount) internal {
        QConstant.BorrowInfo storage info = accountBorrows[account];
        if (info.interestIndex == 0) {
            info.interestIndex = accInterestIndex;
        }

        info.borrow = info.borrow.mul(accInterestIndex).div(info.interestIndex).add(addAmount).sub(subAmount);
        info.interestIndex = accInterestIndex;
        _totalBorrow = _totalBorrow.add(addAmount).sub(subAmount);

        info.borrow = (info.borrow < DUST) ? 0 : info.borrow;
        _totalBorrow = (_totalBorrow < DUST) ? 0 : _totalBorrow;
    }

    function updateSupplyInfo(address account, uint addAmount, uint subAmount) internal {
        accountBalances[account] = accountBalances[account].add(addAmount).sub(subAmount);
        totalSupply = totalSupply.add(addAmount).sub(subAmount);

        totalSupply = (totalSupply < DUST) ? 0 : totalSupply;
    }

    function getCashPrior() internal view returns (uint) {
        return underlying == address(WBNB) ? address(this).balance.sub(msg.value) : IBEP20(underlying).balanceOf(address(this));
    }

    function pendingAccrueSnapshot() internal view returns (QConstant.AccrueSnapshot memory) {
        QConstant.AccrueSnapshot memory snapshot;
        snapshot.totalBorrow = _totalBorrow;
        snapshot.totalReserve = totalReserve;
        snapshot.accInterestIndex = accInterestIndex;

        if (block.timestamp > lastAccruedTime && _totalBorrow > 0) {
            uint borrowRate = rateModel.getBorrowRate(getCashPrior(), _totalBorrow, totalReserve);
            uint interestFactor = borrowRate.mul(block.timestamp.sub(lastAccruedTime));
            uint pendingInterest = _totalBorrow.mul(interestFactor).div(1e18);

            snapshot.totalBorrow = _totalBorrow.add(pendingInterest);
            snapshot.totalReserve = totalReserve.add(pendingInterest.mul(reserveFactor).div(1e18));
            snapshot.accInterestIndex = accInterestIndex.add(interestFactor.mul(accInterestIndex).div(1e18));
        }
        return snapshot;
    }
}

File 25 of 57 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.9.0;

library console {
	address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

	function _sendLogPayload(bytes memory payload) private view {
		uint256 payloadLength = payload.length;
		address consoleAddress = CONSOLE_ADDRESS;
		assembly {
			let payloadStart := add(payload, 32)
			let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
		}
	}

	function log() internal view {
		_sendLogPayload(abi.encodeWithSignature("log()"));
	}

	function logInt(int p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
	}

	function logUint(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function logString(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function logBool(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function logAddress(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function logBytes(bytes memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
	}

	function logBytes1(bytes1 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
	}

	function logBytes2(bytes2 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
	}

	function logBytes3(bytes3 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
	}

	function logBytes4(bytes4 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
	}

	function logBytes5(bytes5 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
	}

	function logBytes6(bytes6 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
	}

	function logBytes7(bytes7 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
	}

	function logBytes8(bytes8 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
	}

	function logBytes9(bytes9 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
	}

	function logBytes10(bytes10 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
	}

	function logBytes11(bytes11 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
	}

	function logBytes12(bytes12 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
	}

	function logBytes13(bytes13 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
	}

	function logBytes14(bytes14 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
	}

	function logBytes15(bytes15 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
	}

	function logBytes16(bytes16 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
	}

	function logBytes17(bytes17 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
	}

	function logBytes18(bytes18 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
	}

	function logBytes19(bytes19 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
	}

	function logBytes20(bytes20 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
	}

	function logBytes21(bytes21 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
	}

	function logBytes22(bytes22 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
	}

	function logBytes23(bytes23 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
	}

	function logBytes24(bytes24 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
	}

	function logBytes25(bytes25 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
	}

	function logBytes26(bytes26 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
	}

	function logBytes27(bytes27 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
	}

	function logBytes28(bytes28 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
	}

	function logBytes29(bytes29 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
	}

	function logBytes30(bytes30 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
	}

	function logBytes31(bytes31 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
	}

	function logBytes32(bytes32 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
	}

	function log(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function log(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function log(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function log(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function log(uint p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
	}

	function log(uint p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
	}

	function log(uint p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
	}

	function log(uint p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
	}

	function log(string memory p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
	}

	function log(string memory p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
	}

	function log(string memory p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
	}

	function log(string memory p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
	}

	function log(bool p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
	}

	function log(bool p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
	}

	function log(bool p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
	}

	function log(bool p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
	}

	function log(address p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
	}

	function log(address p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
	}

	function log(address p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
	}

	function log(address p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
	}

	function log(uint p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
	}

	function log(uint p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
	}

	function log(uint p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
	}

	function log(uint p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
	}

	function log(uint p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
	}

	function log(uint p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
	}

	function log(uint p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
	}

	function log(uint p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
	}

	function log(uint p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
	}

	function log(uint p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
	}

	function log(uint p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
	}

	function log(uint p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
	}

	function log(string memory p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
	}

	function log(string memory p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
	}

	function log(string memory p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
	}

	function log(string memory p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
	}

	function log(bool p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
	}

	function log(bool p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
	}

	function log(bool p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
	}

	function log(bool p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
	}

	function log(bool p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
	}

	function log(bool p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
	}

	function log(bool p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
	}

	function log(bool p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
	}

	function log(bool p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
	}

	function log(bool p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
	}

	function log(bool p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
	}

	function log(bool p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
	}

	function log(address p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
	}

	function log(address p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
	}

	function log(address p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
	}

	function log(address p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
	}

	function log(address p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
	}

	function log(address p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
	}

	function log(address p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
	}

	function log(address p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
	}

	function log(address p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
	}

	function log(address p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
	}

	function log(address p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
	}

	function log(address p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
	}

	function log(address p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
	}

	function log(address p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
	}

	function log(address p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
	}

	function log(address p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
	}

	function log(uint p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
	}

}

File 26 of 57 : IPancakeRouter01.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;

interface IPancakeRouter01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
    external
    payable
    returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
    external
    returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
    external
    returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
    external
    payable
    returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

File 27 of 57 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";

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

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

    uint256 private _status;

    function __ReentrancyGuard_init() internal initializer {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal initializer {
        _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 make 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;
    }
    uint256[49] private __gap;
}

File 28 of 57 : IRateModel.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

interface IRateModel {
    function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint);
    function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactor) external view returns (uint);
}

File 29 of 57 : QDistributorTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/



import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

import "../interfaces/IQDistributor.sol";
import "../interfaces/IQore.sol";
import "../interfaces/IQubitLocker.sol";
import "../library/WhitelistUpgradeable.sol";
import "hardhat/console.sol";

import "../markets/QToken.sol";


contract QDistributorTester is IQDistributor, WhitelistUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ======= TEST FUNCTIONS ====== */
    function setEffectiveSupply(address market, address user, uint effectiveSupply) public {
        marketUsers[market][user].boostedSupply = effectiveSupply;
    }

    /* ========== CONSTANT VARIABLES ========== */

    address private constant QBT = 0x17B7163cf1Dbd286E262ddc68b553D899B93f526;

    uint constant public BOOST_PORTION_Q = 60;
    uint constant public BOOST_PORTION_MAX = 100;

    IQore public constant qore = IQore(0x21824518e7E443812586c96aB5B05E9F91831E06);
    IQubitLocker public qubitLocker; // TODO update address, need to be constant?

    /* ========== STATE VARIABLES ========== */

    mapping(address => DistributionInfo) distributions;
    mapping(address => mapping(address => UserInfo)) marketUsers;

    /* ========== MODIFIERS ========== */

    modifier updateDistributionOf(address market) {
        DistributionInfo storage dist = distributions[market];
        if (dist.accruedAt == 0) {
            dist.accruedAt = block.timestamp;
        }

        uint timeElapsed = block.timestamp > dist.accruedAt ? block.timestamp.sub(dist.accruedAt) : 0;
        if (timeElapsed > 0) {
            if (dist.totalBoostedSupply > 0) {
                dist.accPerShareSupply = dist.accPerShareSupply.add(dist.supplyRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedSupply));
            }

            if (dist.totalBoostedBorrow > 0) {
                dist.accPerShareBorrow = dist.accPerShareBorrow.add(dist.borrowRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedBorrow));
            }
        }
        dist.accruedAt = block.timestamp;
        _;
    }

    modifier onlyQore {
        require(msg.sender == address(qore), "QDistributor: caller is not Qore");
        _;
    }

    modifier onlyMarket {
        bool fromMarket = false;
        address[] memory markets = qore.allMarkets();
        for (uint i = 0; i < markets.length; i++) {
            if (msg.sender == markets[i]) {
                fromMarket = true;
            }
        }
        require(fromMarket == true, "QDistributor: caller should be market");
        _;
    }

    /* ========== EVENTS ========== */

    event QubitDistributionRateUpdated(address indexed qToken, uint supplyRate, uint borrowRate);
    event QubitClaimed(address indexed user, uint amount);

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __WhitelistUpgradeable_init();
        __ReentrancyGuard_init();
    }

    /* ========== VIEWS ========== */

    function userInfoOf(address market, address user) external view returns (UserInfo memory) {
        return marketUsers[market][user];
    }

    function accruedQubit(address market, address user) external view override returns (uint) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        uint _accruedQubit = userInfo.accruedQubit;
        uint accPerShareSupply = dist.accPerShareSupply;
        uint accPerShareBorrow = dist.accPerShareBorrow;

        uint timeElapsed = block.timestamp > dist.accruedAt ? block.timestamp.sub(dist.accruedAt) : 0;
        if (timeElapsed > 0) {
            if (dist.totalBoostedSupply > 0) {
                accPerShareSupply = accPerShareSupply.add(dist.supplyRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedSupply));

                uint pendingQubit = userInfo.boostedSupply.mul(accPerShareSupply.sub(userInfo.accPerShareSupply)).div(1e18);
                _accruedQubit = _accruedQubit.add(pendingQubit);
            }

            if (dist.totalBoostedBorrow > 0) {
                accPerShareBorrow = accPerShareBorrow.add(dist.borrowRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedBorrow));

                uint pendingQubit = userInfo.boostedBorrow.mul(accPerShareBorrow.sub(userInfo.accPerShareBorrow)).div(1e18);
                _accruedQubit = _accruedQubit.add(pendingQubit);
            }
        }
        return _accruedQubit;
    }

    function qubitRatesOf(address market) external view override returns (uint supplyRate, uint borrowRate) {
        return (distributions[market].supplyRate, distributions[market].borrowRate);
    }

    function totalBoosted(address market) external view override returns (uint boostedSupply, uint boostedBorrow) {
        return (distributions[market].totalBoostedSupply, distributions[market].totalBoostedBorrow);
    }

    function boostedBalanceOf(address market, address account) external view override returns (uint boostedSupply, uint boostedBorrow) {
        return (marketUsers[market][account].boostedSupply, marketUsers[market][account].boostedBorrow);
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setQubitLocker(address _locker) external onlyOwner {
        require(address(_locker) != address(0), "QDistributor: invalid locker");
        qubitLocker = IQubitLocker(_locker);
    }

    function setQubitDistributionRates(address qToken, uint supplyRate, uint borrowRate) external onlyOwner updateDistributionOf(qToken) {
        DistributionInfo storage dist = distributions[qToken];
        dist.supplyRate = supplyRate;
        dist.borrowRate = borrowRate;
        emit QubitDistributionRateUpdated(qToken, supplyRate, borrowRate);
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function notifySupplyUpdated(address market, address user) external override nonReentrant onlyQore updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(userInfo.accPerShareSupply);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedSupply).div(1e18));
        }
        userInfo.accPerShareSupply = dist.accPerShareSupply;

        uint boostedSupply = _calculateBoostedSupply(market, user);
        dist.totalBoostedSupply = dist.totalBoostedSupply.add(boostedSupply).sub(userInfo.boostedSupply);
        userInfo.boostedSupply = boostedSupply;
    }

    function notifyBorrowUpdated(address market, address user) external override nonReentrant onlyQore updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedBorrow > 0) {
            uint accQubitPerShare = dist.accPerShareBorrow.sub(userInfo.accPerShareBorrow);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedBorrow).div(1e18));
        }
        userInfo.accPerShareBorrow = dist.accPerShareBorrow;

        uint boostedBorrow = _calculateBoostedBorrow(market, user);
        dist.totalBoostedBorrow = dist.totalBoostedBorrow.add(boostedBorrow).sub(userInfo.boostedBorrow);
        userInfo.boostedBorrow = boostedBorrow;
    }

    function notifyTransferred(address qToken, address sender, address receiver) external override nonReentrant onlyMarket updateDistributionOf(qToken) {
        DistributionInfo storage dist = distributions[qToken];
        UserInfo storage senderInfo = marketUsers[qToken][sender];
        UserInfo storage receiverInfo = marketUsers[qToken][receiver];

        if (senderInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(senderInfo.accPerShareSupply);
            senderInfo.accruedQubit = senderInfo.accruedQubit.add(accQubitPerShare.mul(senderInfo.boostedSupply).div(1e18));
        }
        senderInfo.accPerShareSupply = dist.accPerShareSupply;

        if (receiverInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(receiverInfo.accPerShareSupply);
            receiverInfo.accruedQubit = receiverInfo.accruedQubit.add(accQubitPerShare.mul(receiverInfo.boostedSupply).div(1e18));
        }
        receiverInfo.accPerShareSupply = dist.accPerShareSupply;

        uint boostedSenderSupply = _calculateBoostedSupply(qToken, sender);
        uint boostedReceiverSupply = _calculateBoostedSupply(qToken, receiver);
        dist.totalBoostedSupply = dist.totalBoostedSupply.add(boostedSenderSupply).add(boostedReceiverSupply).sub(senderInfo.boostedSupply).sub(receiverInfo.boostedSupply);
        senderInfo.boostedSupply = boostedSenderSupply;
        receiverInfo.boostedSupply = boostedReceiverSupply;
    }

    function claimQubit(address user) external override nonReentrant {
        require(msg.sender == user, "QDistributor: invalid user");
        uint _accruedQubit = 0;

        address[] memory markets = qore.allMarkets();
        for (uint i = 0; i < markets.length; i++) {
            address market = markets[i];
            _updateSupplyOf(market, user);
            _updateBorrowOf(market, user);

            UserInfo storage userInfo = marketUsers[market][user];
            _accruedQubit = _accruedQubit.add(userInfo.accruedQubit);
            userInfo.accruedQubit = 0;
        }

        uint amount = Math.min(_accruedQubit, IBEP20(QBT).balanceOf(address(this)));
        QBT.safeTransfer(user, amount);
        emit QubitClaimed(user, amount);
    }

    function kick(address user) external override nonReentrant {
        require(qubitLocker.scoreOf(user) == 0, "QDistributor: kick not allowed");

        address[] memory markets = qore.allMarkets();
        for (uint i = 0; i < markets.length; i++) {
            address market = markets[i];
            _updateSupplyOf(market, user);
            _updateBorrowOf(market, user);
        }
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _calculateBoostedSupply(address market, address user) private view returns (uint) {
        uint defaultSupply = IQToken(market).balanceOf(user);
        uint boostedSupply = defaultSupply.mul(BOOST_PORTION_MAX - BOOST_PORTION_Q).div(BOOST_PORTION_MAX);

        uint userScore = qubitLocker.scoreOf(user);
        (uint totalScore,) = qubitLocker.totalScore();
        if (userScore > 0 && totalScore > 0) {
            uint scoreBoosted = IQToken(market).totalSupply().mul(userScore).div(totalScore).mul(BOOST_PORTION_Q).div(100);
            boostedSupply = boostedSupply.add(scoreBoosted);
        }
        return Math.min(boostedSupply, defaultSupply);
    }

    function _calculateBoostedBorrow(address market, address user) private view returns (uint) {
        uint accInterestIndex = IQToken(market).getAccInterestIndex();
        uint defaultBorrow = IQToken(market).borrowBalanceOf(user).mul(1e18).div(accInterestIndex);
        uint boostedBorrow = defaultBorrow.mul(BOOST_PORTION_MAX - BOOST_PORTION_Q).div(BOOST_PORTION_MAX);

        uint userScore = qubitLocker.scoreOf(user);
        (uint totalScore,) = qubitLocker.totalScore();
        if (userScore > 0 && totalScore > 0) {
            uint totalBorrow = IQToken(market).totalBorrow().mul(1e18).div(accInterestIndex);
            uint scoreBoosted = totalBorrow.mul(userScore).div(totalScore).mul(BOOST_PORTION_Q).div(100);
            boostedBorrow = boostedBorrow.add(scoreBoosted);
        }
        return Math.min(boostedBorrow, defaultBorrow);
    }

    function _updateSupplyOf(address market, address user) private updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(userInfo.accPerShareSupply);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedSupply).div(1e18));
        }
        userInfo.accPerShareSupply = dist.accPerShareSupply;

        uint boostedSupply = _calculateBoostedSupply(market, user);
        dist.totalBoostedSupply = dist.totalBoostedSupply.add(boostedSupply).sub(userInfo.boostedSupply);
        userInfo.boostedSupply = boostedSupply;
    }

    function _updateBorrowOf(address market, address user) private updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedBorrow > 0) {
            uint accQubitPerShare = dist.accPerShareBorrow.sub(userInfo.accPerShareBorrow);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedBorrow).div(1e18));
        }
        userInfo.accPerShareBorrow = dist.accPerShareBorrow;

        uint boostedBorrow = _calculateBoostedBorrow(market, user);
        dist.totalBoostedBorrow = dist.totalBoostedBorrow.add(boostedBorrow).sub(userInfo.boostedBorrow);
        userInfo.boostedBorrow = boostedBorrow;
    }
}

File 30 of 57 : IQubitLocker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

interface IQubitLocker {

    struct CheckPoint {
        uint totalWeightedBalance;
        uint slope;
        uint ts;
    }

    function totalBalance() external view returns (uint);
    function balanceOf(address account) external view returns (uint);
    function expiryOf(address account) external view returns (uint);
    function availableOf(address account) external view returns (uint);
    function balanceExpiryOf(address account) external view returns (uint balance, uint expiry);

    function totalScore() external view returns (uint score, uint slope);
    function scoreOf(address account) external view returns (uint);

    function deposit(uint amount, uint unlockTime) external;
    function extendLock(uint expiryTime) external;
    function withdraw() external;

    function depositBehalf(address account, uint amount, uint unlockTime) external;
    function withdrawBehalf(address account) external;
}

File 31 of 57 : WhitelistUpgradeable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";


contract WhitelistUpgradeable is OwnableUpgradeable {
    mapping (address => bool) private _whitelist;
    bool private _disable;                      // default - false means whitelist feature is working on. if true no more use of whitelist

    event Whitelisted(address indexed _address, bool whitelist);
    event EnableWhitelist();
    event DisableWhitelist();

    modifier onlyWhitelisted {
        require(_disable || _whitelist[msg.sender], "Whitelist: caller is not on the whitelist");
        _;
    }

    function __WhitelistUpgradeable_init() internal initializer {
        __Ownable_init();
    }

    function isWhitelist(address _address) public view returns(bool) {
        return _whitelist[_address];
    }

    function setWhitelist(address _address, bool _on) external onlyOwner {
        _whitelist[_address] = _on;

        emit Whitelisted(_address, _on);
    }

    function disableWhitelist(bool disable) external onlyOwner {
        _disable = disable;
        if (disable) {
            emit DisableWhitelist();
        } else {
            emit EnableWhitelist();
        }
    }

    uint256[49] private __gap;
}

File 32 of 57 : QToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";

import "../interfaces/IPancakeRouter02.sol";
import "../interfaces/IQDistributor.sol";
import "../library/QConstant.sol";
import "../library/SafeToken.sol";
import "./QMarket.sol";


contract QToken is QMarket {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANT ========== */

    IQDistributor public constant qDistributor = IQDistributor(0x67B806ab830801348ce719E0705cC2f2718117a1);

    /* ========== STATE VARIABLES ========== */

    string public name;
    string public symbol;
    uint8 public decimals;

    mapping(address => mapping(address => uint)) private _transferAllowances;

    /* ========== EVENT ========== */

    event Mint(address minter, uint mintAmount);
    event Redeem(address account, uint underlyingAmount, uint qTokenAmount);

    event Borrow(address account, uint ammount, uint accountBorrow);
    event RepayBorrow(address payer, address borrower, uint amount, uint accountBorrow);
    event LiquidateBorrow(address liquidator, address borrower, uint amount, address qTokenCollateral, uint seizeAmount);

    event Transfer(address indexed from, address indexed to, uint amount);
    event Approval(address indexed owner, address indexed spender, uint amount);

    /* ========== INITIALIZER ========== */

    function initialize(string memory _name, string memory _symbol, uint8 _decimals) external initializer {
        __QMarket_init();

        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }

    /* ========== VIEWS ========== */

    function allowance(address account, address spender) external view override returns (uint) {
        return _transferAllowances[account][spender];
    }

    function getOwner() external view returns (address) {
        return owner();
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function transfer(address dst, uint amount) external override accrue nonReentrant returns (bool) {
        _transferTokens(msg.sender, msg.sender, dst, amount);
        return true;
    }

    function transferFrom(address src, address dst, uint amount) external override accrue nonReentrant returns (bool) {
        _transferTokens(msg.sender, src, dst, amount);
        return true;
    }

    function approve(address spender, uint amount) external override returns (bool) {
        _transferAllowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function supply(address account, uint uAmount) payable external override accrue onlyQore nonReentrant returns (uint) {
        uint exchangeRate = exchangeRate();
        uAmount = underlying == address(WBNB) ? msg.value : uAmount;
        uAmount = _doTransferIn(account, uAmount);
        uint qAmount = uAmount.mul(1e18).div(exchangeRate);

        totalSupply = totalSupply.add(qAmount);
        accountBalances[account] = accountBalances[account].add(qAmount);

        emit Mint(account, qAmount);
        emit Transfer(address(this), account, qAmount);
        return qAmount;
    }

    function redeemToken(address redeemer, uint qAmount) external override accrue onlyQore nonReentrant returns (uint) {
        return _redeem(redeemer, qAmount, 0);
    }

    function redeemUnderlying(address redeemer, uint uAmount) external override accrue onlyQore nonReentrant returns (uint) {
        return _redeem(redeemer, 0, uAmount);
    }

    function borrow(address account, uint amount) external override accrue onlyQore nonReentrant returns (uint) {
        require(getCash() >= amount, "QToken: borrow amount exceeds cash");
        updateBorrowInfo(account, amount, 0);
        _doTransferOut(account, amount);

        emit Borrow(account, amount, borrowBalanceOf(account));
        return amount;
    }

    function repayBorrow(address account, uint amount) external payable override accrue onlyQore nonReentrant returns (uint) {
        if (amount == uint(-1)) {
            amount = borrowBalanceOf(account);
        }
        return _repay(account, account, underlying == address(WBNB) ? msg.value : amount);
    }

    function repayBorrowBehalf(address payer, address borrower, uint amount) external payable override accrue onlyQore nonReentrant returns (uint) {
        if (amount == uint(-1)) {
            amount = borrowBalanceOf(borrower);
        }
        return _repay(payer, borrower, underlying == address(WBNB) ? msg.value : amount);
    }

    function liquidateBorrow(address qTokenCollateral, address liquidator, address borrower, uint amount) external payable override accrue onlyQore nonReentrant returns (uint qAmountToSeize) {
        require(borrower != liquidator, "QToken: cannot liquidate yourself");

        amount = underlying == address(WBNB) ? msg.value : amount;
        amount = _repay(liquidator, borrower, amount);
        require(amount > 0 && amount < uint(- 1), "QToken: invalid repay amount");

        qAmountToSeize = IQValidator(qore.qValidator()).qTokenAmountToSeize(address(this), qTokenCollateral, amount);
        require(IQToken(payable(qTokenCollateral)).balanceOf(borrower) >= qAmountToSeize, "QToken: too much seize amount");
        emit LiquidateBorrow(liquidator, borrower, amount, qTokenCollateral, qAmountToSeize);
    }

    function seize(address liquidator, address borrower, uint qAmount) external override accrue onlyQore nonReentrant {
        accountBalances[borrower] = accountBalances[borrower].sub(qAmount);
        accountBalances[liquidator] = accountBalances[liquidator].add(qAmount);
        qDistributor.notifyTransferred(address(this), borrower, liquidator);
        emit Transfer(borrower, liquidator, qAmount);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _transferTokens(address spender, address src, address dst, uint amount) private {
        require(src != dst && IQValidator(qore.qValidator()).redeemAllowed(address(this), src, amount), "QToken: cannot transfer");
        require(amount != 0, "QToken: zero amount");

        uint _allowance = spender == src ? uint(- 1) : _transferAllowances[src][spender];
        uint _allowanceNew = _allowance.sub(amount, 'QToken: transfer amount exceeds allowance');

        accountBalances[src] = accountBalances[src].sub(amount);
        accountBalances[dst] = accountBalances[dst].add(amount);

        qDistributor.notifyTransferred(address(this), src, dst);

        if (_allowance != uint(- 1)) {
            _transferAllowances[src][msg.sender] = _allowanceNew;
        }
        emit Transfer(src, dst, amount);
    }

    function _doTransferIn(address from, uint amount) private returns (uint) {
        if (underlying == address(WBNB)) {
            require(msg.value >= amount, "QToken: value mismatch");
            return Math.min(msg.value, amount);
        } else {
            uint balanceBefore = IBEP20(underlying).balanceOf(address(this));
            underlying.safeTransferFrom(from, address(this), amount);
            return IBEP20(underlying).balanceOf(address(this)).sub(balanceBefore);
        }
    }

    function _doTransferOut(address to, uint amount) private {
        if (underlying == address(WBNB)) {
            SafeToken.safeTransferETH(to, amount);
        } else {
            underlying.safeTransfer(to, amount);
        }
    }

    function _redeem(address account, uint qAmountIn, uint uAmountIn) private returns (uint) {
        require(qAmountIn == 0 || uAmountIn == 0, "QToken: one of qAmountIn or uAmountIn must be zero");
        require(totalSupply >= qAmountIn, "QToken: not enough total supply");
        require(getCash() >= uAmountIn || uAmountIn == 0, "QToken: not enough underlying");
        require(getCash() >= qAmountIn.mul(exchangeRate()).div(1e18) || qAmountIn == 0, "QToken: not enough underlying");

        uint qAmountToRedeem = qAmountIn > 0 ? qAmountIn : uAmountIn.mul(1e18).div(exchangeRate());
        uint uAmountToRedeem = qAmountIn > 0 ? qAmountIn.mul(exchangeRate()).div(1e18) : uAmountIn;

        require(IQValidator(qore.qValidator()).redeemAllowed(address(this), account, qAmountToRedeem), "QToken: cannot redeem");

        totalSupply = totalSupply.sub(qAmountToRedeem);
        accountBalances[account] = accountBalances[account].sub(qAmountToRedeem);
        _doTransferOut(account, uAmountToRedeem);

        emit Transfer(account, address(this), qAmountToRedeem);
        emit Redeem(account, uAmountToRedeem, qAmountToRedeem);
        return uAmountToRedeem;
    }

    function _repay(address payer, address borrower, uint amount) private returns (uint) {
        uint borrowBalance = borrowBalanceOf(borrower);
        uint repayAmount = Math.min(borrowBalance, amount);
        repayAmount = _doTransferIn(payer, repayAmount);
        updateBorrowInfo(borrower, 0, repayAmount);

        if (underlying == address(WBNB)) {
            uint refundAmount = amount > repayAmount ? amount.sub(repayAmount) : 0;
            if (refundAmount > 0) {
                _doTransferOut(payer, refundAmount);
            }
        }

        emit RepayBorrow(payer, borrower, repayAmount, borrowBalanceOf(borrower));
        return repayAmount;
    }
}

File 33 of 57 : QoreAdmin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "./interfaces/IQore.sol";
import "./interfaces/IQDistributor.sol";
import "./interfaces/IPriceCalculator.sol";
import "./library/WhitelistUpgradeable.sol";
import {QConstant} from "./library/QConstant.sol";
import "./interfaces/IQToken.sol";


abstract contract QoreAdmin is IQore, WhitelistUpgradeable {

    /* ========== CONSTANT VARIABLES ========== */

    IPriceCalculator public constant priceCalculator = IPriceCalculator(0x20E5E35ba29dC3B540a1aee781D0814D5c77Bce6);

    /* ========== STATE VARIABLES ========== */

    address public keeper;
    address public override qValidator;
    IQDistributor public qDistributor;

    address[] public markets; // qTokenAddress[]
    mapping(address => QConstant.MarketInfo) public marketInfos; // (qTokenAddress => MarketInfo)

    uint public closeFactor;
    uint public override liquidationIncentive;

    /* ========== Event ========== */

    event MarketListed(address qToken);
    event MarketEntered(address qToken, address account);
    event MarketExited(address qToken, address account);

    event CloseFactorUpdated(uint newCloseFactor);
    event CollateralFactorUpdated(address qToken, uint newCollateralFactor);
    event LiquidationIncentiveUpdated(uint newLiquidationIncentive);
    event BorrowCapUpdated(address indexed qToken, uint newBorrowCap);

    /* ========== MODIFIERS ========== */

    modifier onlyKeeper {
        require(msg.sender == keeper || msg.sender == owner(), 'Qore: caller is not the owner or keeper');
        _;
    }

    modifier onlyListedMarket(address qToken) {
        require(marketInfos[qToken].isListed, "Qore: invalid market");
        _;
    }

    /* ========== INITIALIZER ========== */

    function __Qore_init() internal initializer {
        __WhitelistUpgradeable_init();

        closeFactor = 5e17;
        liquidationIncentive = 11e17;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setKeeper(address _keeper) external onlyKeeper {
        require(_keeper != address(0), "Qore: invalid keeper address");
        keeper = _keeper;
    }

    function setQValidator(address _qValidator) external onlyKeeper {
        require(_qValidator != address(0), "Qore: invalid qValidator address");
        qValidator = _qValidator;
    }

    function setQDistributor(address _qDistributor) external onlyKeeper {
        require(_qDistributor != address(0), "Qore: invalid qDistributor address");
        qDistributor = IQDistributor(_qDistributor);
    }

    function setCloseFactor(uint newCloseFactor) external onlyKeeper {
        require(newCloseFactor >= QConstant.CLOSE_FACTOR_MIN && newCloseFactor <= QConstant.CLOSE_FACTOR_MAX, "Qore: invalid close factor");
        closeFactor = newCloseFactor;
        emit CloseFactorUpdated(newCloseFactor);
    }

    function setCollateralFactor(address qToken, uint newCollateralFactor) external onlyKeeper onlyListedMarket(qToken) {
        require(newCollateralFactor <= QConstant.COLLATERAL_FACTOR_MAX, "Qore: invalid collateral factor");
        if (newCollateralFactor != 0 && priceCalculator.getUnderlyingPrice(qToken) == 0) {
            revert("Qore: invalid underlying price");
        }

        marketInfos[qToken].collateralFactor = newCollateralFactor;
        emit CollateralFactorUpdated(qToken, newCollateralFactor);
    }

    function setLiquidationIncentive(uint newLiquidationIncentive) external onlyKeeper {
        liquidationIncentive = newLiquidationIncentive;
        emit LiquidationIncentiveUpdated(newLiquidationIncentive);
    }

    function setMarketBorrowCaps(address[] calldata qTokens, uint[] calldata newBorrowCaps) external onlyKeeper {
        require(qTokens.length != 0 && qTokens.length == newBorrowCaps.length, "Qore: invalid data");

        for (uint i = 0; i < qTokens.length; i++) {
            marketInfos[qTokens[i]].borrowCap = newBorrowCaps[i];
            emit BorrowCapUpdated(qTokens[i], newBorrowCaps[i]);
        }
    }

    function listMarket(address payable qToken, uint borrowCap, uint collateralFactor) external onlyKeeper {
        require(!marketInfos[qToken].isListed, "Qore: already listed market");
        for (uint i = 0; i < markets.length; i ++) {
            require(markets[i] != qToken, "Qore: already listed market");
        }

        marketInfos[qToken] = QConstant.MarketInfo({isListed : true, borrowCap : borrowCap, collateralFactor : collateralFactor});
        markets.push(qToken);
        emit MarketListed(qToken);
    }

    function removeMarket(address payable qToken) external onlyKeeper {
        require(marketInfos[qToken].isListed, "Qore: unlisted market");
        require(IQToken(qToken).totalSupply() == 0 && IQToken(qToken).totalBorrow() == 0, "Qore: cannot remove market");

        address[] memory updatedMarkets = new address[](markets.length - 1);
        uint counter = 0;
        for (uint i = 0; i < markets.length; i++) {
            if (markets[i] != qToken) {
                updatedMarkets[counter++] = markets[i];
            }
        }
        markets = updatedMarkets;
        delete marketInfos[qToken];
    }
}

File 34 of 57 : QoreTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;


import "@openzeppelin/contracts/math/SafeMath.sol";
import "../interfaces/IQToken.sol";
import "../interfaces/IQValidator.sol";

import "../QoreAdmin.sol";


contract QoreTester is QoreAdmin {
    using SafeMath for uint;

    function notifySupplyUpdated(address market, address user) external {
        qDistributor.notifySupplyUpdated(market, user);
    }

    /* ========== CONSTANT VARIABLES ========== */

    address internal constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;

    /* ========== STATE VARIABLES ========== */

    mapping(address => address[]) public marketListOfUsers; // (account => qTokenAddress[])
    mapping(address => mapping(address => bool)) public usersOfMarket; // (qTokenAddress => (account => joined))
    address[] public totalUserList;

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __Qore_init();
    }

    /* ========== MODIFIERS ========== */

    modifier onlyMemberOfMarket(address qToken) {
        require(usersOfMarket[qToken][msg.sender], "Qore: must enter market");
        _;
    }

    /* ========== VIEWS ========== */

    function allMarkets() external view override returns (address[] memory) {
        return markets;
    }

    function marketInfoOf(address qToken) external view override returns (QConstant.MarketInfo memory) {
        return marketInfos[qToken];
    }

    function marketListOf(address account) external view override returns (address[] memory) {
        return marketListOfUsers[account];
    }

    function checkMembership(address account, address qToken) external view override returns (bool) {
        return usersOfMarket[qToken][account];
    }

    function getTotalUserList() external view override returns (address[] memory) {
        return totalUserList;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function enterMarkets(address[] memory qTokens) public override {
        for (uint i = 0; i < qTokens.length; i++) {
            _enterMarket(payable(qTokens[i]), msg.sender);
        }
    }

    function exitMarket(address qToken) external override onlyListedMarket(qToken) onlyMemberOfMarket(qToken) {
        QConstant.AccountSnapshot memory snapshot = IQToken(qToken).accruedAccountSnapshot(msg.sender);
        require(snapshot.borrowBalance == 0, "Qore: borrow balance must be zero");
        require(IQValidator(qValidator).redeemAllowed(qToken, msg.sender, snapshot.qTokenBalance), "Qore: cannot redeem");

        delete usersOfMarket[qToken][msg.sender];
        _removeUserMarket(qToken, msg.sender);
        emit MarketExited(qToken, msg.sender);
    }

    function supply(address qToken, uint uAmount) external override payable onlyListedMarket(qToken) returns (uint) {
        uAmount = IQToken(qToken).underlying() == address(WBNB) ? msg.value : uAmount;

        uint qAmount = IQToken(qToken).supply{value : msg.value}(msg.sender, uAmount);
        qDistributor.notifySupplyUpdated(qToken, msg.sender);

        return qAmount;
    }

    function redeemToken(address qToken, uint qAmount) external override onlyListedMarket(qToken) returns (uint) {
        uint uAmountRedeem = IQToken(qToken).redeemToken(msg.sender, qAmount);
        qDistributor.notifySupplyUpdated(qToken, msg.sender);

        return uAmountRedeem;
    }

    function redeemUnderlying(address qToken, uint uAmount) external override onlyListedMarket(qToken) returns (uint) {
        uint uAmountRedeem = IQToken(qToken).redeemUnderlying(msg.sender, uAmount);
        qDistributor.notifySupplyUpdated(qToken, msg.sender);

        return uAmountRedeem;
    }

    function borrow(address qToken, uint amount) external override onlyListedMarket(qToken) {
        _enterMarket(qToken, msg.sender);
        require(IQValidator(qValidator).borrowAllowed(qToken, msg.sender, amount), "Qore: cannot borrow");

        IQToken(payable(qToken)).borrow(msg.sender, amount);
        qDistributor.notifyBorrowUpdated(qToken, msg.sender);
    }

    function repayBorrow(address qToken, uint amount) external override payable onlyListedMarket(qToken) {
        IQToken(payable(qToken)).repayBorrow{value : msg.value}(msg.sender, amount);
        qDistributor.notifyBorrowUpdated(qToken, msg.sender);
    }

    function repayBorrowBehalf(address qToken, address borrower, uint amount) external override payable onlyListedMarket(qToken) {
        IQToken(payable(qToken)).repayBorrowBehalf{value : msg.value}(msg.sender, borrower, amount);
        qDistributor.notifyBorrowUpdated(qToken, borrower);
    }

    function liquidateBorrow(address qTokenBorrowed, address qTokenCollateral, address borrower, uint amount) external override payable {
        require(marketInfos[qTokenBorrowed].isListed && marketInfos[qTokenCollateral].isListed, "Qore: invalid market");
        require(IQValidator(qValidator).liquidateAllowed(qTokenBorrowed, borrower, amount, closeFactor), "Qore: cannot liquidate borrow");

        // TODO need a test for distribution update

        uint qAmountToSeize = IQToken(qTokenBorrowed).liquidateBorrow{value : msg.value}(qTokenCollateral, msg.sender, borrower, amount);
        IQToken(qTokenCollateral).seize(msg.sender, borrower, qAmountToSeize);
        qDistributor.notifySupplyUpdated(qTokenCollateral, borrower);
        qDistributor.notifySupplyUpdated(qTokenCollateral, msg.sender);
        qDistributor.notifyBorrowUpdated(qTokenBorrowed, borrower);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _enterMarket(address qToken, address _account) internal onlyListedMarket(qToken) {
        if (!usersOfMarket[qToken][_account]) {
            usersOfMarket[qToken][_account] = true;
            marketListOfUsers[_account].push(qToken);
            emit MarketEntered(qToken, _account);
        }
    }

    function _removeUserMarket(address qTokenToExit, address _account) private {
        require(marketListOfUsers[_account].length > 0, "Qore: cannot pop user market");

        address[] memory updatedMarkets = new address[](marketListOfUsers[_account].length - 1);
        uint counter = 0;
        for (uint i = 0; i < marketListOfUsers[_account].length; i++) {
            if (marketListOfUsers[_account][i] != qTokenToExit) {
                updatedMarkets[counter++] = marketListOfUsers[_account][i];
            }
        }
        marketListOfUsers[_account] = updatedMarkets;
    }
}

File 35 of 57 : Qore.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "./interfaces/IQToken.sol";
import "./interfaces/IQValidator.sol";

import "./QoreAdmin.sol";


contract Qore is QoreAdmin {
    using SafeMath for uint;

    /* ========== CONSTANT VARIABLES ========== */

    address internal constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;

    /* ========== STATE VARIABLES ========== */

    mapping(address => address[]) public marketListOfUsers; // (account => qTokenAddress[])
    mapping(address => mapping(address => bool)) public usersOfMarket; // (qTokenAddress => (account => joined))
    address[] public totalUserList;

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __Qore_init();
    }

    /* ========== MODIFIERS ========== */

    modifier onlyMemberOfMarket(address qToken) {
        require(usersOfMarket[qToken][msg.sender], "Qore: must enter market");
        _;
    }

    /* ========== VIEWS ========== */

    function allMarkets() external view override returns (address[] memory) {
        return markets;
    }

    function marketInfoOf(address qToken) external view override returns (QConstant.MarketInfo memory) {
        return marketInfos[qToken];
    }

    function marketListOf(address account) external view override returns (address[] memory) {
        return marketListOfUsers[account];
    }

    function checkMembership(address account, address qToken) external view override returns (bool) {
        return usersOfMarket[qToken][account];
    }

    function getTotalUserList() external view override returns (address[] memory) {
        return totalUserList;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function enterMarkets(address[] memory qTokens) public override {
        for (uint i = 0; i < qTokens.length; i++) {
            _enterMarket(payable(qTokens[i]), msg.sender);
        }
    }

    function exitMarket(address qToken) external override onlyListedMarket(qToken) onlyMemberOfMarket(qToken) {
        QConstant.AccountSnapshot memory snapshot = IQToken(qToken).accruedAccountSnapshot(msg.sender);
        require(snapshot.borrowBalance == 0, "Qore: borrow balance must be zero");
        require(IQValidator(qValidator).redeemAllowed(qToken, msg.sender, snapshot.qTokenBalance), "Qore: cannot redeem");

        delete usersOfMarket[qToken][msg.sender];
        _removeUserMarket(qToken, msg.sender);
        emit MarketExited(qToken, msg.sender);
    }

    function supply(address qToken, uint uAmount) external override payable onlyListedMarket(qToken) returns (uint) {
        uAmount = IQToken(qToken).underlying() == address(WBNB) ? msg.value : uAmount;

        uint qAmount = IQToken(qToken).supply{value : msg.value}(msg.sender, uAmount);
        qDistributor.notifySupplyUpdated(qToken, msg.sender);

        return qAmount;
    }

    function redeemToken(address qToken, uint qAmount) external override onlyListedMarket(qToken) returns (uint) {
        uint uAmountRedeem = IQToken(qToken).redeemToken(msg.sender, qAmount);
        qDistributor.notifySupplyUpdated(qToken, msg.sender);

        return uAmountRedeem;
    }

    function redeemUnderlying(address qToken, uint uAmount) external override onlyListedMarket(qToken) returns (uint) {
        uint uAmountRedeem = IQToken(qToken).redeemUnderlying(msg.sender, uAmount);
        qDistributor.notifySupplyUpdated(qToken, msg.sender);

        return uAmountRedeem;
    }

    function borrow(address qToken, uint amount) external override onlyListedMarket(qToken) {
        _enterMarket(qToken, msg.sender);
        require(IQValidator(qValidator).borrowAllowed(qToken, msg.sender, amount), "Qore: cannot borrow");

        IQToken(payable(qToken)).borrow(msg.sender, amount);
        qDistributor.notifyBorrowUpdated(qToken, msg.sender);
    }

    function repayBorrow(address qToken, uint amount) external override payable onlyListedMarket(qToken) {
        IQToken(payable(qToken)).repayBorrow{value : msg.value}(msg.sender, amount);
        qDistributor.notifyBorrowUpdated(qToken, msg.sender);
    }

    function repayBorrowBehalf(address qToken, address borrower, uint amount) external override payable onlyListedMarket(qToken) {
        IQToken(payable(qToken)).repayBorrowBehalf{value : msg.value}(msg.sender, borrower, amount);
        qDistributor.notifyBorrowUpdated(qToken, borrower);
    }

    function liquidateBorrow(address qTokenBorrowed, address qTokenCollateral, address borrower, uint amount) external override payable {
        require(marketInfos[qTokenBorrowed].isListed && marketInfos[qTokenCollateral].isListed, "Qore: invalid market");
        require(IQValidator(qValidator).liquidateAllowed(qTokenBorrowed, borrower, amount, closeFactor), "Qore: cannot liquidate borrow");

        uint qAmountToSeize = IQToken(qTokenBorrowed).liquidateBorrow{value : msg.value}(qTokenCollateral, msg.sender, borrower, amount);
        IQToken(qTokenCollateral).seize(msg.sender, borrower, qAmountToSeize);
        qDistributor.notifyBorrowUpdated(qTokenBorrowed, borrower);
    }

    function removeUserFromList(address _account) external onlyKeeper {
        require(marketListOfUsers[_account].length == 0, "Qore: cannot remove user");
        address[] memory updateUserList = new address[](totalUserList.length - 1);

        uint counter = 0;
        for (uint i = 0; i < totalUserList.length; i++) {
            if (totalUserList[i] != _account) {
                updateUserList[counter++] = totalUserList[i];
            }
        }
        totalUserList = updateUserList;
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _enterMarket(address qToken, address _account) internal onlyListedMarket(qToken) {
        if (!usersOfMarket[qToken][_account]) {
            usersOfMarket[qToken][_account] = true;
            if (marketListOfUsers[_account].length == 0) {
                totalUserList.push(_account);
            }
            marketListOfUsers[_account].push(qToken);
            emit MarketEntered(qToken, _account);
        }
    }

    function _removeUserMarket(address qTokenToExit, address _account) private {
        require(marketListOfUsers[_account].length > 0, "Qore: cannot pop user market");

        address[] memory updatedMarkets = new address[](marketListOfUsers[_account].length - 1);
        uint counter = 0;
        for (uint i = 0; i < marketListOfUsers[_account].length; i++) {
            if (marketListOfUsers[_account][i] != qTokenToExit) {
                updatedMarkets[counter++] = marketListOfUsers[_account][i];
            }
        }
        marketListOfUsers[_account] = updatedMarkets;
    }
}

File 36 of 57 : SimplePriceCalculatorTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "../interfaces/IQToken.sol";


contract SimplePriceCalculatorTester {
    address public constant USDT = 0x55d398326f99059fF775485246999027B3197955;
    address public constant BUSD = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;
    address public constant BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51;
    address public constant CAKE = 0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82;
    address public constant BNB = 0x0000000000000000000000000000000000000000;
    address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;

    uint public priceBunny;
    uint public priceCake;
    uint public priceBNB;

    constructor() public {
        priceBunny = 20e18;
        priceCake = 15e18;
        priceBNB = 400e18;
    }

    function setUnderlyingPrice(address qTokenAddress, uint price) public {
        IQToken qToken = IQToken(qTokenAddress);
        address addr = qToken.underlying();
        if (addr == BUNNY) {
            priceBunny = price;
        } else if (addr == CAKE) {
            priceCake = price;
        } else if (addr == BNB || addr == WBNB) {
            priceBNB = price;
        }
    }

    function getUnderlyingPrice(address qTokenAddress) public view returns (uint)  {
        IQToken qToken = IQToken(qTokenAddress);
        address addr = qToken.underlying();
        if (addr == BUNNY) {
            return priceBunny;
        } else if (addr == CAKE) {
            return priceCake;
        } else if (addr == BUSD) {
            return 1e18;
        } else if (addr == USDT) {
            return 1e18;
        } else if (addr == BNB || addr == WBNB) {
            return priceBNB;
        } else {
            return 0;
        }
    }

    function getUnderlyingPrices(address[] memory assets) public view returns (uint[] memory)  {
        uint[] memory returnValue = new uint[](assets.length);
        for (uint i = 0; i < assets.length; i++) {
            IQToken qToken = IQToken(payable(assets[i]));
            address addr = qToken.underlying();
            if (addr == BUNNY) {
                returnValue[i] = priceBunny;
            } else if (addr == CAKE) {
                returnValue[i] = priceCake;
            } else if (addr == BUSD) {
                returnValue[i] = 1e18;
            } else if (addr == USDT) {
                returnValue[i] = 1e18;
            } else if (addr == BNB || addr == WBNB) {
                returnValue[i] = priceBNB;
            } else {
                returnValue[i] = 0;
            }
        }
        return returnValue;
    }
}

File 37 of 57 : QDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

import "./library/WhitelistUpgradeable.sol";
import "./library/SafeToken.sol";
import "./interfaces/IBEP20.sol";
import "./interfaces/IQDistributor.sol";
import "./interfaces/IQubitLocker.sol";
import "./interfaces/IQToken.sol";
import "./interfaces/IQore.sol";


contract QDistributor is IQDistributor, WhitelistUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANT VARIABLES ========== */

    address private constant QBT = 0xF523e4478d909968090a232eB380E2dd6f802518;

    uint constant public BOOST_PORTION_Q = 60;
    uint constant public BOOST_PORTION_MAX = 100;

    IQore public constant qore = IQore(0xb3f98A31A02d133f65da961086EcDa4133bdf48e);
    IQubitLocker public constant qubitLocker = IQubitLocker(0xeA34f39DF510eAFFb789d575c9aa800d61476256);

    /* ========== STATE VARIABLES ========== */

    mapping(address => DistributionInfo) distributions;
    mapping(address => mapping(address => UserInfo)) marketUsers;

    /* ========== MODIFIERS ========== */

    modifier updateDistributionOf(address market) {
        DistributionInfo storage dist = distributions[market];
        if (dist.accruedAt == 0) {
            dist.accruedAt = block.timestamp;
        }

        uint timeElapsed = block.timestamp > dist.accruedAt ? block.timestamp.sub(dist.accruedAt) : 0;
        if (timeElapsed > 0) {
            if (dist.totalBoostedSupply > 0) {
                dist.accPerShareSupply = dist.accPerShareSupply.add(dist.supplyRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedSupply));
            }

            if (dist.totalBoostedBorrow > 0) {
                dist.accPerShareBorrow = dist.accPerShareBorrow.add(dist.borrowRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedBorrow));
            }
        }
        dist.accruedAt = block.timestamp;
        _;
    }

    modifier onlyQore {
        require(msg.sender == address(qore), "QDistributor: caller is not Qore");
        _;
    }

    modifier onlyMarket {
        bool fromMarket = false;
        address[] memory markets = qore.allMarkets();
        for (uint i = 0; i < markets.length; i++) {
            if (msg.sender == markets[i]) {
                fromMarket = true;
            }
        }
        require(fromMarket == true, "QDistributor: caller should be market");
        _;
    }

    /* ========== EVENTS ========== */

    event QubitDistributionRateUpdated(address indexed qToken, uint supplyRate, uint borrowRate);
    event QubitClaimed(address indexed user, uint amount);

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __WhitelistUpgradeable_init();
        __ReentrancyGuard_init();
    }

    /* ========== VIEWS ========== */

    function userInfoOf(address market, address user) external view returns (UserInfo memory) {
        return marketUsers[market][user];
    }

    function accruedQubit(address market, address user) external view override returns (uint) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        uint _accruedQubit = userInfo.accruedQubit;
        uint accPerShareSupply = dist.accPerShareSupply;
        uint accPerShareBorrow = dist.accPerShareBorrow;

        uint timeElapsed = block.timestamp > dist.accruedAt ? block.timestamp.sub(dist.accruedAt) : 0;
        if (timeElapsed > 0) {
            if (dist.totalBoostedSupply > 0) {
                accPerShareSupply = accPerShareSupply.add(dist.supplyRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedSupply));

                uint pendingQubit = userInfo.boostedSupply.mul(accPerShareSupply.sub(userInfo.accPerShareSupply)).div(1e18);
                _accruedQubit = _accruedQubit.add(pendingQubit);
            }

            if (dist.totalBoostedBorrow > 0) {
                accPerShareBorrow = accPerShareBorrow.add(dist.borrowRate.mul(timeElapsed).mul(1e18).div(dist.totalBoostedBorrow));

                uint pendingQubit = userInfo.boostedBorrow.mul(accPerShareBorrow.sub(userInfo.accPerShareBorrow)).div(1e18);
                _accruedQubit = _accruedQubit.add(pendingQubit);
            }
        }
        return _accruedQubit;
    }

    function qubitRatesOf(address market) external view override returns (uint supplyRate, uint borrowRate) {
        return (distributions[market].supplyRate, distributions[market].borrowRate);
    }

    function totalBoosted(address market) external view override returns (uint boostedSupply, uint boostedBorrow) {
        return (distributions[market].totalBoostedSupply, distributions[market].totalBoostedBorrow);
    }

    function boostedBalanceOf(address market, address account) external view override returns (uint boostedSupply, uint boostedBorrow) {
        return (marketUsers[market][account].boostedSupply, marketUsers[market][account].boostedBorrow);
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setQubitDistributionRates(address qToken, uint supplyRate, uint borrowRate) external onlyOwner updateDistributionOf(qToken) {
        DistributionInfo storage dist = distributions[qToken];
        dist.supplyRate = supplyRate;
        dist.borrowRate = borrowRate;
        emit QubitDistributionRateUpdated(qToken, supplyRate, borrowRate);
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function notifySupplyUpdated(address market, address user) external override nonReentrant onlyQore updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(userInfo.accPerShareSupply);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedSupply).div(1e18));
        }
        userInfo.accPerShareSupply = dist.accPerShareSupply;

        uint boostedSupply = _calculateBoostedSupply(market, user);
        dist.totalBoostedSupply = dist.totalBoostedSupply.add(boostedSupply).sub(userInfo.boostedSupply);
        userInfo.boostedSupply = boostedSupply;
    }

    function notifyBorrowUpdated(address market, address user) external override nonReentrant onlyQore updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedBorrow > 0) {
            uint accQubitPerShare = dist.accPerShareBorrow.sub(userInfo.accPerShareBorrow);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedBorrow).div(1e18));
        }
        userInfo.accPerShareBorrow = dist.accPerShareBorrow;

        uint boostedBorrow = _calculateBoostedBorrow(market, user);
        dist.totalBoostedBorrow = dist.totalBoostedBorrow.add(boostedBorrow).sub(userInfo.boostedBorrow);
        userInfo.boostedBorrow = boostedBorrow;
    }
    
    function notifyTransferred(address qToken, address sender, address receiver) external override nonReentrant onlyMarket updateDistributionOf(qToken) {
        DistributionInfo storage dist = distributions[qToken];
        UserInfo storage senderInfo = marketUsers[qToken][sender];
        UserInfo storage receiverInfo = marketUsers[qToken][receiver];

        if (senderInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(senderInfo.accPerShareSupply);
            senderInfo.accruedQubit = senderInfo.accruedQubit.add(accQubitPerShare.mul(senderInfo.boostedSupply).div(1e18));
        }
        senderInfo.accPerShareSupply = dist.accPerShareSupply;

        if (receiverInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(receiverInfo.accPerShareSupply);
            receiverInfo.accruedQubit = receiverInfo.accruedQubit.add(accQubitPerShare.mul(receiverInfo.boostedSupply).div(1e18));
        }
        receiverInfo.accPerShareSupply = dist.accPerShareSupply;

        uint boostedSenderSupply = _calculateBoostedSupply(qToken, sender);
        uint boostedReceiverSupply = _calculateBoostedSupply(qToken, receiver);
        dist.totalBoostedSupply = dist.totalBoostedSupply.add(boostedSenderSupply).add(boostedReceiverSupply).sub(senderInfo.boostedSupply).sub(receiverInfo.boostedSupply);
        senderInfo.boostedSupply = boostedSenderSupply;
        receiverInfo.boostedSupply = boostedReceiverSupply;
    }

    function claimQubit(address user) external override nonReentrant {
        require(msg.sender == user, "QDistributor: invalid user");
        uint _accruedQubit = 0;

        address[] memory markets = qore.allMarkets();
        for (uint i = 0; i < markets.length; i++) {
            address market = markets[i];
            _accruedQubit = _accruedQubit.add(_claimQubit(market, user));
        }

        _transferQubit(user, _accruedQubit);
    }

    function claimQubit(address market, address user) external nonReentrant {
        require(msg.sender == user, "QDistributor: invalid user");

        uint _accruedQubit = _claimQubit(market, user);
        _transferQubit(user, _accruedQubit);
    }

    function kick(address user) external override nonReentrant {
        require(qubitLocker.scoreOf(user) == 0, "QDistributor: kick not allowed");

        address[] memory markets = qore.allMarkets();
        for (uint i = 0; i < markets.length; i++) {
            address market = markets[i];
            _updateSupplyOf(market, user);
            _updateBorrowOf(market, user);
        }
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _claimQubit(address market, address user) private returns (uint _accruedQubit) {
        bool hasBoostedSupply = marketUsers[market][user].boostedSupply > 0;
        bool hasBoostedBorrow = marketUsers[market][user].boostedBorrow > 0;
        if (hasBoostedSupply)
            _updateSupplyOf(market, user);
        if (hasBoostedBorrow)
            _updateBorrowOf(market, user);

        if (hasBoostedSupply || hasBoostedBorrow) {
            UserInfo storage userInfo = marketUsers[market][user];
            _accruedQubit = _accruedQubit.add(userInfo.accruedQubit);
            userInfo.accruedQubit = 0;
        }

        return _accruedQubit;
    }

    function _transferQubit(address user, uint amount) private {
        amount = Math.min(amount, IBEP20(QBT).balanceOf(address(this)));
        QBT.safeTransfer(user, amount);
        emit QubitClaimed(user, amount);
    }

    function _calculateBoostedSupply(address market, address user) private view returns (uint) {
        uint defaultSupply = IQToken(market).balanceOf(user);
        uint boostedSupply = defaultSupply.mul(BOOST_PORTION_MAX - BOOST_PORTION_Q).div(BOOST_PORTION_MAX);

        uint userScore = qubitLocker.scoreOf(user);
        (uint totalScore,) = qubitLocker.totalScore();
        if (userScore > 0 && totalScore > 0) {
            uint scoreBoosted = IQToken(market).totalSupply().mul(userScore).div(totalScore).mul(BOOST_PORTION_Q).div(100);
            boostedSupply = boostedSupply.add(scoreBoosted);
        }
        return Math.min(boostedSupply, defaultSupply);
    }

    function _calculateBoostedBorrow(address market, address user) private view returns (uint) {
        uint accInterestIndex = IQToken(market).getAccInterestIndex();
        uint defaultBorrow = IQToken(market).borrowBalanceOf(user).mul(1e18).div(accInterestIndex);
        uint boostedBorrow = defaultBorrow.mul(BOOST_PORTION_MAX - BOOST_PORTION_Q).div(BOOST_PORTION_MAX);

        uint userScore = qubitLocker.scoreOf(user);
        (uint totalScore,) = qubitLocker.totalScore();
        if (userScore > 0 && totalScore > 0) {
            uint totalBorrow = IQToken(market).totalBorrow().mul(1e18).div(accInterestIndex);
            uint scoreBoosted = totalBorrow.mul(userScore).div(totalScore).mul(BOOST_PORTION_Q).div(100);
            boostedBorrow = boostedBorrow.add(scoreBoosted);
        }
        return Math.min(boostedBorrow, defaultBorrow);
    }

    function _updateSupplyOf(address market, address user) private updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedSupply > 0) {
            uint accQubitPerShare = dist.accPerShareSupply.sub(userInfo.accPerShareSupply);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedSupply).div(1e18));
        }
        userInfo.accPerShareSupply = dist.accPerShareSupply;

        uint boostedSupply = _calculateBoostedSupply(market, user);
        dist.totalBoostedSupply = dist.totalBoostedSupply.add(boostedSupply).sub(userInfo.boostedSupply);
        userInfo.boostedSupply = boostedSupply;
    }

    function _updateBorrowOf(address market, address user) private updateDistributionOf(market) {
        DistributionInfo storage dist = distributions[market];
        UserInfo storage userInfo = marketUsers[market][user];

        if (userInfo.boostedBorrow > 0) {
            uint accQubitPerShare = dist.accPerShareBorrow.sub(userInfo.accPerShareBorrow);
            userInfo.accruedQubit = userInfo.accruedQubit.add(accQubitPerShare.mul(userInfo.boostedBorrow).div(1e18));
        }
        userInfo.accPerShareBorrow = dist.accPerShareBorrow;

        uint boostedBorrow = _calculateBoostedBorrow(market, user);
        dist.totalBoostedBorrow = dist.totalBoostedBorrow.add(boostedBorrow).sub(userInfo.boostedBorrow);
        userInfo.boostedBorrow = boostedBorrow;
    }
}

File 38 of 57 : IBEP20.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.4.0;

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

    /**
     * @dev Returns the token decimals.
     */
    function decimals() external view returns (uint8);

    /**
     * @dev Returns the token symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the token name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the bep token owner.
     */
    function getOwner() external view returns (address);

    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

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

File 39 of 57 : QubitLocker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

import "../interfaces/IQubitLocker.sol";
import "../library/WhitelistUpgradeable.sol";
import "../library/SafeToken.sol";


contract QubitLocker is IQubitLocker, WhitelistUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANTS ============= */

    address public constant QBT = 0xF523e4478d909968090a232eB380E2dd6f802518;

    uint public constant LOCK_UNIT_BASE = 7 days;
    uint public constant LOCK_UNIT_MAX = 2 * 365 days;

    /* ========== STATE VARIABLES ========== */

    mapping(address => uint) public balances;
    mapping(address => uint) public expires;

    uint public override totalBalance;

    uint private _lastTotalScore;
    uint private _lastSlope;
    uint private _lastTimestamp;
    mapping(uint => uint) private _slopeChanges;

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __WhitelistUpgradeable_init();
        __ReentrancyGuard_init();
        _lastTimestamp = block.timestamp;
    }

    /* ========== VIEWS ========== */

    function balanceOf(address account) external view override returns (uint) {
        return balances[account];
    }

    function expiryOf(address account) external view override returns (uint) {
        return expires[account];
    }

    function availableOf(address account) external view override returns (uint) {
        return expires[account] < block.timestamp ? balances[account] : 0;
    }

    function balanceExpiryOf(address account) external view override returns (uint balance, uint expiry) {
        return (balances[account], expires[account]);
    }

    function totalScore() public view override returns (uint score, uint slope) {
        score = _lastTotalScore;
        slope = _lastSlope;

        uint prevTimestamp = _lastTimestamp;
        uint nextTimestamp = truncateExpiry(_lastTimestamp).add(LOCK_UNIT_BASE);
        while (nextTimestamp < block.timestamp) {
            uint deltaScore = nextTimestamp.sub(prevTimestamp).mul(slope);
            score = score < deltaScore ? 0 : score.sub(deltaScore);
            slope = slope.sub(_slopeChanges[nextTimestamp]);

            prevTimestamp = nextTimestamp;
            nextTimestamp = nextTimestamp.add(LOCK_UNIT_BASE);
        }

        uint deltaScore = block.timestamp > prevTimestamp ? block.timestamp.sub(prevTimestamp).mul(slope) : 0;
        score = score > deltaScore ? score.sub(deltaScore) : 0;
    }

    /**
     * @notice Calculate time-weighted balance of account
     * @param account Account of which the balance will be calculated
     */
    function scoreOf(address account) external view override returns (uint) {
        if (expires[account] < block.timestamp) return 0;
        return expires[account].sub(block.timestamp).mul(balances[account].div(LOCK_UNIT_MAX));
    }

    function truncateExpiry(uint time) public pure returns (uint) {
        return time.div(LOCK_UNIT_BASE).mul(LOCK_UNIT_BASE);
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function deposit(uint amount, uint expiry) external override nonReentrant {
        require(amount > 0, 'QubitLocker: invalid amount');

        expiry = balances[msg.sender] == 0 ? truncateExpiry(expiry) : expires[msg.sender];
        require(block.timestamp < expiry && expiry <= block.timestamp + LOCK_UNIT_MAX, 'QubitLocker: invalid expiry');

        _slopeChanges[expiry] = _slopeChanges[expiry].add(amount.div(LOCK_UNIT_MAX));
        _updateTotalScore(amount, expiry);

        QBT.safeTransferFrom(msg.sender, address(this), amount);
        totalBalance = totalBalance.add(amount);

        balances[msg.sender] = balances[msg.sender].add(amount);
        expires[msg.sender] = expiry;
    }

    function extendLock(uint nextExpiry) external override nonReentrant {
        uint amount = balances[msg.sender];
        require(amount > 0, "QubitLocker: zero balance");

        uint prevExpiry = expires[msg.sender];
        nextExpiry = truncateExpiry(nextExpiry);
        require(Math.max(prevExpiry, block.timestamp) < nextExpiry && nextExpiry <= block.timestamp + LOCK_UNIT_MAX, 'QubitLocker: invalid expiry time');

        uint slopeChange = (_slopeChanges[prevExpiry] < amount.div(LOCK_UNIT_MAX)) ? _slopeChanges[prevExpiry] : amount.div(LOCK_UNIT_MAX);
        _slopeChanges[prevExpiry] = _slopeChanges[prevExpiry].sub(slopeChange);
        _slopeChanges[nextExpiry] = _slopeChanges[nextExpiry].add(slopeChange);
        _updateTotalScoreExtendingLock(amount, prevExpiry, nextExpiry);
        expires[msg.sender] = nextExpiry;
    }

    /**
     * @notice Withdraw all tokens for `msg.sender`
     * @dev Only possible if the lock has expired
     */
    function withdraw() external override nonReentrant {
        require(balances[msg.sender] > 0 && block.timestamp >= expires[msg.sender], "QubitLocker: invalid state");
        _updateTotalScore(0, 0);

        uint amount = balances[msg.sender];
        totalBalance = totalBalance.sub(amount);
        delete balances[msg.sender];
        delete expires[msg.sender];
        QBT.safeTransfer(msg.sender, amount);
    }

    function depositBehalf(address account, uint amount, uint expiry) external override onlyWhitelisted nonReentrant {
        require(amount > 0, 'QubitLocker: invalid amount');

        expiry = balances[account] == 0 ? truncateExpiry(expiry) : expires[account];
        require(block.timestamp < expiry && expiry <= block.timestamp + LOCK_UNIT_MAX, 'QubitLocker: invalid expiry');

        _slopeChanges[expiry] = _slopeChanges[expiry].add(amount.div(LOCK_UNIT_MAX));
        _updateTotalScore(amount, expiry);

        QBT.safeTransferFrom(msg.sender, address(this), amount);
        totalBalance = totalBalance.add(amount);

        balances[account] = balances[account].add(amount);
        expires[account] = expiry;
    }

    function withdrawBehalf(address account) external override onlyWhitelisted nonReentrant {
        require(balances[account] > 0 && block.timestamp >= expires[account], "QubitLocker: invalid state");
        _updateTotalScore(0, 0);

        uint amount = balances[account];
        totalBalance = totalBalance.sub(amount);
        delete balances[account];
        delete expires[account];
        QBT.safeTransfer(account, amount);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _updateTotalScore(uint newAmount, uint nextExpiry) private {
        (uint score, uint slope) = totalScore();

        if (newAmount > 0) {
            uint slopeChange = newAmount.div(LOCK_UNIT_MAX);
            uint newAmountDeltaScore = nextExpiry.sub(block.timestamp).mul(slopeChange);

            slope = slope.add(slopeChange);
            score = score.add(newAmountDeltaScore);
        }

        _lastTotalScore = score;
        _lastSlope = slope;
        _lastTimestamp = block.timestamp;
    }

    function _updateTotalScoreExtendingLock(uint amount, uint prevExpiry, uint nextExpiry) private {
        (uint score, uint slope) = totalScore();

        uint deltaScore = nextExpiry.sub(prevExpiry).mul(amount.div(LOCK_UNIT_MAX));
        score = score.add(deltaScore);

        _lastTotalScore = score;
        _lastSlope = slope;
        _lastTimestamp = block.timestamp;
    }
}

File 40 of 57 : SafeBEP20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import './IBEP20.sol';
import '../../math/SafeMath.sol';
import '../../utils/Address.sol';

/**
 * @title SafeBEP20
 * @dev Wrappers around BEP20 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 SafeBEP20 for IBEP20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeBEP20 {
    using SafeMath for uint256;
    using Address for address;

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

    function safeTransferFrom(
        IBEP20 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
     * {IBEP20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IBEP20 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'
        // solhint-disable-next-line max-line-length
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            'SafeBEP20: approve from non-zero to non-zero allowance'
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    function safeDecreaseAllowance(
        IBEP20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(
            value,
            'SafeBEP20: decreased allowance below zero'
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @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(IBEP20 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, 'SafeBEP20: low-level call failed');
        if (returndata.length > 0) {
            // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), 'SafeBEP20: BEP20 operation did not succeed');
        }
    }
}

File 41 of 57 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.4.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, 'SafeMath: addition overflow');

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, 'SafeMath: subtraction overflow');
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, 'SafeMath: multiplication overflow');

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, 'SafeMath: division by zero');
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, 'SafeMath: modulo by zero');
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }

    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x < y ? x : y;
    }

    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
}

File 42 of 57 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.2;

/**
 * @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
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            codehash := extcodehash(account)
        }
        return (codehash != accountHash && codehash != 0x0);
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (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');
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 weiValue,
        string memory errorMessage
    ) private returns (bytes memory) {
        require(isContract(target), 'Address: call to non-contract');

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{value: weiValue}(data);
        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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 43 of 57 : QubitPresaleTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol";
import "../interfaces/IPancakeRouter02.sol";
import "../interfaces/IPancakeFactory.sol";
import "../interfaces/IPancakePair.sol";
import "../interfaces/IPresaleLocker.sol";
import "../interfaces/IQubitPresale.sol";
import "../interfaces/IPriceCalculator.sol";
import "../library/SafeToken.sol";


contract QubitPresaleTester is IQubitPresale, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANTS ============= */

    address public constant BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51;
    address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;

    address public constant BUNNY_WBNB_LP = 0x5aFEf8567414F29f0f927A0F2787b188624c10E2;
    address public constant QBT_WBNB_LP = 0x67EFeF66A55c4562144B9AcfCFbc62F9E4269b3e;

    address public constant DEPLOYER = 0xbeE397129374D0b4db7bf1654936951e5bdfe5a6;

    IPancakeRouter02 private constant router = IPancakeRouter02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    IPancakeFactory private constant factory = IPancakeFactory(0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73);
    IPriceCalculator public constant priceCalculator = IPriceCalculator(0x20E5E35ba29dC3B540a1aee781D0814D5c77Bce6);

    /* ========== STATE VARIABLES ========== */

    uint public startTime;
    uint public endTime;
    uint public presaleAmountUSD;
    uint public totalBunnyBnbLp;
    uint public qbtAmount;
    uint public override qbtBnbLpAmount;
    uint public override lpPriceAtArchive;
    uint private _distributionCursor;

    mapping(address => uint) public bunnyBnbLpOf;
    mapping(address => bool) public claimedOf;
    address[] public accountList;
    bool public archived;

    IPresaleLocker public qbtBnbLocker;

    mapping(address => uint) public refundLpOf;
    address public QBT;

    /* ========== EVENTS ========== */

    event Deposit(address indexed user, uint amount);
    event Distributed(uint length, uint remain);

    /* ========== INITIALIZER ========== */

    function initialize(uint _startTime, uint _endTime, uint _presaleAmountUSD, uint _qbtAmount, address _qbtAddress) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();

        startTime = _startTime;
        endTime = _endTime;
        presaleAmountUSD = _presaleAmountUSD;
        qbtAmount = _qbtAmount;
        QBT = _qbtAddress;

        BUNNY_WBNB_LP.safeApprove(address(router), uint(~0));
        QBT.safeApprove(address(router), uint(~0));
        BUNNY.safeApprove(address(router), uint(~0));
        WBNB.safeApprove(address(router), uint(~0));
    }

    /* ========== VIEWS ========== */

    function allocationOf(address _user) public override view returns (uint) {
        return totalBunnyBnbLp == 0 ? 0 : bunnyBnbLpOf[_user].mul(1e18).div(totalBunnyBnbLp);
    }

    function refundOf(address _user) public override view returns (uint) {
        uint lpPriceNow = lpPriceAtArchive;
        if (lpPriceAtArchive == 0) {
            (, lpPriceNow) = priceCalculator.valueOfAsset(BUNNY_WBNB_LP, 1e18);
        }

        if (totalBunnyBnbLp.mul(lpPriceNow).div(1e18) <= presaleAmountUSD) {
            return 0;
        }

        uint lpAmountToPay = presaleAmountUSD.mul(allocationOf(_user)).div(lpPriceNow);
        return bunnyBnbLpOf[_user].sub(lpAmountToPay);
    }

    function accountListLength() external override view returns (uint) {
        return accountList.length;
    }

    function presaleDataOf(address account) public view returns (PresaleData memory) {
        PresaleData memory presaleData;
        presaleData.startTime = startTime;
        presaleData.endTime = endTime;
        presaleData.userLpAmount = bunnyBnbLpOf[account];
        presaleData.totalLpAmount = totalBunnyBnbLp;
        presaleData.claimedOf = claimedOf[account];
        presaleData.refundLpAmount = refundLpOf[account];
        presaleData.qbtBnbLpAmount = qbtBnbLpAmount;

        return presaleData;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function setQubitBnbLocker(address _qubitBnbLocker) public override onlyOwner {
        require(_qubitBnbLocker != address(0), "QubitPresale: invalid address");

        qbtBnbLocker = IPresaleLocker(_qubitBnbLocker);
        qbtBnbLocker.setPresaleEndTime(endTime);
        QBT_WBNB_LP.safeApprove(address(qbtBnbLocker), uint(~0));
    }

    function setPresaleAmountUSD(uint _presaleAmountUSD) public override onlyOwner {
        require(block.timestamp < startTime, "QubitPresale: already started");

        presaleAmountUSD = _presaleAmountUSD;
    }

    function setPeriod(uint _start, uint _end) public override onlyOwner {
        require(block.timestamp < startTime, "QubitPresale: already started");
        require(block.timestamp < _start && _start < _end, "QubitPresale: invalid time values");
        require(address(qbtBnbLocker) != address(0), "QubitPresale: QbtBnbLocker must be set");

        startTime = _start;
        endTime = _end;

        qbtBnbLocker.setPresaleEndTime(endTime);
    }

    function setQbtAmount(uint _qbtAmount) public override onlyOwner {
        require(block.timestamp < startTime, "QubitPresale: already started");

        qbtAmount = _qbtAmount;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function deposit(uint _amount) public override {
        require(block.timestamp > startTime && block.timestamp < endTime, "QubitPresale: not in presale");
        require(_amount > 0, "QubitPresale: invalid amount");

        if (bunnyBnbLpOf[msg.sender] == 0) {
            accountList.push(msg.sender);
        }
        bunnyBnbLpOf[msg.sender] = bunnyBnbLpOf[msg.sender].add(_amount);
        totalBunnyBnbLp = totalBunnyBnbLp.add(_amount);

        BUNNY_WBNB_LP.safeTransferFrom(msg.sender, address(this), _amount);
        emit Deposit(msg.sender, _amount);
    }

    function archive() public override onlyOwner returns (uint bunnyAmount, uint wbnbAmount) {
        require(!archived && qbtBnbLpAmount == 0, "QubitPresale: already archived");
        require(IBEP20(QBT).balanceOf(address(this)) == qbtAmount, "QubitPresale: lack of QBT");
        require(block.timestamp > endTime, "QubitPresale: not harvest time");
        (, lpPriceAtArchive) = priceCalculator.valueOfAsset(BUNNY_WBNB_LP, 1e18);
        require(lpPriceAtArchive > 0, "QubitPresale: invalid lp price");
        uint presaleAmount = presaleAmountUSD.div(lpPriceAtArchive).mul(1e18);

        // burn manually transferred LP token
        if (IPancakePair(BUNNY_WBNB_LP).balanceOf(BUNNY_WBNB_LP) > 0) {
            IPancakePair(BUNNY_WBNB_LP).burn(DEPLOYER);
        }

        uint amount = Math.min(totalBunnyBnbLp, presaleAmount);
        (bunnyAmount, wbnbAmount) = router.removeLiquidity(BUNNY, WBNB, amount, 0, 0, address(this), block.timestamp);
        BUNNY.safeTransfer(DEAD, bunnyAmount);

        uint qbtAmountFixed = presaleAmount < totalBunnyBnbLp ? qbtAmount : qbtAmount.mul(totalBunnyBnbLp).div(presaleAmount);
        (,, qbtBnbLpAmount) = router.addLiquidity(QBT, WBNB, qbtAmountFixed, wbnbAmount, 0, 0, address(this), block.timestamp);

        archived = true;
    }

    function distribute(uint distributeThreshold) external override onlyOwner {
        require(block.timestamp > endTime, "QubitPresale: not harvest time");
        require(archived, "QubitPresale: not yet archived");
        uint start = _distributionCursor;
        uint totalUserCount = accountList.length;
        uint remain = totalUserCount > _distributionCursor ? totalUserCount - _distributionCursor : 0;
        uint length = Math.min(remain, distributeThreshold);
        for (uint i = start; i < start + length; i++) {
            address account = accountList[i];
            if (!claimedOf[account]) {
                claimedOf[account] = true;

                uint refundingLpAmount = refundOf(account);
                if (refundingLpAmount > 0 && refundLpOf[account] == 0) {
                    refundLpOf[account] = refundingLpAmount;
                    BUNNY_WBNB_LP.safeTransfer(account, refundingLpAmount);
                }

                uint depositLpAmount = qbtBnbLpAmount.mul(allocationOf(account)).div(1e18);
                if (depositLpAmount > 0) {
                    delete bunnyBnbLpOf[account];
                    // block qbtBnbLocker for test
                    // qbtBnbLocker.depositBehalf(account, depositLpAmount);
                }
            }
            _distributionCursor++;
        }
        remain = totalUserCount > _distributionCursor ? totalUserCount - _distributionCursor : 0;
        emit Distributed(length, remain);
    }

    function sweep(uint _lpAmount, uint _offerAmount) public override onlyOwner {
        require(_lpAmount <= IBEP20(BUNNY_WBNB_LP).balanceOf(address(this)), "QubitPresale: not enough token 0");
        require(_offerAmount <= IBEP20(QBT).balanceOf(address(this)), "QubitPresale: not enough token 1");
        BUNNY_WBNB_LP.safeTransfer(msg.sender, _lpAmount);
        QBT.safeTransfer(msg.sender, _offerAmount);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

}

File 44 of 57 : IPresaleLocker.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

interface IPresaleLocker {
    function setPresale(address _presaleContract) external;

    function setPresaleEndTime(uint endTime) external;

    function balanceOf(address account) external view returns (uint);

    function withdrawableBalanceOf(address account) external view returns (uint);

    function depositBehalf(address account, uint balance) external;

    function withdraw(uint amount) external;

    function withdrawAll() external;

    function recoverToken(address tokenAddress, uint tokenAmount) external;
}

File 45 of 57 : IQubitPresale.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

interface IQubitPresale {
    struct PresaleData {
        uint startTime;
        uint endTime;
        uint userLpAmount;
        uint totalLpAmount;
        bool claimedOf;
        uint refundLpAmount;
        uint qbtBnbLpAmount;
    }

    function lpPriceAtArchive() external view returns (uint);

    function qbtBnbLpAmount() external view returns (uint);

    function allocationOf(address _user) external view returns (uint);

    function refundOf(address _user) external view returns (uint);

    function accountListLength() external view returns (uint);

    function setQubitBnbLocker(address _qubitBnbLocker) external;

    function setPresaleAmountUSD(uint _limitAmount) external;

    function setPeriod(uint _start, uint _end) external;

    function setQbtAmount(uint _qbtAmount) external;

    function deposit(uint _amount) external;

    function archive() external returns (uint bunnyAmount, uint wbnbAmount);

    function distribute(uint distributeThreshold) external;

    function sweep(uint _lpAmount, uint _offerAmount) external;
}

File 46 of 57 : QubitPresale.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol";
import "../interfaces/IPancakeRouter02.sol";
import "../interfaces/IPancakeFactory.sol";
import "../interfaces/IPancakePair.sol";
import "../interfaces/IPresaleLocker.sol";
import "../interfaces/IQubitPresale.sol";
import "../interfaces/IPriceCalculator.sol";
import "../library/SafeToken.sol";


contract QubitPresale is IQubitPresale, OwnableUpgradeable, ReentrancyGuardUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANTS ============= */

    address public constant BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51;
    address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
    address public constant QBT = 0x17B7163cf1Dbd286E262ddc68b553D899B93f526;
    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;

    address public constant BUNNY_WBNB_LP = 0x5aFEf8567414F29f0f927A0F2787b188624c10E2;
    address public constant QBT_WBNB_LP = 0x67EFeF66A55c4562144B9AcfCFbc62F9E4269b3e;

    address public constant DEPLOYER = 0xbeE397129374D0b4db7bf1654936951e5bdfe5a6;

    IPancakeRouter02 private constant router = IPancakeRouter02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
    IPancakeFactory private constant factory = IPancakeFactory(0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73);
    IPriceCalculator public constant priceCalculator = IPriceCalculator(0x20E5E35ba29dC3B540a1aee781D0814D5c77Bce6);

    /* ========== STATE VARIABLES ========== */

    uint public startTime;
    uint public endTime;
    uint public presaleAmountUSD;
    uint public totalBunnyBnbLp;
    uint public qbtAmount;
    uint public override qbtBnbLpAmount;
    uint public override lpPriceAtArchive;
    uint private _distributionCursor;

    mapping(address => uint) public bunnyBnbLpOf;
    mapping(address => bool) public claimedOf;
    address[] public accountList;
    bool public archived;

    IPresaleLocker public qbtBnbLocker;

    mapping(address => uint) public refundLpOf;

    /* ========== EVENTS ========== */

    event Deposit(address indexed user, uint amount);
    event Distributed(uint length, uint remain);

    /* ========== INITIALIZER ========== */

    function initialize(uint _startTime, uint _endTime, uint _presaleAmountUSD, uint _qbtAmount) external initializer {
        __Ownable_init();
        __ReentrancyGuard_init();

        startTime = _startTime;
        endTime = _endTime;
        presaleAmountUSD = _presaleAmountUSD;
        qbtAmount = _qbtAmount;

        BUNNY_WBNB_LP.safeApprove(address(router), uint(~0));
        QBT.safeApprove(address(router), uint(~0));
        BUNNY.safeApprove(address(router), uint(~0));
        WBNB.safeApprove(address(router), uint(~0));
    }

    /* ========== VIEWS ========== */

    function allocationOf(address _user) public override view returns (uint) {
        return totalBunnyBnbLp == 0 ? 0 : bunnyBnbLpOf[_user].mul(1e18).div(totalBunnyBnbLp);
    }

    function refundOf(address _user) public override view returns (uint) {
        uint lpPriceNow = lpPriceAtArchive;
        if (lpPriceAtArchive == 0) {
            (, lpPriceNow) = priceCalculator.valueOfAsset(BUNNY_WBNB_LP, 1e18);
        }

        if (totalBunnyBnbLp.mul(lpPriceNow).div(1e18) <= presaleAmountUSD) {
            return 0;
        }

        uint lpAmountToPay = presaleAmountUSD.mul(allocationOf(_user)).div(lpPriceNow);
        return bunnyBnbLpOf[_user].sub(lpAmountToPay);
    }

    function accountListLength() external override view returns (uint) {
        return accountList.length;
    }

    function presaleDataOf(address account) public view returns (PresaleData memory) {
        PresaleData memory presaleData;
        presaleData.startTime = startTime;
        presaleData.endTime = endTime;
        presaleData.userLpAmount = bunnyBnbLpOf[account];
        presaleData.totalLpAmount = totalBunnyBnbLp;
        presaleData.claimedOf = claimedOf[account];
        presaleData.refundLpAmount = refundLpOf[account];
        presaleData.qbtBnbLpAmount = qbtBnbLpAmount;

        return presaleData;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function setQubitBnbLocker(address _qubitBnbLocker) public override onlyOwner {
        require(_qubitBnbLocker != address(0), "QubitPresale: invalid address");

        qbtBnbLocker = IPresaleLocker(_qubitBnbLocker);
        qbtBnbLocker.setPresaleEndTime(endTime);
        QBT_WBNB_LP.safeApprove(address(qbtBnbLocker), uint(~0));
    }

    function setPresaleAmountUSD(uint _presaleAmountUSD) public override onlyOwner {
        require(block.timestamp < startTime, "QubitPresale: already started");

        presaleAmountUSD = _presaleAmountUSD;
    }

    function setPeriod(uint _start, uint _end) public override onlyOwner {
        require(block.timestamp < startTime, "QubitPresale: already started");
        require(block.timestamp < _start && _start < _end, "QubitPresale: invalid time values");
        require(address(qbtBnbLocker) != address(0), "QubitPresale: QbtBnbLocker must be set");

        startTime = _start;
        endTime = _end;

        qbtBnbLocker.setPresaleEndTime(endTime);
    }

    function setQbtAmount(uint _qbtAmount) public override onlyOwner {
        require(block.timestamp < startTime, "QubitPresale: already started");

        qbtAmount = _qbtAmount;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function deposit(uint _amount) public override {
        require(block.timestamp > startTime && block.timestamp < endTime, "QubitPresale: not in presale");
        require(_amount > 0, "QubitPresale: invalid amount");

        if (bunnyBnbLpOf[msg.sender] == 0) {
            accountList.push(msg.sender);
        }
        bunnyBnbLpOf[msg.sender] = bunnyBnbLpOf[msg.sender].add(_amount);
        totalBunnyBnbLp = totalBunnyBnbLp.add(_amount);

        BUNNY_WBNB_LP.safeTransferFrom(msg.sender, address(this), _amount);
        emit Deposit(msg.sender, _amount);
    }

    function archive() public override onlyOwner returns (uint bunnyAmount, uint wbnbAmount) {
        require(!archived && qbtBnbLpAmount == 0, "QubitPresale: already archived");
        require(IBEP20(QBT).balanceOf(address(this)) == qbtAmount, "QubitPresale: lack of QBT");
        require(block.timestamp > endTime, "QubitPresale: not harvest time");
        (, lpPriceAtArchive) = priceCalculator.valueOfAsset(BUNNY_WBNB_LP, 1e18);
        require(lpPriceAtArchive > 0, "QubitPresale: invalid lp price");
        uint presaleAmount = presaleAmountUSD.div(lpPriceAtArchive).mul(1e18);

        // burn manually transferred LP token
        if (IPancakePair(BUNNY_WBNB_LP).balanceOf(BUNNY_WBNB_LP) > 0) {
            IPancakePair(BUNNY_WBNB_LP).burn(DEPLOYER);
        }

        uint amount = Math.min(totalBunnyBnbLp, presaleAmount);
        (bunnyAmount, wbnbAmount) = router.removeLiquidity(BUNNY, WBNB, amount, 0, 0, address(this), block.timestamp);
        BUNNY.safeTransfer(DEAD, bunnyAmount);

        uint qbtAmountFixed = presaleAmount < totalBunnyBnbLp ? qbtAmount : qbtAmount.mul(totalBunnyBnbLp).div(presaleAmount);
        (,, qbtBnbLpAmount) = router.addLiquidity(QBT, WBNB, qbtAmountFixed, wbnbAmount, 0, 0, address(this), block.timestamp);

        archived = true;
    }

    function distribute(uint distributeThreshold) external override onlyOwner {
        require(block.timestamp > endTime, "QubitPresale: not harvest time");
        require(archived, "QubitPresale: not yet archived");
        uint start = _distributionCursor;
        uint totalUserCount = accountList.length;
        uint remain = totalUserCount > _distributionCursor ? totalUserCount - _distributionCursor : 0;
        uint length = Math.min(remain, distributeThreshold);
        for (uint i = start; i < start + length; i++) {
            address account = accountList[i];
            if (!claimedOf[account]) {
                claimedOf[account] = true;

                uint refundingLpAmount = refundOf(account);
                if (refundingLpAmount > 0 && refundLpOf[account] == 0) {
                    refundLpOf[account] = refundingLpAmount;
                    BUNNY_WBNB_LP.safeTransfer(account, refundingLpAmount);
                }

                uint depositLpAmount = qbtBnbLpAmount.mul(allocationOf(account)).div(1e18);
                if (depositLpAmount > 0) {
                    delete bunnyBnbLpOf[account];
                    qbtBnbLocker.depositBehalf(account, depositLpAmount);
                }
            }
            _distributionCursor++;
        }
        remain = totalUserCount > _distributionCursor ? totalUserCount - _distributionCursor : 0;
        emit Distributed(length, remain);
    }

    function sweep(uint _lpAmount, uint _offerAmount) public override onlyOwner {
        require(_lpAmount <= IBEP20(BUNNY_WBNB_LP).balanceOf(address(this)), "QubitPresale: not enough token 0");
        require(_offerAmount <= IBEP20(QBT).balanceOf(address(this)), "QubitPresale: not enough token 1");
        BUNNY_WBNB_LP.safeTransfer(msg.sender, _lpAmount);
        QBT.safeTransfer(msg.sender, _offerAmount);
    }

    /* ========== PRIVATE FUNCTIONS ========== */

}

File 47 of 57 : QubitReservoir.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";
import "./library/WhitelistUpgradeable.sol";
import "./library/SafeToken.sol";


contract QubitReservoir is WhitelistUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANT VARIABLES ========== */

    address private constant QBT = 0x17B7163cf1Dbd286E262ddc68b553D899B93f526;

    /* ========== STATE VARIABLES ========== */

    address public receiver;

    uint public startAt;
    uint public ratePerSec;
    uint public ratePerSec2;
    uint public ratePerSec3;
    uint public dripped;

    /* ========== INITIALIZER ========== */

    function initialize(address _receiver, uint _ratePerSec, uint _ratePerSec2, uint _ratePerSec3, uint _startAt) external initializer {
        __WhitelistUpgradeable_init();

        require(_receiver != address(0), "QubitReservoir: invalid receiver");
        require(_ratePerSec > 0, "QubitReservoir: invalid rate");

        receiver = _receiver;
        ratePerSec = _ratePerSec;
        ratePerSec2 = _ratePerSec2;
        ratePerSec3 = _ratePerSec3;
        startAt = _startAt;
    }

    /* ========== VIEWS ========== */

    function getDripInfo() external view returns (uint, uint, uint) {
        if (block.timestamp < startAt || block.timestamp.sub(startAt) <= 30 days) {
            return (startAt, ratePerSec, dripped);
        }
        else if (30 days < block.timestamp.sub(startAt) && block.timestamp.sub(startAt) <= 60 days) {
            return (startAt, ratePerSec2, dripped);
        } else {
            return (startAt, ratePerSec3, dripped);
        }
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function drip() public onlyOwner returns (uint) {
        require(block.timestamp >= startAt, "QubitReservoir: not started");

        uint balance = IBEP20(QBT).balanceOf(address(this));
        uint totalDrip;
        if (block.timestamp.sub(startAt) <= 30 days) {
            totalDrip = ratePerSec.mul(block.timestamp.sub(startAt));
        } else if (block.timestamp.sub(startAt) <= 60 days) {
            totalDrip = ratePerSec.mul(30 days);
            totalDrip = totalDrip.add(ratePerSec2.mul(block.timestamp.sub(startAt + 30 days)));
        } else {
            totalDrip = ratePerSec.mul(30 days);
            totalDrip = totalDrip.add(ratePerSec2.mul(30 days));
            totalDrip = totalDrip.add(ratePerSec3.mul(block.timestamp.sub(startAt + 60 days)));
        }

        uint amountToDrip = Math.min(balance, totalDrip.sub(dripped));
        dripped = dripped.add(amountToDrip);
        QBT.safeTransfer(receiver, amountToDrip);
        return amountToDrip;
    }

    function setStartAt(uint _startAt) public onlyOwner {
        require(startAt <= _startAt, "QubitReservoir: invalid startAt");
        startAt = _startAt;
    }
}

File 48 of 57 : QubitDevReservoir.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";
import "./library/WhitelistUpgradeable.sol";
import "./library/SafeToken.sol";
import "./interfaces/IQubitLocker.sol";


contract QubitDevReservoir is WhitelistUpgradeable {
    using SafeMath for uint;
    using SafeToken for address;

    /* ========== CONSTANT VARIABLES ========== */

    address internal constant QBT = 0x17B7163cf1Dbd286E262ddc68b553D899B93f526;


    /* ========== STATE VARIABLES ========== */

    address public receiver;
    IQubitLocker public qubitLocker;

    uint public startAt;
    uint public ratePerSec;
    uint public dripped;

    /* ========== INITIALIZER ========== */

    function initialize(address _receiver, uint _ratePerSec, uint _startAt) external initializer {
        __WhitelistUpgradeable_init();

        require(_receiver != address(0), "QubitDevReservoir: invalid receiver");
        require(_ratePerSec > 0, "QubitDevReservoir: invalid rate");

        receiver = _receiver;
        ratePerSec = _ratePerSec;
        startAt = _startAt;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setLocker(address _qubitLocker) external onlyOwner {
        require(_qubitLocker != address(0), "QubitDevReservoir: invalid locker address");
        qubitLocker = IQubitLocker(_qubitLocker);
        IBEP20(QBT).approve(_qubitLocker, uint(- 1));
    }

    /* ========== VIEWS ========== */

    function getDripInfo() external view returns (uint, uint, uint) {
        return (startAt, ratePerSec, dripped);
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function drip() public onlyOwner returns (uint) {
        require(block.timestamp >= startAt, "QubitDevReservoir: not started");

        uint balance = IBEP20(QBT).balanceOf(address(this));
        uint totalDrip = ratePerSec.mul(block.timestamp.sub(startAt));
        uint amountToDrip = Math.min(balance, totalDrip.sub(dripped));
        dripped = dripped.add(amountToDrip);
        QBT.safeTransfer(receiver, amountToDrip);
        return amountToDrip;
    }

    function dripToLocker() public onlyOwner returns (uint) {
        require(address(qubitLocker) != address(0), "QubitDevReservoir: no locker assigned");
        require(block.timestamp >= startAt, "QubitDevReservoir: not started");
        uint balance = IBEP20(QBT).balanceOf(address(this));
        uint totalDrip = ratePerSec.mul(block.timestamp.sub(startAt));
        uint amountToDrip = Math.min(balance, totalDrip.sub(dripped));
        dripped = dripped.add(amountToDrip);

        if (qubitLocker.expiryOf(receiver) > block.timestamp) {
            qubitLocker.depositBehalf(receiver, amountToDrip, 0);
            return amountToDrip;
        }
        else {
            qubitLocker.depositBehalf(receiver, amountToDrip, block.timestamp + 365 days * 2);
            return amountToDrip;
        }
    }

    function setStartAt(uint _startAt) public onlyOwner {
        require(startAt <= _startAt, "QubitDevReservoir: invalid startAt");
        startAt = _startAt;
    }
}

File 49 of 57 : RateModelSlope.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "../../interfaces/IRateModel.sol";


contract RateModelSlope is IRateModel, OwnableUpgradeable {
    using SafeMath for uint;

    uint private baseRatePerYear;
    uint private slopePerYearFirst;
    uint private slopePerYearSecond;
    uint private optimal;

    function initialize(
        uint _baseRatePerYear,
        uint _slopePerYearFirst,
        uint _slopePerYearSecond,
        uint _optimal) external initializer {
        __Ownable_init();

        baseRatePerYear = _baseRatePerYear;
        slopePerYearFirst = _slopePerYearFirst;
        slopePerYearSecond = _slopePerYearSecond;
        optimal = _optimal;
    }

    function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) {
        if (reserves >= cash.add(borrows)) return 0;
        return borrows.mul(1e18).div(cash.add(borrows).sub(reserves));
    }

    function getBorrowRate(uint cash, uint borrows, uint reserves) public view override returns (uint) {
        uint utilization = utilizationRate(cash, borrows, reserves);
        if (optimal > 0 && utilization < optimal) {
            return baseRatePerYear.add(utilization.mul(slopePerYearFirst).div(optimal)).div(365 days);
        } else {
            uint ratio = utilization.sub(optimal).mul(1e18).div(uint(1e18).sub(optimal));
            return baseRatePerYear.add(slopePerYearFirst).add(ratio.mul(slopePerYearSecond).div(1e18)).div(365 days);
        }
    }

    function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactor) public view override returns (uint) {
        uint oneMinusReserveFactor = uint(1e18).sub(reserveFactor);
        uint borrowRate = getBorrowRate(cash, borrows, reserves);
        uint rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18);
        return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18);
    }
}

File 50 of 57 : InterestRateModelHarness.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../interfaces/IRateModel.sol";


contract InterestRateModelHarness is IRateModel {
    uint public constant opaqueBorrowFailureCode = 20;
    bool public failBorrowRate;
    uint public borrowRate;

    constructor(uint borrowRate_) public {
        borrowRate = borrowRate_;
    }

    function setFailBorrowRate(bool failBorrowRate_) public {
        failBorrowRate = failBorrowRate_;
    }

    function setBorrowRate(uint borrowRate_) public {
        borrowRate = borrowRate_;
    }

    function getBorrowRate(uint _cash, uint _borrows, uint _reserves) override public view returns (uint) {
        _cash;     // unused
        _borrows;  // unused
        _reserves; // unused
        require(!failBorrowRate, "INTEREST_RATE_MODEL_ERROR");
        return borrowRate;
    }

    function getSupplyRate(uint _cash, uint _borrows, uint _reserves, uint _reserveFactor) override external view returns (uint) {
        _cash;     // unused
        _borrows;  // unused
        _reserves; // unused
        return borrowRate * (1 - _reserveFactor);
    }
}

File 51 of 57 : RateModelLinear.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "../../interfaces/IRateModel.sol";


contract RateModelLinear is IRateModel, OwnableUpgradeable {
    using SafeMath for uint;

    uint private baseRatePerYear;
    uint private multiplierPerYear;

    function initialize(
        uint _baseRatePerYear,
        uint _multiplierPerYear
    ) external initializer {
        __Ownable_init();
        baseRatePerYear = _baseRatePerYear;
        multiplierPerYear = _multiplierPerYear;
    }

    function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) {
        if (borrows == 0) return 0;
        return borrows.mul(1e18).div(cash.add(borrows).sub(reserves));
    }

    function getBorrowRate(uint cash, uint borrows, uint reserves) public view override returns (uint) {
        uint utilization = utilizationRate(cash, borrows, reserves);
        return (utilization.mul(multiplierPerYear).div(1e18).add(baseRatePerYear)).div(365 days);
    }

    function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactor) public view override returns (uint) {
        uint oneMinusReserveFactor = uint(1e18).sub(reserveFactor);
        uint borrowRate = getBorrowRate(cash, borrows, reserves);
        uint rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18);
        return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18);
    }
}

File 52 of 57 : QValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import "../interfaces/IPriceCalculator.sol";
import "../interfaces/IQValidator.sol";
import "../interfaces/IQToken.sol";
import "../interfaces/IQore.sol";
import "../library/QConstant.sol";


contract QValidator is IQValidator, OwnableUpgradeable {
    using SafeMath for uint;

    /* ========== CONSTANT VARIABLES ========== */

    IPriceCalculator public constant oracle = IPriceCalculator(0x20E5E35ba29dC3B540a1aee781D0814D5c77Bce6);

    /* ========== STATE VARIABLES ========== */

    IQore public qore;

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __Ownable_init();
    }

    /* ========== VIEWS ========== */

    function getAccountLiquidity(address account, address qToken, uint redeemAmount, uint borrowAmount) external view override returns (uint liquidity, uint shortfall) {
        uint accCollateralValueInUSD;
        uint accBorrowValueInUSD;

        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = oracle.getUnderlyingPrices(assets);
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            accCollateralValueInUSD = accCollateralValueInUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            accBorrowValueInUSD = accBorrowValueInUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));

            if (assets[i] == qToken) {
                accBorrowValueInUSD = accBorrowValueInUSD.add(redeemAmount.mul(collateralValuePerShareInUSD).div(1e18));
                accBorrowValueInUSD = accBorrowValueInUSD.add(borrowAmount.mul(prices[i]).div(1e18));
            }
        }

        liquidity = accCollateralValueInUSD > accBorrowValueInUSD ? accCollateralValueInUSD.sub(accBorrowValueInUSD) : 0;
        shortfall = accCollateralValueInUSD > accBorrowValueInUSD ? 0 : accBorrowValueInUSD.sub(accCollateralValueInUSD);
    }

    function getAccountLiquidityValue(address account) external view override returns (uint collateralUSD, uint borrowUSD) {
        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = oracle.getUnderlyingPrices(assets);
        collateralUSD = 0;
        borrowUSD = 0;
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            collateralUSD = collateralUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            borrowUSD = borrowUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));
        }
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setQore(address _qore) external onlyOwner {
        require(_qore != address(0), "QValidator: invalid qore address");
        qore = IQore(_qore);
    }

    /* ========== ALLOWED FUNCTIONS ========== */

    function redeemAllowed(address qToken, address redeemer, uint redeemAmount) external override returns (bool) {
        (, uint shortfall) = _getAccountLiquidityInternal(redeemer, qToken, redeemAmount, 0);
        return shortfall == 0;
    }

    function borrowAllowed(address qToken, address borrower, uint borrowAmount) external override returns (bool) {
        require(oracle.getUnderlyingPrice(address(qToken)) > 0, "QValidator: Underlying price error");

        // Borrow cap of 0 corresponds to unlimited borrowing
        uint borrowCap = qore.marketInfoOf(qToken).borrowCap;
        if (borrowCap != 0) {
            uint totalBorrows = IQToken(payable(qToken)).accruedTotalBorrow();
            uint nextTotalBorrows = totalBorrows.add(borrowAmount);
            require(nextTotalBorrows < borrowCap, "QValidator: market borrow cap reached");
        }

        (, uint shortfall) = _getAccountLiquidityInternal(borrower, qToken, 0, borrowAmount);
        return shortfall == 0;
    }

    function liquidateAllowed(address qToken, address borrower, uint liquidateAmount, uint closeFactor) external override returns (bool) {
        // The borrower must have shortfall in order to be liquidate
        (, uint shortfall) = _getAccountLiquidityInternal(borrower, address(0), 0, 0);
        require(shortfall != 0, "QValidator: Insufficient shortfall");

        // The liquidator may not repay more than what is allowed by the closeFactor
        uint borrowBalance = IQToken(payable(qToken)).accruedBorrowBalanceOf(borrower);
        uint maxClose = closeFactor.mul(borrowBalance).div(1e18);
        return liquidateAmount <= maxClose;
    }

    function qTokenAmountToSeize(address qTokenBorrowed, address qTokenCollateral, uint amount) external override returns (uint seizeQAmount) {
        uint priceBorrowed = oracle.getUnderlyingPrice(qTokenBorrowed);
        uint priceCollateral = oracle.getUnderlyingPrice(qTokenCollateral);
        require(priceBorrowed != 0 && priceCollateral != 0, "QValidator: price error");

        uint exchangeRate = IQToken(payable(qTokenCollateral)).accruedExchangeRate();
        require(exchangeRate != 0, "QValidator: exchangeRate of qTokenCollateral is zero");

        // seizeQTokenAmount = amount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)
        return amount.mul(qore.liquidationIncentive()).mul(priceBorrowed).div(priceCollateral.mul(exchangeRate));
    }

    /* ========== PRIVATE FUNCTIONS ========== */

    function _getAccountLiquidityInternal(address account, address qToken, uint redeemAmount, uint borrowAmount) private returns (uint liquidity, uint shortfall) {
        uint accCollateralValueInUSD;
        uint accBorrowValueInUSD;

        address[] memory assets = qore.marketListOf(account);
        uint[] memory prices = oracle.getUnderlyingPrices(assets);
        for (uint i = 0; i < assets.length; i++) {
            require(prices[i] != 0, "QValidator: price error");
            QConstant.AccountSnapshot memory snapshot = IQToken(payable(assets[i])).accruedAccountSnapshot(account);

            uint collateralValuePerShareInUSD = snapshot.exchangeRate.mul(prices[i]).mul(qore.marketInfoOf(payable(assets[i])).collateralFactor).div(1e36);
            accCollateralValueInUSD = accCollateralValueInUSD.add(snapshot.qTokenBalance.mul(collateralValuePerShareInUSD).div(1e18));
            accBorrowValueInUSD = accBorrowValueInUSD.add(snapshot.borrowBalance.mul(prices[i]).div(1e18));

            if (assets[i] == qToken) {
                accBorrowValueInUSD = accBorrowValueInUSD.add(redeemAmount.mul(collateralValuePerShareInUSD).div(1e18));
                accBorrowValueInUSD = accBorrowValueInUSD.add(borrowAmount.mul(prices[i]).div(1e18));
            }
        }

        liquidity = accCollateralValueInUSD > accBorrowValueInUSD ? accCollateralValueInUSD.sub(accBorrowValueInUSD) : 0;
        shortfall = accCollateralValueInUSD > accBorrowValueInUSD ? 0 : accBorrowValueInUSD.sub(accCollateralValueInUSD);
    }
}

File 53 of 57 : BEP20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";


abstract contract BEP20Upgradeable is IBEP20, OwnableUpgradeable {
    using SafeMath for uint;

    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) private _allowances;
    uint256 private _totalSupply;
    string private _name;
    string private _symbol;
    uint8 private _decimals;

    uint256[50] private __gap;

    /**
     * @dev sets initials supply and the owner
     */
    function __BEP20__init(string memory name, string memory symbol, uint8 decimals) internal initializer {
        __Ownable_init();
        _name = name;
        _symbol = symbol;
        _decimals = decimals;
    }

    /**
     * @dev Returns the bep token owner.
     */
    function getOwner() external override view returns (address) {
        return owner();
    }

    /**
     * @dev Returns the token decimals.
     */
    function decimals() external override view returns (uint8) {
        return _decimals;
    }

    /**
     * @dev Returns the token symbol.
     */
    function symbol() external override view returns (string memory) {
        return _symbol;
    }

    /**
    * @dev Returns the token name.
    */
    function name() external override view returns (string memory) {
        return _name;
    }

    /**
     * @dev See {BEP20-totalSupply}.
     */
    function totalSupply() public override view returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {BEP20-balanceOf}.
     */
    function balanceOf(address account) public override view returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {BEP20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) external override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {BEP20-allowance}.
     */
    function allowance(address owner, address spender) external override view returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {BEP20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) external override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {BEP20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {BEP20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for `sender`'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "BEP20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {BEP20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {BEP20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "BEP20: decreased allowance below zero"));
        return true;
    }

    /**
   * @dev Burn `amount` tokens and decreasing the total supply.
   */
    function burn(uint256 amount) public returns (bool) {
        _burn(_msgSender(), amount);
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "BEP20: transfer from the zero address");
        require(recipient != address(0), "BEP20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "BEP20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "BEP20: burn from the zero address");

        _balances[account] = _balances[account].sub(amount, "BEP20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "BEP20: approve from the zero address");
        require(spender != address(0), "BEP20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
     * from the caller's allowance.
     *
     * See {_burn} and {_approve}.
     */
    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "BEP20: burn amount exceeds allowance"));
    }
}

File 54 of 57 : QubitTokenTester.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "../library/BEP20Upgradeable.sol";


contract QubitTokenTester is BEP20Upgradeable {

    /* ========== STATE VARIABLES ========== */

    mapping(address => bool) private _minters;

    /* ========== MODIFIERS ========== */


    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __BEP20__init("Qubit Token", "QBT", 18);
        _minters[owner()] = true;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setMinter(address minter, bool canMint) external onlyOwner {
        _minters[minter] = canMint;
    }

    function mint(address _to, uint256 _amount) public {
        _mint(_to, _amount);
    }

    /* ========== VIEWS ========== */

    function isMinter(address account) public view returns (bool) {
        return _minters[account];
    }
}

File 55 of 57 : QubitToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "./library/BEP20Upgradeable.sol";


contract QubitToken is BEP20Upgradeable {

    /* ========== STATE VARIABLES ========== */

    mapping(address => bool) private _minters;

    /* ========== MODIFIERS ========== */

    modifier onlyMinter {
        require(msg.sender != address(0) && isMinter(msg.sender), "QBT: caller is not the minter");
        _;
    }

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __BEP20__init("Qubit Token", "QBT", 18);
        _minters[owner()] = true;
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setMinter(address minter, bool canMint) external onlyOwner {
        _minters[minter] = canMint;
    }

    function mint(address _to, uint256 _amount) public onlyMinter {
        _mint(_to, _amount);
    }

    /* ========== VIEWS ========== */

    function isMinter(address account) public view returns (bool) {
        return _minters[account];
    }
}

File 56 of 57 : DashboardBSC.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

/*
      ___       ___       ___       ___       ___
     /\  \     /\__\     /\  \     /\  \     /\  \
    /::\  \   /:/ _/_   /::\  \   _\:\  \    \:\  \
    \:\:\__\ /:/_/\__\ /::\:\__\ /\/::\__\   /::\__\
     \::/  / \:\/:/  / \:\::/  / \::/\/__/  /:/\/__/
     /:/  /   \::/  /   \::/  /   \:\__\    \/__/
     \/__/     \/__/     \/__/     \/__/

*
* MIT License
* ===========
*
* Copyright (c) 2021 QubitFinance
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "../interfaces/IPriceCalculator.sol";
import "../interfaces/IQToken.sol";
import "../interfaces/IQore.sol";
import "../interfaces/IDashboard.sol";
import "../interfaces/IQDistributor.sol";
import "../interfaces/IQubitLocker.sol";
import "../interfaces/IQValidator.sol";


contract DashboardBSC is IDashboard, OwnableUpgradeable {
    using SafeMath for uint;

    /* ========== CONSTANT VARIABLES ========== */

    address private constant QBT = 0xF523e4478d909968090a232eB380E2dd6f802518;
    IPriceCalculator public constant priceCalculator = IPriceCalculator(0x20E5E35ba29dC3B540a1aee781D0814D5c77Bce6);

    /* ========== STATE VARIABLES ========== */

    IQore public qore;
    IQDistributor public qDistributor;
    IQubitLocker public qubitLocker;
    IQValidator public qValidator;

    /* ========== INITIALIZER ========== */

    function initialize() external initializer {
        __Ownable_init();
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    function setQore(address _qore) external onlyOwner {
        require(_qore != address(0), "DashboardBSC: invalid qore address");
        qore = IQore(_qore);
    }

    function setQDistributor(address _qDistributor) external onlyOwner {
        require(_qDistributor != address(0), "DashboardBSC: invalid qDistributor address");
        qDistributor = IQDistributor(_qDistributor);
    }

    function setLocker(address _qubitLocker) external onlyOwner {
        require(_qubitLocker != address(0), "DashboardBSC: invalid locker address");
        qubitLocker = IQubitLocker(_qubitLocker);
    }

    function setQValidator(address _qValidator) external onlyOwner {
        require(_qValidator != address(0), "DashboardBSC: invalid qValidator address");
        qValidator = IQValidator(_qValidator);
    }

    /* ========== VIEW FUNCTIONS ========== */

    function statusOf(address account, address[] memory markets) external view override returns (LockerData memory, MarketData[] memory) {
        MarketData[] memory results = new MarketData[](markets.length);
        for (uint i = 0; i < markets.length; i++) {
            results[i] = marketDataOf(account, markets[i]);
        }
        return (lockerDataOf(account), results);
    }

    function apyDistributionOf(address market) public view returns (uint apySupplyQBT, uint apyBorrowQBT) {
        (uint supplyRate, uint borrowRate) = qDistributor.qubitRatesOf(market);
        (uint boostedSupply, uint boostedBorrow) = qDistributor.totalBoosted(market);

        // base supply QBT APY == (qubitRate * 365 days * price Of Qubit) / (Total effective balance * exchangeRate * price of asset) / 2.5
        uint numerSupply = supplyRate.mul(365 days).mul(priceCalculator.priceOf(QBT));
        uint denomSupply = boostedSupply.mul(IQToken(market).exchangeRate()).mul(priceCalculator.getUnderlyingPrice(market)).div(1e36);
        apySupplyQBT = (denomSupply > 0) ? numerSupply.div(denomSupply).mul(100).div(250) : 0;

        // base borrow QBT APY == (qubitRate * 365 days * price Of Qubit) / (Total effective balance * interestIndex * price of asset) / 2.5
        uint numerBorrow = borrowRate.mul(365 days).mul(priceCalculator.priceOf(QBT));
        uint denomBorrow = boostedBorrow.mul(IQToken(market).getAccInterestIndex()).mul(priceCalculator.getUnderlyingPrice(market)).div(1e36);
        apyBorrowQBT = (denomBorrow > 0) ? numerBorrow.div(denomBorrow).mul(100).div(250) : 0;
    }

    function userApyDistributionOf(address account, address market) public view returns (uint userApySupplyQBT, uint userApyBorrowQBT) {
        (uint apySupplyQBT, uint apyBorrowQBT) = apyDistributionOf(market);

        (uint userBoostedSupply, uint userBoostedBorrow) = qDistributor.boostedBalanceOf(market, account);
        uint userSupply = IQToken(market).balanceOf(account);
        uint userBorrow = IQToken(market).borrowBalanceOf(account).mul(1e18).div(IQToken(market).getAccInterestIndex());

        // user supply QBT APY == ((qubitRate * 365 days * price Of Qubit) / (Total effective balance * exchangeRate * price of asset) ) * my boosted balance  / my balance
        userApySupplyQBT = (userSupply > 0) ? apySupplyQBT.mul(250).div(100).mul(userBoostedSupply).div(userSupply) : 0;
        // user borrow QBT APY == (qubitRate * 365 days * price Of Qubit) / (Total effective balance * interestIndex * price of asset) * my boosted balance  / my balance
        userApyBorrowQBT = (userBorrow > 0) ? apyBorrowQBT.mul(250).div(100).mul(userBoostedBorrow).div(userBorrow) : 0;
    }

    function marketDataOf(address account, address market) public view returns (MarketData memory) {
        MarketData memory marketData;

        (uint apySupplyQBT, uint apyBorrowQBT) = apyDistributionOf(market);
        marketData.apySupply = IQToken(market).supplyRatePerSec().mul(365 days);
        marketData.apySupplyQBT = apySupplyQBT;
        marketData.apyBorrow = IQToken(market).borrowRatePerSec().mul(365 days);
        marketData.apyBorrowQBT = apyBorrowQBT;

        // calculate my APY for QBT reward
        (uint userBoostedSupply, uint userBoostedBorrow) = qDistributor.boostedBalanceOf(market, account);
        uint userSupply = IQToken(market).balanceOf(account);
        uint userBorrow = IQToken(market).borrowBalanceOf(account).mul(1e18).div(IQToken(market).getAccInterestIndex());
        // user supply QBT APY == ((qubitRate * 365 days * price Of Qubit) / (Total effective balance * exchangeRate * price of asset) ) * my boosted balance  / my balance
        marketData.apyMySupplyQBT = (userSupply > 0) ? apySupplyQBT.mul(250).div(100).mul(userBoostedSupply).div(userSupply) : 0;
        // user borrow QBT APY == (qubitRate * 365 days * price Of Qubit) / (Total effective balance * interestIndex * price of asset) * my boosted balance  / my balance
        marketData.apyMyBorrowQBT = (userBorrow > 0) ? apyBorrowQBT.mul(250).div(100).mul(userBoostedBorrow).div(userBorrow) : 0;


        marketData.liquidity = IQToken(market).getCash();
        marketData.collateralFactor = qore.marketInfoOf(market).collateralFactor;

        marketData.membership = qore.checkMembership(account, market);
        marketData.supply = IQToken(market).underlyingBalanceOf(account);
        marketData.borrow = IQToken(market).borrowBalanceOf(account);
        marketData.totalSupply = IQToken(market).totalSupply().mul(IQToken(market).exchangeRate()).div(1e18);
        marketData.totalBorrow = IQToken(market).totalBorrow();
        (marketData.supplyBoosted, marketData.borrowBoosted) = qDistributor.boostedBalanceOf(market, account);
        (marketData.totalSupplyBoosted, marketData.totalBorrowBoosted) = qDistributor.totalBoosted(market);
        return marketData;
    }

    function marketsOf(address account, address[] memory markets) public view returns (MarketData[] memory) {
        MarketData[] memory results = new MarketData[](markets.length);
        for (uint i = 0; i < markets.length; i++) {
            results[i] = marketDataOf(account, markets[i]);
        }
        return results;
    }

    function lockerDataOf(address account) public view returns (LockerData memory) {
        LockerData memory lockerInfo;

        lockerInfo.totalLocked = qubitLocker.totalBalance();
        lockerInfo.locked = qubitLocker.balanceOf(account);

        (uint totalScore,) = qubitLocker.totalScore();
        lockerInfo.totalScore = totalScore;
        lockerInfo.score = qubitLocker.scoreOf(account);

        lockerInfo.available = qubitLocker.availableOf(account);
        lockerInfo.expiry = qubitLocker.expiryOf(account);
        return lockerInfo;
    }

    function portfolioDataOf(address account) public view returns (PortfolioData memory) {
        PortfolioData memory portfolioData;
        address[] memory markets = qore.allMarkets();
        uint supplyEarnInUSD;
        uint supplyQBTEarnInUSD;
        uint borrowInterestInUSD;
        uint borrowQBTEarnInUSD;
        uint totalInUSD;

        for (uint i = 0; i < markets.length; i++) {
            MarketData memory marketData;
            marketData = marketDataOf(account, markets[i]);

            uint marketSupplyInUSD = marketData.supply.mul(priceCalculator.getUnderlyingPrice(markets[i])).div(1e18);
            uint marketBorrowInUSD = marketData.borrow.mul(priceCalculator.getUnderlyingPrice(markets[i])).div(1e18);

            supplyEarnInUSD = supplyEarnInUSD.add(marketSupplyInUSD.mul(marketData.apySupply).div(1e18));
            borrowInterestInUSD = borrowInterestInUSD.add(marketBorrowInUSD.mul(marketData.apyBorrow).div(1e18));
            supplyQBTEarnInUSD = supplyQBTEarnInUSD.add(marketSupplyInUSD.mul(marketData.apyMySupplyQBT).div(1e18));
            borrowQBTEarnInUSD = borrowQBTEarnInUSD.add(marketBorrowInUSD.mul(marketData.apyMyBorrowQBT).div(1e18));
            totalInUSD = totalInUSD.add(marketSupplyInUSD).add(marketBorrowInUSD);

            portfolioData.supplyInUSD = portfolioData.supplyInUSD.add(marketSupplyInUSD);
            portfolioData.borrowInUSD = portfolioData.borrowInUSD.add(marketBorrowInUSD);
            if (marketData.membership) {
                portfolioData.limitInUSD = portfolioData.limitInUSD.add(marketSupplyInUSD.mul(marketData.collateralFactor).div(1e18));
            }
        }
        if (totalInUSD > 0) {
            if (supplyEarnInUSD.add(supplyQBTEarnInUSD).add(borrowQBTEarnInUSD) > borrowInterestInUSD) {
                portfolioData.userApy = int(supplyEarnInUSD.add(supplyQBTEarnInUSD).add(borrowQBTEarnInUSD).sub(borrowInterestInUSD).mul(1e18).div(totalInUSD));
            } else {
                portfolioData.userApy = int(- 1) * int(borrowInterestInUSD.sub(supplyEarnInUSD.add(supplyQBTEarnInUSD).add(borrowQBTEarnInUSD)).mul(1e18).div(totalInUSD));
            }
            portfolioData.userApySupply = supplyEarnInUSD.mul(1e18).div(totalInUSD);
            portfolioData.userApySupplyQBT = supplyQBTEarnInUSD.mul(1e18).div(totalInUSD);
            portfolioData.userApyBorrow = borrowInterestInUSD.mul(1e18).div(totalInUSD);
            portfolioData.userApyBorrowQBT = borrowQBTEarnInUSD.mul(1e18).div(totalInUSD);
        }

        return portfolioData;
    }

    function getUserLiquidityData(uint page, uint resultPerPage) external view returns (AccountLiquidityData[] memory, uint next) {
        uint index = page.mul(resultPerPage);
        uint limit = page.add(1).mul(resultPerPage);
        next = page.add(1);

        if (limit > qore.getTotalUserList().length) {
            limit = qore.getTotalUserList().length;
            next = 0;
        }

        if (qore.getTotalUserList().length == 0 || index > qore.getTotalUserList().length - 1) {
            return (new AccountLiquidityData[](0), 0);
        }

        AccountLiquidityData[] memory segment = new AccountLiquidityData[](limit.sub(index));

        uint cursor = 0;
        for (index; index < limit; index++) {
            if (index < qore.getTotalUserList().length) {
                address account = qore.getTotalUserList()[index];
                uint marketCount = qore.marketListOf(account).length;
                (uint collateralUSD, uint borrowUSD) = qValidator.getAccountLiquidityValue(account);
                segment[cursor] = AccountLiquidityData({account : account, marketCount : marketCount, collateralUSD : collateralUSD, borrowUSD : borrowUSD});
            }
            cursor++;
        }
        return (segment, next);
        //
        //        uint start;
        //        uint size;
        //        if (pageSize == 0) {
        //            start = 0;
        //            size = totalUserList.length;
        //        } else if (totalUserList.length < pageSize) {
        //            start = 0;
        //            size = page == 0 ? totalUserList.length : 0;
        //        } else {
        //            start = page.mul(pageSize);
        //            if (start <= totalUserList.length) {
        //                if (page == totalUserList.length.div(pageSize)) {
        //                    size = totalUserList.length.mod(pageSize);
        //                } else {
        //                    size = pageSize;
        //                }
        //            } else {
        //                size = 0;
        //            }
        //        }
        //
        //        AccountPortfolio[] memory portfolioList = new AccountPortfolio[](size);
        //        for (uint i = start; i < start.add(size); i ++) {
        //            portfolioList[i.sub(start)].userAddress = totalUserList[i];
        //            (portfolioList[i.sub(start)].collateralUSD, portfolioList[i.sub(start)].borrowUSD) = qValidator.getAccountLiquidityValue(totalUserList[i]);
        //            portfolioList[i.sub(start)].marketListLength = marketListOfUsers[totalUserList[i]].length;
        //        }
        //        return portfolioList;
    }

    function getUnclaimedQBT(address account) public view returns (uint unclaimedQBT) {
        address[] memory markets = qore.allMarkets();

        for (uint i = 0; i < markets.length; i++) {
            unclaimedQBT = unclaimedQBT.add(qDistributor.accruedQubit(markets[i], account));
        }
    }

    function getAvgBoost(address account) public view returns (uint) {
        address[] memory markets = qore.allMarkets();
        uint boostSum;
        uint boostNum;

        for (uint i = 0; i < markets.length; i++) {
            (uint userBoostedSupply, uint userBoostedBorrow) = qDistributor.boostedBalanceOf(markets[i], account);
            uint userSupply = IQToken(markets[i]).balanceOf(account);
            uint userBorrow = IQToken(markets[i]).borrowBalanceOf(account).mul(1e18).div(IQToken(markets[i]).getAccInterestIndex());

            uint supplyBoost = (userSupply > 0) ? userBoostedSupply.mul(1e18).div(userSupply).mul(250).div(100) : 0;
            uint borrowBoost = (userBorrow > 0) ? userBoostedBorrow.mul(1e18).div(userBorrow).mul(250).div(100) : 0;

            if (supplyBoost > 0) {
                boostSum = boostSum.add(supplyBoost);
                boostNum = boostNum.add(1);
            }
            if (borrowBoost > 0) {
                boostSum = boostSum.add(borrowBoost);
                boostNum = boostNum.add(1);
            }
        }
        return (boostNum > 0) ? boostSum.div(boostNum) : 0;
    }
}

File 57 of 57 : IDashboard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import "../library/QConstant.sol";

interface IDashboard {

    struct LockerData {
        uint totalLocked;
        uint locked;
        uint totalScore;
        uint score;
        uint available;
        uint expiry;
    }

    struct MarketData {
        uint apySupply;
        uint apySupplyQBT;
        uint apyMySupplyQBT;
        uint apyBorrow;
        uint apyBorrowQBT;
        uint apyMyBorrowQBT;
        uint liquidity;
        uint collateralFactor;
        bool membership;
        uint supply;
        uint borrow;
        uint totalSupply;
        uint totalBorrow;
        uint supplyBoosted;
        uint borrowBoosted;
        uint totalSupplyBoosted;
        uint totalBorrowBoosted;
    }

    struct PortfolioData {
        int userApy;
        uint userApySupply;
        uint userApySupplyQBT;
        uint userApyBorrow;
        uint userApyBorrowQBT;
        uint supplyInUSD;
        uint borrowInUSD;
        uint limitInUSD;
    }

    struct AccountLiquidityData {
        address account;
        uint marketCount;
        uint collateralUSD;
        uint borrowUSD;
    }

    function statusOf(address account, address[] memory markets) external view returns (LockerData memory, MarketData[] memory);
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"apyDistributionOf","outputs":[{"internalType":"uint256","name":"apySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyBorrowQBT","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAvgBoost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getUnclaimedQBT","outputs":[{"internalType":"uint256","name":"unclaimedQBT","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"page","type":"uint256"},{"internalType":"uint256","name":"resultPerPage","type":"uint256"}],"name":"getUserLiquidityData","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"marketCount","type":"uint256"},{"internalType":"uint256","name":"collateralUSD","type":"uint256"},{"internalType":"uint256","name":"borrowUSD","type":"uint256"}],"internalType":"struct IDashboard.AccountLiquidityData[]","name":"","type":"tuple[]"},{"internalType":"uint256","name":"next","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockerDataOf","outputs":[{"components":[{"internalType":"uint256","name":"totalLocked","type":"uint256"},{"internalType":"uint256","name":"locked","type":"uint256"},{"internalType":"uint256","name":"totalScore","type":"uint256"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct IDashboard.LockerData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"market","type":"address"}],"name":"marketDataOf","outputs":[{"components":[{"internalType":"uint256","name":"apySupply","type":"uint256"},{"internalType":"uint256","name":"apySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyMySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyBorrow","type":"uint256"},{"internalType":"uint256","name":"apyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"apyMyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"internalType":"bool","name":"membership","type":"bool"},{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"borrow","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalBorrow","type":"uint256"},{"internalType":"uint256","name":"supplyBoosted","type":"uint256"},{"internalType":"uint256","name":"borrowBoosted","type":"uint256"},{"internalType":"uint256","name":"totalSupplyBoosted","type":"uint256"},{"internalType":"uint256","name":"totalBorrowBoosted","type":"uint256"}],"internalType":"struct IDashboard.MarketData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address[]","name":"markets","type":"address[]"}],"name":"marketsOf","outputs":[{"components":[{"internalType":"uint256","name":"apySupply","type":"uint256"},{"internalType":"uint256","name":"apySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyMySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyBorrow","type":"uint256"},{"internalType":"uint256","name":"apyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"apyMyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"internalType":"bool","name":"membership","type":"bool"},{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"borrow","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalBorrow","type":"uint256"},{"internalType":"uint256","name":"supplyBoosted","type":"uint256"},{"internalType":"uint256","name":"borrowBoosted","type":"uint256"},{"internalType":"uint256","name":"totalSupplyBoosted","type":"uint256"},{"internalType":"uint256","name":"totalBorrowBoosted","type":"uint256"}],"internalType":"struct IDashboard.MarketData[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"portfolioDataOf","outputs":[{"components":[{"internalType":"int256","name":"userApy","type":"int256"},{"internalType":"uint256","name":"userApySupply","type":"uint256"},{"internalType":"uint256","name":"userApySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"userApyBorrow","type":"uint256"},{"internalType":"uint256","name":"userApyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"supplyInUSD","type":"uint256"},{"internalType":"uint256","name":"borrowInUSD","type":"uint256"},{"internalType":"uint256","name":"limitInUSD","type":"uint256"}],"internalType":"struct IDashboard.PortfolioData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceCalculator","outputs":[{"internalType":"contract IPriceCalculator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"qDistributor","outputs":[{"internalType":"contract IQDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"qValidator","outputs":[{"internalType":"contract IQValidator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"qore","outputs":[{"internalType":"contract IQore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"qubitLocker","outputs":[{"internalType":"contract IQubitLocker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_qubitLocker","type":"address"}],"name":"setLocker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_qDistributor","type":"address"}],"name":"setQDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_qValidator","type":"address"}],"name":"setQValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_qore","type":"address"}],"name":"setQore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address[]","name":"markets","type":"address[]"}],"name":"statusOf","outputs":[{"components":[{"internalType":"uint256","name":"totalLocked","type":"uint256"},{"internalType":"uint256","name":"locked","type":"uint256"},{"internalType":"uint256","name":"totalScore","type":"uint256"},{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct IDashboard.LockerData","name":"","type":"tuple"},{"components":[{"internalType":"uint256","name":"apySupply","type":"uint256"},{"internalType":"uint256","name":"apySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyMySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"apyBorrow","type":"uint256"},{"internalType":"uint256","name":"apyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"apyMyBorrowQBT","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"internalType":"bool","name":"membership","type":"bool"},{"internalType":"uint256","name":"supply","type":"uint256"},{"internalType":"uint256","name":"borrow","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"uint256","name":"totalBorrow","type":"uint256"},{"internalType":"uint256","name":"supplyBoosted","type":"uint256"},{"internalType":"uint256","name":"borrowBoosted","type":"uint256"},{"internalType":"uint256","name":"totalSupplyBoosted","type":"uint256"},{"internalType":"uint256","name":"totalBorrowBoosted","type":"uint256"}],"internalType":"struct IDashboard.MarketData[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"market","type":"address"}],"name":"userApyDistributionOf","outputs":[{"internalType":"uint256","name":"userApySupplyQBT","type":"uint256"},{"internalType":"uint256","name":"userApyBorrowQBT","type":"uint256"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50613661806100206000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c806376e9657b116100c3578063940ced9e1161007c578063940ced9e146102c95780639c60c85d146102d1578063a95d02d3146102e4578063adea2243146102ec578063cffe1658146102ff578063f2fde38b1461031f5761014d565b806376e9657b146102765780637bc2c30e146102965780638129fc1c1461029e57806388d9394e146102a65780638b95e335146102b95780638da5cb5b146102c15761014d565b8063326fb04911610115578063326fb049146101f15780635b7f8251146102045780635bce4b07146102175780636a89c9ac1461022c5780637022011b1461024d578063715018a61461026e5761014d565b806310404def14610152578063123d98551461017b57806315e6e7021461019c578063171060ec146101bc5780631bfdffb5146101d1575b600080fd5b610165610160366004612dfe565b610332565b60405161017291906135b8565b60405180910390f35b61018e610189366004612e52565b61070c565b604051610172929190613528565b6101af6101aa366004612e1a565b6107ca565b6040516101729190613550565b6101cf6101ca366004612dfe565b610f8c565b005b6101e46101df366004612dfe565b61101c565b604051610172919061351a565b6101656101ff366004612dfe565b611351565b6101cf610212366004612dfe565b6114a0565b61021f611527565b604051610172919061319f565b61023f61023a36600461301e565b611536565b6040516101729291906131cd565b61026061025b366004612dfe565b611aeb565b6040516101729291906135c1565b6101cf612006565b610289610284366004612dfe565b61208f565b604051610172919061355f565b61021f61250c565b6101cf61251b565b6101cf6102b4366004612dfe565b6125a6565b61021f61262d565b61021f612645565b61021f612654565b6101cf6102df366004612dfe565b612663565b61021f6126ea565b6102606102fa366004612e1a565b6126f9565b61031261030d366004612e52565b6128b1565b6040516101729190613240565b6101cf61032d366004612dfe565b61294c565b60006060606560009054906101000a90046001600160a01b03166001600160a01b031663375a7cba6040518163ffffffff1660e01b815260040160006040518083038186803b15801561038457600080fd5b505afa158015610398573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526103c09190810190612f04565b905060008060005b83518110156106e957606654845160009182916001600160a01b0390911690631a50ef2f908890869081106103f957fe5b60200260200101518a6040518363ffffffff1660e01b815260040161041f9291906131b3565b604080518083038186803b15801561043657600080fd5b505afa15801561044a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061046e919061303f565b91509150600086848151811061048057fe5b60200260200101516001600160a01b03166370a082318a6040518263ffffffff1660e01b81526004016104b3919061319f565b60206040518083038186803b1580156104cb57600080fd5b505afa1580156104df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105039190613006565b9050600061063588868151811061051657fe5b60200260200101516001600160a01b031663d88c3f226040518163ffffffff1660e01b815260040160206040518083038186803b15801561055657600080fd5b505afa15801561056a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061058e9190613006565b61062f670de0b6b3a76400008b89815181106105a657fe5b60200260200101516001600160a01b031663374c49b48f6040518263ffffffff1660e01b81526004016105d9919061319f565b60206040518083038186803b1580156105f157600080fd5b505afa158015610605573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106299190613006565b90612a0d565b90612a4e565b90506000808311610647576000610665565b610665606461062f60fa61062987838b670de0b6b3a7640000612a0d565b90506000808311610677576000610695565b610695606461062f60fa61062987838b670de0b6b3a7640000612a0d565b905081156106b7576106a78983612a80565b98506106b4886001612a80565b97505b80156106d7576106c78982612a80565b98506106d4886001612a80565b97505b5050600190940193506103c892505050565b50600081116106f9576000610703565b6107038282612a4e565b95945050505050565b610714612cc1565b606080835167ffffffffffffffff8111801561072f57600080fd5b5060405190808252806020026020018201604052801561076957816020015b610756612cf7565b81526020019060019003908161074e5790505b50905060005b84518110156107b4576107958686838151811061078857fe5b60200260200101516107ca565b8282815181106107a157fe5b602090810291909101015260010161076f565b506107be8561101c565b925090505b9250929050565b6107d2612cf7565b6107da612cf7565b6000806107e685611aeb565b9150915061082b6301e13380866001600160a01b031663587a5e506040518163ffffffff1660e01b815260040160206040518083038186803b1580156105f157600080fd5b83526020808401839052604080516356150b4160e01b8152905161087d926301e13380926001600160a01b038a16926356150b4192600480840193919291829003018186803b1580156105f157600080fd5b606084015260808301819052606654604051631a50ef2f60e01b815260009182916001600160a01b0390911690631a50ef2f906108c0908a908c906004016131b3565b604080518083038186803b1580156108d757600080fd5b505afa1580156108eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090f919061303f565b915091506000876001600160a01b03166370a082318a6040518263ffffffff1660e01b8152600401610941919061319f565b60206040518083038186803b15801561095957600080fd5b505afa15801561096d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109919190613006565b90506000610a41896001600160a01b031663d88c3f226040518163ffffffff1660e01b815260040160206040518083038186803b1580156109d157600080fd5b505afa1580156109e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a099190613006565b61062f670de0b6b3a76400008c6001600160a01b031663374c49b48f6040518263ffffffff1660e01b81526004016105d9919061319f565b905060008211610a52576000610a68565b610a688261062f866106296064838c60fa612a0d565b604088015280610a79576000610a8f565b610a8f8161062f856106296064838b60fa612a0d565b8760a0018181525050886001600160a01b0316633b1d21a26040518163ffffffff1660e01b815260040160206040518083038186803b158015610ad157600080fd5b505afa158015610ae5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b099190613006565b60c0880152606554604051636e8584fd60e01b81526001600160a01b0390911690636e8584fd90610b3e908c9060040161319f565b60606040518083038186803b158015610b5657600080fd5b505afa158015610b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8e9190612fbd565b60409081015160e0890152606554905163929fe9a160e01b81526001600160a01b039091169063929fe9a190610bca908d908d906004016131b3565b60206040518083038186803b158015610be257600080fd5b505afa158015610bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c1a9190612f9d565b15156101008801526040516324d6a2e160e21b81526001600160a01b038a169063935a8b8490610c4e908d9060040161319f565b60206040518083038186803b158015610c6657600080fd5b505afa158015610c7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c9e9190613006565b610120880152604051630dd3126d60e21b81526001600160a01b038a169063374c49b490610cd0908d9060040161319f565b60206040518083038186803b158015610ce857600080fd5b505afa158015610cfc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d209190613006565b87610140018181525050610de3670de0b6b3a764000061062f8b6001600160a01b0316633ba0b9a96040518163ffffffff1660e01b815260040160206040518083038186803b158015610d7257600080fd5b505afa158015610d86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610daa9190613006565b8c6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105f157600080fd5b87610160018181525050886001600160a01b0316638285ef406040518163ffffffff1660e01b815260040160206040518083038186803b158015610e2657600080fd5b505afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e9190613006565b610180880152606654604051631a50ef2f60e01b81526001600160a01b0390911690631a50ef2f90610e96908c908e906004016131b3565b604080518083038186803b158015610ead57600080fd5b505afa158015610ec1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ee5919061303f565b6101c08901526101a0880152606654604051633ec91d2960e01b81526001600160a01b0390911690633ec91d2990610f21908c9060040161319f565b604080518083038186803b158015610f3857600080fd5b505afa158015610f4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f70919061303f565b6102008901526101e08801525094955050505050505b92915050565b610f94612aa5565b6001600160a01b0316610fa5612645565b6001600160a01b031614610fd45760405162461bcd60e51b8152600401610fcb9061349d565b60405180910390fd5b6001600160a01b038116610ffa5760405162461bcd60e51b8152600401610fcb90613253565b606780546001600160a01b0319166001600160a01b0392909216919091179055565b611024612cc1565b61102c612cc1565b606760009054906101000a90046001600160a01b03166001600160a01b031663ad7a672f6040518163ffffffff1660e01b815260040160206040518083038186803b15801561107a57600080fd5b505afa15801561108e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b29190613006565b81526067546040516370a0823160e01b81526001600160a01b03909116906370a08231906110e490869060040161319f565b60206040518083038186803b1580156110fc57600080fd5b505afa158015611110573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111349190613006565b60208201526067546040805163c006719f60e01b815281516000936001600160a01b03169263c006719f9260048082019391829003018186803b15801561117a57600080fd5b505afa15801561118e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111b2919061303f565b506040808401829052606754905163099d7a2b60e11b81529192506001600160a01b03169063133af456906111eb90879060040161319f565b60206040518083038186803b15801561120357600080fd5b505afa158015611217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123b9190613006565b6060830152606754604051630d546da960e41b81526001600160a01b039091169063d546da909061127090879060040161319f565b60206040518083038186803b15801561128857600080fd5b505afa15801561129c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112c09190613006565b6080830152606754604051638497fab960e01b81526001600160a01b0390911690638497fab9906112f590879060040161319f565b60206040518083038186803b15801561130d57600080fd5b505afa158015611321573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113459190613006565b60a08301525092915050565b60006060606560009054906101000a90046001600160a01b03166001600160a01b031663375a7cba6040518163ffffffff1660e01b815260040160006040518083038186803b1580156113a357600080fd5b505afa1580156113b7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113df9190810190612f04565b905060005b815181101561149957606654825161148f916001600160a01b03169063b6ba253c9085908590811061141257fe5b6020026020010151876040518363ffffffff1660e01b81526004016114389291906131b3565b60206040518083038186803b15801561145057600080fd5b505afa158015611464573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114889190613006565b8490612a80565b92506001016113e4565b5050919050565b6114a8612aa5565b6001600160a01b03166114b9612645565b6001600160a01b0316146114df5760405162461bcd60e51b8152600401610fcb9061349d565b6001600160a01b0381166115055760405162461bcd60e51b8152600401610fcb90613453565b606680546001600160a01b0319166001600160a01b0392909216919091179055565b6067546001600160a01b031681565b60606000806115458585612a0d565b9050600061155885610629886001612a80565b9050611565866001612a80565b9250606560009054906101000a90046001600160a01b03166001600160a01b031663bd8f94816040518163ffffffff1660e01b815260040160006040518083038186803b1580156115b557600080fd5b505afa1580156115c9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526115f19190810190612f04565b5181111561168b57606560009054906101000a90046001600160a01b03166001600160a01b031663bd8f94816040518163ffffffff1660e01b815260040160006040518083038186803b15801561164757600080fd5b505afa15801561165b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526116839190810190612f04565b519050600092505b606560009054906101000a90046001600160a01b03166001600160a01b031663bd8f94816040518163ffffffff1660e01b815260040160006040518083038186803b1580156116d957600080fd5b505afa1580156116ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117159190810190612f04565b5115806117a357506065546040805163bd8f948160e01b815290516001926001600160a01b03169163bd8f9481916004808301926000929190829003018186803b15801561176257600080fd5b505afa158015611776573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261179e9190810190612f04565b510382115b156117e75760408051600080825260208201909252906117d9565b6117c6612d7d565b8152602001906001900390816117be5790505b5060009350935050506107c3565b60606117f38284612aa9565b67ffffffffffffffff8111801561180957600080fd5b5060405190808252806020026020018201604052801561184357816020015b611830612d7d565b8152602001906001900390816118285790505b50905060005b82841015611adf57606560009054906101000a90046001600160a01b03166001600160a01b031663bd8f94816040518163ffffffff1660e01b815260040160006040518083038186803b15801561189f57600080fd5b505afa1580156118b3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118db9190810190612f04565b51841015611ad3576065546040805163bd8f948160e01b815290516000926001600160a01b03169163bd8f94819160048083019286929190829003018186803b15801561192757600080fd5b505afa15801561193b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119639190810190612f04565b858151811061196e57fe5b60209081029190910101516065546040516305189e1160e41b81529192506000916001600160a01b0390911690635189e110906119af90859060040161319f565b60006040518083038186803b1580156119c757600080fd5b505afa1580156119db573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a039190810190612f04565b51606854604051634c22a68560e11b815291925060009182916001600160a01b0316906398454d0a90611a3a90879060040161319f565b604080518083038186803b158015611a5157600080fd5b505afa158015611a65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a89919061303f565b915091506040518060800160405280856001600160a01b0316815260200184815260200183815260200182815250868681518110611ac357fe5b6020026020010181905250505050505b60019384019301611849565b50935050509250929050565b606654604051630dcc80e760e31b81526000918291829182916001600160a01b0390911690636e64073890611b2490889060040161319f565b604080518083038186803b158015611b3b57600080fd5b505afa158015611b4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b73919061303f565b606654604051633ec91d2960e01b815292945090925060009182916001600160a01b031690633ec91d2990611bac908a9060040161319f565b604080518083038186803b158015611bc357600080fd5b505afa158015611bd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bfb919061303f565b60405163b95ed06f60e01b81529193509150600090611cb0907320e5e35ba29dc3b540a1aee781d0814d5c77bce69063b95ed06f90611c529073f523e4478d909968090a232eb380e2dd6f8025189060040161319f565b60206040518083038186803b158015611c6a57600080fd5b505afa158015611c7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ca29190613006565b610629876301e13380612a0d565b90506000611dd56ec097ce7bc90715b34b9f100000000061062f7320e5e35ba29dc3b540a1aee781d0814d5c77bce66001600160a01b031663fc57d4df8d6040518263ffffffff1660e01b8152600401611d0a919061319f565b60206040518083038186803b158015611d2257600080fd5b505afa158015611d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5a9190613006565b6106298d6001600160a01b0316633ba0b9a96040518163ffffffff1660e01b815260040160206040518083038186803b158015611d9657600080fd5b505afa158015611daa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dce9190613006565b8990612a0d565b905060008111611de6576000611dfa565b611dfa60fa61062f60646106298686612a4e565b60405163b95ed06f60e01b8152909850600090611ead907320e5e35ba29dc3b540a1aee781d0814d5c77bce69063b95ed06f90611e4f9073f523e4478d909968090a232eb380e2dd6f8025189060040161319f565b60206040518083038186803b158015611e6757600080fd5b505afa158015611e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9f9190613006565b610629886301e13380612a0d565b90506000611fd26ec097ce7bc90715b34b9f100000000061062f7320e5e35ba29dc3b540a1aee781d0814d5c77bce66001600160a01b031663fc57d4df8f6040518263ffffffff1660e01b8152600401611f07919061319f565b60206040518083038186803b158015611f1f57600080fd5b505afa158015611f33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f579190613006565b6106298f6001600160a01b031663d88c3f226040518163ffffffff1660e01b815260040160206040518083038186803b158015611f9357600080fd5b505afa158015611fa7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fcb9190613006565b8a90612a0d565b905060008111611fe3576000611ff7565b611ff760fa61062f60646106298686612a4e565b98505050505050505050915091565b61200e612aa5565b6001600160a01b031661201f612645565b6001600160a01b0316146120455760405162461bcd60e51b8152600401610fcb9061349d565b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b612097612dae565b61209f612dae565b60655460408051631bad3e5d60e11b815290516060926001600160a01b03169163375a7cba916004808301926000929190829003018186803b1580156120e457600080fd5b505afa1580156120f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121209190810190612f04565b9050600080808080805b86518110156124195761213b612cf7565b61214b8b89848151811061078857fe5b9050600061220d670de0b6b3a764000061062f7320e5e35ba29dc3b540a1aee781d0814d5c77bce66001600160a01b031663fc57d4df8d888151811061218d57fe5b60200260200101516040518263ffffffff1660e01b81526004016121b1919061319f565b60206040518083038186803b1580156121c957600080fd5b505afa1580156121dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122019190613006565b61012086015190612a0d565b905060006122cf670de0b6b3a764000061062f7320e5e35ba29dc3b540a1aee781d0814d5c77bce66001600160a01b031663fc57d4df8e898151811061224f57fe5b60200260200101516040518263ffffffff1660e01b8152600401612273919061319f565b60206040518083038186803b15801561228b57600080fd5b505afa15801561229f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c39190613006565b61014087015190612a0d565b90506122fe6122f7670de0b6b3a764000061062f866000015186612a0d90919063ffffffff16565b8a90612a80565b985061232d612326670de0b6b3a764000061062f866060015185612a0d90919063ffffffff16565b8890612a80565b965061235c612355670de0b6b3a764000061062f866040015186612a0d90919063ffffffff16565b8990612a80565b975061238b612384670de0b6b3a764000061062f8660a0015185612a0d90919063ffffffff16565b8790612a80565b95506123a18161239b8785612a80565b90612a80565b60a08c01519095506123b39083612a80565b60a08c015260c08b01516123c79082612a80565b60c08c01526101008301511561240e576124086123fd670de0b6b3a764000061062f8660e0015186612a0d90919063ffffffff16565b60e08d015190612a80565b60e08c01525b50505060010161212a565b5080156124ff578261242f8361239b8888612a80565b11156124645761245d8161062f670de0b6b3a7640000610629876124578861239b8d8d612a80565b90612aa9565b8752612492565b61248c8161062f670de0b6b3a76400006106296124858761239b8c8c612a80565b8890612aa9565b60000387525b6124a88161062f87670de0b6b3a7640000612a0d565b60208801526124c38161062f86670de0b6b3a7640000612a0d565b60408801526124de8161062f85670de0b6b3a7640000612a0d565b60608801526124f98161062f84670de0b6b3a7640000612a0d565b60808801525b5094979650505050505050565b6066546001600160a01b031681565b600054610100900460ff16806125345750612534612ad1565b80612542575060005460ff16155b61255e5760405162461bcd60e51b8152600401610fcb906133c4565b600054610100900460ff16158015612589576000805460ff1961ff0019909116610100171660011790555b612591612ae2565b80156125a3576000805461ff00191690555b50565b6125ae612aa5565b6001600160a01b03166125bf612645565b6001600160a01b0316146125e55760405162461bcd60e51b8152600401610fcb9061349d565b6001600160a01b03811661260b5760405162461bcd60e51b8152600401610fcb90613314565b606580546001600160a01b0319166001600160a01b0392909216919091179055565b7320e5e35ba29dc3b540a1aee781d0814d5c77bce681565b6033546001600160a01b031690565b6068546001600160a01b031681565b61266b612aa5565b6001600160a01b031661267c612645565b6001600160a01b0316146126a25760405162461bcd60e51b8152600401610fcb9061349d565b6001600160a01b0381166126c85760405162461bcd60e51b8152600401610fcb906134d2565b606880546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031681565b60008060008061270885611aeb565b606654604051631a50ef2f60e01b815292945090925060009182916001600160a01b031690631a50ef2f90612743908a908c906004016131b3565b604080518083038186803b15801561275a57600080fd5b505afa15801561276e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612792919061303f565b915091506000876001600160a01b03166370a082318a6040518263ffffffff1660e01b81526004016127c4919061319f565b60206040518083038186803b1580156127dc57600080fd5b505afa1580156127f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128149190613006565b90506000612854896001600160a01b031663d88c3f226040518163ffffffff1660e01b815260040160206040518083038186803b1580156109d157600080fd5b90506000821161286557600061287b565b61287b8261062f866106296064838c60fa612a0d565b97506000811161288c5760006128a2565b6128a28161062f856106296064838b60fa612a0d565b96505050505050509250929050565b606080825167ffffffffffffffff811180156128cc57600080fd5b5060405190808252806020026020018201604052801561290657816020015b6128f3612cf7565b8152602001906001900390816128eb5790505b50905060005b8351811015612944576129258585838151811061078857fe5b82828151811061293157fe5b602090810291909101015260010161290c565b509392505050565b612954612aa5565b6001600160a01b0316612965612645565b6001600160a01b03161461298b5760405162461bcd60e51b8152600401610fcb9061349d565b6001600160a01b0381166129b15760405162461bcd60e51b8152600401610fcb90613297565b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b600082612a1c57506000610f86565b82820282848281612a2957fe5b0414612a475760405162461bcd60e51b8152600401610fcb90613412565b9392505050565b6000808211612a6f5760405162461bcd60e51b8152600401610fcb9061338d565b818381612a7857fe5b049392505050565b600082820183811015612a475760405162461bcd60e51b8152600401610fcb906132dd565b3390565b600082821115612acb5760405162461bcd60e51b8152600401610fcb90613356565b50900390565b6000612adc30612b60565b15905090565b600054610100900460ff1680612afb5750612afb612ad1565b80612b09575060005460ff16155b612b255760405162461bcd60e51b8152600401610fcb906133c4565b600054610100900460ff16158015612b50576000805460ff1961ff0019909116610100171660011790555b612b58612b66565b612591612be7565b3b151590565b600054610100900460ff1680612b7f5750612b7f612ad1565b80612b8d575060005460ff16155b612ba95760405162461bcd60e51b8152600401610fcb906133c4565b600054610100900460ff16158015612591576000805460ff1961ff00199091166101001716600117905580156125a3576000805461ff001916905550565b600054610100900460ff1680612c005750612c00612ad1565b80612c0e575060005460ff16155b612c2a5760405162461bcd60e51b8152600401610fcb906133c4565b600054610100900460ff16158015612c55576000805460ff1961ff0019909116610100171660011790555b6000612c5f612aa5565b603380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35080156125a3576000805461ff001916905550565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806102200160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806080016040528060006001600160a01b031681526020016000815260200160008152602001600081525090565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610f8681613616565b600060208284031215612e0f578081fd5b8135612a4781613616565b60008060408385031215612e2c578081fd5b8235612e3781613616565b91506020830135612e4781613616565b809150509250929050565b60008060408385031215612e64578182fd5b8235612e6f81613616565b915060208381013567ffffffffffffffff811115612e8b578283fd5b8401601f81018613612e9b578283fd5b8035612eae612ea9826135f6565b6135cf565b81815283810190838501858402850186018a1015612eca578687fd5b8694505b83851015612ef457612ee08a82612df3565b835260019490940193918501918501612ece565b5080955050505050509250929050565b60006020808385031215612f16578182fd5b825167ffffffffffffffff811115612f2c578283fd5b8301601f81018513612f3c578283fd5b8051612f4a612ea9826135f6565b8181528381019083850185840285018601891015612f66578687fd5b8694505b83851015612f91578051612f7d81613616565b835260019490940193918501918501612f6a565b50979650505050505050565b600060208284031215612fae578081fd5b81518015158114612a47578182fd5b600060608284031215612fce578081fd5b612fd860606135cf565b82518015158114612fe7578283fd5b8152602083810151908201526040928301519281019290925250919050565b600060208284031215613017578081fd5b5051919050565b60008060408385031215613030578182fd5b50508035926020909101359150565b60008060408385031215613051578182fd5b505080516020909101519092909150565b6000815180845260208085019450808401835b8381101561309c576130888783516130e7565b610220969096019590820190600101613075565b509495945050505050565b15159052565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e083015261010080820151613144828501826130a7565b505061012081810151908301526101408082015190830152610160808201519083015261018080820151908301526101a080820151908301526101c080820151908301526101e0808201519083015261020090810151910152565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6040808252835182820181905260009190606090818501906020808901865b8381101561322c57815180516001600160a01b031686528381015184870152878101518887015286015186860152608090940193908201906001016131ec565b505095909501959095525092949350505050565b600060208252612a476020830184613062565b60208082526024908201527f44617368626f6172644253433a20696e76616c6964206c6f636b6572206164646040820152637265737360e01b606082015260800190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526022908201527f44617368626f6172644253433a20696e76616c696420716f7265206164647265604082015261737360f01b606082015260800190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252602a908201527f44617368626f6172644253433a20696e76616c696420714469737472696275746040820152696f72206164647265737360b01b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526028908201527f44617368626f6172644253433a20696e76616c6964207156616c696461746f72604082015267206164647265737360c01b606082015260800190565b60c08101610f8682846130ad565b600061353482856130ad565b60e060c083015261354860e0830184613062565b949350505050565b6102208101610f8682846130e7565b600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b90815260200190565b918252602082015260400190565b60405181810167ffffffffffffffff811182821017156135ee57600080fd5b604052919050565b600067ffffffffffffffff82111561360c578081fd5b5060209081020190565b6001600160a01b03811681146125a357600080fdfea2646970667358221220cef87ad8c670491e60aaad6484d8cd0e7508665c714e8f2a0044f0349f842f5564736f6c634300060c0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading