ETH Price: $3,346.40 (-0.92%)
Gas: 7.01 Gwei

Contract

0x4F9C8DDd27c5440196Af3FAC23b427dd7FC57D14
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Create EMA Oracl...133059512021-09-27 5:21:451183 days ago1632720105IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0172932954.45419843
Create EMA Oracl...133058392021-09-27 4:55:241183 days ago1632718524IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0187472959.03489982
Create EMA Oracl...133056912021-09-27 4:21:271183 days ago1632716487IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0306236395.75931121
Create EMA Oracl...133056892021-09-27 4:20:581183 days ago1632716458IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0248791788.45236604
Create EMA Oracl...133056652021-09-27 4:15:041183 days ago1632716104IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0297329692.97420986
Create EMA Oracl...133052072021-09-27 2:29:121183 days ago1632709752IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0207429363.76731931
Create EMA Oracl...133047682021-09-27 0:51:211183 days ago1632703881IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0155898746.97882431
Create EMA Oracl...133045062021-09-26 23:55:231183 days ago1632700523IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0136512342.28235001
Create EMA Oracl...132994412021-09-26 5:01:431184 days ago1632632503IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0149383746.54279287
Create EMA Oracl...132988612021-09-26 2:47:261184 days ago1632624446IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0199880861.82651773
Create EMA Oracl...132982502021-09-26 0:29:201184 days ago1632616160IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0149980246.1096014
Create EMA Oracl...132979742021-09-25 23:29:241184 days ago1632612564IN
0x4F9C8DDd...d7FC57D14
0 ETH0.009739129.34889927
Create EMA Oracl...132976382021-09-25 22:18:131184 days ago1632608293IN
0x4F9C8DDd...d7FC57D14
0 ETH0.013192339.79469729
Create EMA Oracl...132971962021-09-25 20:44:181184 days ago1632602658IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0202748762.71632481
Create EMA Oracl...132968752021-09-25 19:31:311184 days ago1632598291IN
0x4F9C8DDd...d7FC57D14
0 ETH0.020775464.60818
Create EMA Oracl...132850032021-09-23 23:38:481186 days ago1632440328IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0188691466.87534026
Create EMA Oracl...132846742021-09-23 22:29:411186 days ago1632436181IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0224955579.7347026
Create EMA Oracl...132844052021-09-23 21:27:421186 days ago1632432462IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0194205668.8296804
Create EMA Oracl...132841112021-09-23 20:24:571186 days ago1632428697IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0165459558.6415712
Create EMA Oracl...132838232021-09-23 19:20:401186 days ago1632424840IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0217064776.931318
Create EMA Oracl...132833922021-09-23 17:44:321186 days ago1632419072IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0210607574.64275936
Create EMA Oracl...132810692021-09-23 9:01:271187 days ago1632387687IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0172684861.20234186
Create EMA Oracl...132808432021-09-23 8:16:331187 days ago1632384993IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0142800250.61076448
Create EMA Oracl...132804342021-09-23 6:42:391187 days ago1632379359IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0141090650.00485312
Create EMA Oracl...132799052021-09-23 4:42:061187 days ago1632372126IN
0x4F9C8DDd...d7FC57D14
0 ETH0.0138620549.13147756
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
133059512021-09-27 5:21:451183 days ago1632720105
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133058392021-09-27 4:55:241183 days ago1632718524
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133056912021-09-27 4:21:271183 days ago1632716487
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133056892021-09-27 4:20:581183 days ago1632716458
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133056652021-09-27 4:15:041183 days ago1632716104
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133052072021-09-27 2:29:121183 days ago1632709752
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133047682021-09-27 0:51:211183 days ago1632703881
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
133045062021-09-26 23:55:231183 days ago1632700523
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132994412021-09-26 5:01:431184 days ago1632632503
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132988612021-09-26 2:47:261184 days ago1632624446
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132982502021-09-26 0:29:201184 days ago1632616160
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132979742021-09-25 23:29:241184 days ago1632612564
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132976382021-09-25 22:18:131184 days ago1632608293
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132971962021-09-25 20:44:181184 days ago1632602658
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132968752021-09-25 19:31:311184 days ago1632598291
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132850032021-09-23 23:38:481186 days ago1632440328
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132846742021-09-23 22:29:411186 days ago1632436181
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132844052021-09-23 21:27:421186 days ago1632432462
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132841112021-09-23 20:24:571186 days ago1632428697
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132838232021-09-23 19:20:401186 days ago1632424840
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132833922021-09-23 17:44:321186 days ago1632419072
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132810692021-09-23 9:01:271187 days ago1632387687
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132808432021-09-23 8:16:331187 days ago1632384993
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132804342021-09-23 6:42:391187 days ago1632379359
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
132799052021-09-23 4:42:061187 days ago1632372126
0x4F9C8DDd...d7FC57D14
 Contract Creation0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Factory

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, GNU GPLv3 license
File 1 of 82 : Factory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {FundingMultitoken} from "./tokens/FundingMultitoken.sol";
import {NFTWithSVG} from "./tokens/NFTWithSVG.sol";
import {ZeroCouponBond} from "./zero-coupon-bond/ZeroCouponBond.sol";
import {EMAOracle} from "./models/interest-oracle/EMAOracle.sol";
import {AaveMarket} from "./moneymarkets/aave/AaveMarket.sol";
import {BProtocolMarket} from "./moneymarkets/bprotocol/BProtocolMarket.sol";
import {
    CompoundERC20Market
} from "./moneymarkets/compound/CompoundERC20Market.sol";
import {CreamERC20Market} from "./moneymarkets/cream/CreamERC20Market.sol";
import {HarvestMarket} from "./moneymarkets/harvest/HarvestMarket.sol";
import {YVaultMarket} from "./moneymarkets/yvault/YVaultMarket.sol";
import {DInterest} from "./DInterest.sol";
import {DInterestWithDepositFee} from "./DInterestWithDepositFee.sol";

contract Factory {
    using Clones for address;

    event CreateClone(
        string indexed contractName,
        address template,
        bytes32 salt,
        address clone
    );

    function createNFT(
        address template,
        bytes32 salt,
        string calldata _tokenName,
        string calldata _tokenSymbol
    ) external returns (NFTWithSVG) {
        NFTWithSVG clone = NFTWithSVG(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(_tokenName, _tokenSymbol);
        clone.transferOwnership(msg.sender);

        emit CreateClone("NFTWithSVG", template, salt, address(clone));
        return clone;
    }

    function createFundingMultitoken(
        address template,
        bytes32 salt,
        string calldata _uri,
        address[] calldata _dividendTokens,
        address _wrapperTemplate,
        bool _deployWrapperOnMint,
        string memory _baseName,
        string memory _baseSymbol,
        uint8 _decimals
    ) external returns (FundingMultitoken) {
        FundingMultitoken clone =
            FundingMultitoken(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            msg.sender,
            _uri,
            _dividendTokens,
            _wrapperTemplate,
            _deployWrapperOnMint,
            _baseName,
            _baseSymbol,
            _decimals
        );

        emit CreateClone("FundingMultitoken", template, salt, address(clone));
        return clone;
    }

    function createZeroCouponBond(
        address template,
        bytes32 salt,
        address _pool,
        address _vesting,
        uint64 _maturationTimetstamp,
        uint256 _initialDepositAmount,
        string calldata _tokenName,
        string calldata _tokenSymbol
    ) external returns (ZeroCouponBond) {
        ZeroCouponBond clone =
            ZeroCouponBond(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            msg.sender,
            _pool,
            _vesting,
            _maturationTimetstamp,
            _initialDepositAmount,
            _tokenName,
            _tokenSymbol
        );

        emit CreateClone("ZeroCouponBond", template, salt, address(clone));
        return clone;
    }

    function createEMAOracle(
        address template,
        bytes32 salt,
        uint256 _emaInitial,
        uint256 _updateInterval,
        uint256 _smoothingFactor,
        uint256 _averageWindowInIntervals,
        address _moneyMarket
    ) external returns (EMAOracle) {
        EMAOracle clone = EMAOracle(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            _emaInitial,
            _updateInterval,
            _smoothingFactor,
            _averageWindowInIntervals,
            _moneyMarket
        );

        emit CreateClone("EMAOracle", template, salt, address(clone));
        return clone;
    }

    function createAaveMarket(
        address template,
        bytes32 salt,
        address _provider,
        address _aToken,
        address _aaveMining,
        address _rewards,
        address _rescuer,
        address _stablecoin
    ) external returns (AaveMarket) {
        AaveMarket clone = AaveMarket(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            _provider,
            _aToken,
            _aaveMining,
            _rewards,
            _rescuer,
            _stablecoin
        );
        clone.transferOwnership(msg.sender);

        emit CreateClone("AaveMarket", template, salt, address(clone));
        return clone;
    }

    function createBProtocolMarket(
        address template,
        bytes32 salt,
        address _bToken,
        address _bComptroller,
        address _rewards,
        address _rescuer,
        address _stablecoin
    ) external returns (BProtocolMarket) {
        BProtocolMarket clone =
            BProtocolMarket(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            _bToken,
            _bComptroller,
            _rewards,
            _rescuer,
            _stablecoin
        );
        clone.transferOwnership(msg.sender);

        emit CreateClone("BProtocolMarket", template, salt, address(clone));
        return clone;
    }

    function createCompoundERC20Market(
        address template,
        bytes32 salt,
        address _cToken,
        address _comptroller,
        address _rewards,
        address _rescuer,
        address _stablecoin
    ) external returns (CompoundERC20Market) {
        CompoundERC20Market clone =
            CompoundERC20Market(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            _cToken,
            _comptroller,
            _rewards,
            _rescuer,
            _stablecoin
        );
        clone.transferOwnership(msg.sender);

        emit CreateClone("CompoundERC20Market", template, salt, address(clone));
        return clone;
    }

    function createCreamERC20Market(
        address template,
        bytes32 salt,
        address _cToken,
        address _rescuer,
        address _stablecoin
    ) external returns (CreamERC20Market) {
        CreamERC20Market clone =
            CreamERC20Market(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(_cToken, _rescuer, _stablecoin);
        clone.transferOwnership(msg.sender);

        emit CreateClone("CreamERC20Market", template, salt, address(clone));
        return clone;
    }

    function createHarvestMarket(
        address template,
        bytes32 salt,
        address _vault,
        address _rewards,
        address _stakingPool,
        address _rescuer,
        address _stablecoin
    ) external returns (HarvestMarket) {
        HarvestMarket clone = HarvestMarket(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(_vault, _rewards, _stakingPool, _rescuer, _stablecoin);
        clone.transferOwnership(msg.sender);

        emit CreateClone("HarvestMarket", template, salt, address(clone));
        return clone;
    }

    function createYVaultMarket(
        address template,
        bytes32 salt,
        address _vault,
        address _rescuer,
        address _stablecoin
    ) external returns (YVaultMarket) {
        YVaultMarket clone = YVaultMarket(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(_vault, _rescuer, _stablecoin);
        clone.transferOwnership(msg.sender);

        emit CreateClone("YVaultMarket", template, salt, address(clone));
        return clone;
    }

    function createDInterest(
        address template,
        bytes32 salt,
        uint64 _MaxDepositPeriod,
        uint256 _MinDepositAmount,
        address _feeModel,
        address _interestModel,
        address _interestOracle,
        address _depositNFT,
        address _fundingMultitoken,
        address _mphMinter
    ) external returns (DInterest) {
        DInterest clone = DInterest(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            _MaxDepositPeriod,
            _MinDepositAmount,
            _feeModel,
            _interestModel,
            _interestOracle,
            _depositNFT,
            _fundingMultitoken,
            _mphMinter
        );
        clone.transferOwnership(msg.sender, true, false);

        emit CreateClone("DInterest", template, salt, address(clone));
        return clone;
    }

    struct DInterestWithDepositFeeParams {
        uint64 _MaxDepositPeriod;
        uint256 _MinDepositAmount;
        uint256 _DepositFee;
        address _feeModel;
        address _interestModel;
        address _interestOracle;
        address _depositNFT;
        address _fundingMultitoken;
        address _mphMinter;
    }

    function createDInterestWithDepositFee(
        address template,
        bytes32 salt,
        DInterestWithDepositFeeParams calldata params
    ) external returns (DInterestWithDepositFee) {
        DInterestWithDepositFee clone =
            DInterestWithDepositFee(template.cloneDeterministic(salt));

        // initialize
        clone.initialize(
            params._MaxDepositPeriod,
            params._MinDepositAmount,
            params._DepositFee,
            params._feeModel,
            params._interestModel,
            params._interestOracle,
            params._depositNFT,
            params._fundingMultitoken,
            params._mphMinter
        );
        clone.transferOwnership(msg.sender, true, false);

        emit CreateClone(
            "DInterestWithDepositFee",
            template,
            salt,
            address(clone)
        );
        return clone;
    }

    function predictAddress(address template, bytes32 salt)
        external
        view
        returns (address)
    {
        return template.predictDeterministicAddress(salt);
    }
}

File 2 of 82 : AccessControlUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControlUpgradeable {
    function hasRole(bytes32 role, address account) external view returns (bool);
    function getRoleAdmin(bytes32 role) external view returns (bytes32);
    function grantRole(bytes32 role, address account) external;
    function revokeRole(bytes32 role, address account) external;
    function renounceRole(bytes32 role, address account) external;
}

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
    function __AccessControl_init() internal initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
    }

    function __AccessControl_init_unchained() internal initializer {
    }
    struct RoleData {
        mapping (address => bool) members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

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

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{20}) is missing role (0x[0-9a-f]{32})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if(!hasRole(role, account)) {
            revert(string(abi.encodePacked(
                "AccessControl: account ",
                StringsUpgradeable.toHexString(uint160(account), 20),
                " is missing role ",
                StringsUpgradeable.toHexString(uint256(role), 32)
            )));
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
    uint256[49] private __gap;
}

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

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/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 82 : ClonesUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

File 5 of 82 : Initializable.sol
// SPDX-License-Identifier: MIT

// solhint-disable-next-line compiler-version
pragma solidity ^0.8.0;

/**
 * @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 {ERC1967Proxy-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 || !_initialized, "Initializable: contract is already initialized");

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

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 6 of 82 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/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 7 of 82 : IERC1155ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

/**
 * @dev _Available since v3.1._
 */
interface IERC1155ReceiverUpgradeable is IERC165Upgradeable {

    /**
        @dev Handles the receipt of a single ERC1155 token type. This function is
        called at the end of a `safeTransferFrom` after the balance has been updated.
        To accept the transfer, this must return
        `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
        (i.e. 0xf23a6e61, or its own function selector).
        @param operator The address which initiated the transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param id The ID of the token being transferred
        @param value The amount of tokens being transferred
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
    */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    )
        external
        returns(bytes4);

    /**
        @dev Handles the receipt of a multiple ERC1155 token types. This function
        is called at the end of a `safeBatchTransferFrom` after the balances have
        been updated. To accept the transfer(s), this must return
        `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
        (i.e. 0xbc197c81, or its own function selector).
        @param operator The address which initiated the batch transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param ids An array containing ids of each token being transferred (order and length must match values array)
        @param values An array containing amounts of each token being transferred (order and length must match ids array)
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
    */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    )
        external
        returns(bytes4);
}

File 8 of 82 : IERC1155Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

File 9 of 82 : IERC1155MetadataURIUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC1155Upgradeable.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURIUpgradeable is IERC1155Upgradeable {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 10 of 82 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The defaut value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
        __Context_init_unchained();
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * 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) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        _approve(sender, _msgSender(), currentAllowance - amount);

        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 {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + 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 {IERC20-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 virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        _approve(_msgSender(), spender, currentAllowance - subtractedValue);

        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 virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        _balances[sender] = senderBalance - amount;
        _balances[recipient] += 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 virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += 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 virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        _balances[account] = accountBalance - amount;
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This 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 virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    uint256[45] private __gap;
}

File 11 of 82 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `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 12 of 82 : ERC20BurnableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
    function __ERC20Burnable_init() internal initializer {
        __Context_init_unchained();
        __ERC20Burnable_init_unchained();
    }

    function __ERC20Burnable_init_unchained() internal initializer {
    }
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        uint256 currentAllowance = allowance(account, _msgSender());
        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
        _approve(account, _msgSender(), currentAllowance - amount);
        _burn(account, amount);
    }
    uint256[50] private __gap;
}

File 13 of 82 : IERC20MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

File 14 of 82 : ERC721Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC721Upgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "./extensions/IERC721MetadataUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
    using AddressUpgradeable for address;
    using StringsUpgradeable for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping (uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping (address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    function __ERC721_init(string memory name_, string memory symbol_) internal initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __ERC721_init_unchained(name_, symbol_);
    }

    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal initializer {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, tokenId.toString()))
            : '';
    }

    /**
     * @dev Base URI for computing {tokenURI}. Empty by default, can be overriden
     * in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721Upgradeable.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721Upgradeable.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721Upgradeable.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
        private returns (bool)
    {
        if (to.isContract()) {
            try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721ReceiverUpgradeable(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
    uint256[44] private __gap;
}

File 15 of 82 : IERC721ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721ReceiverUpgradeable {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

File 16 of 82 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

File 17 of 82 : ERC721URIStorageUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../ERC721Upgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev ERC721 token with storage based token URI management.
 */
abstract contract ERC721URIStorageUpgradeable is Initializable, ERC721Upgradeable {
    function __ERC721URIStorage_init() internal initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __ERC721URIStorage_init_unchained();
    }

    function __ERC721URIStorage_init_unchained() internal initializer {
    }
    using StringsUpgradeable for uint256;

    // Optional mapping for token URIs
    mapping (uint256 => string) private _tokenURIs;

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token");

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }

        return super.tokenURI(tokenId);
    }

    /**
     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual override {
        super._burn(tokenId);

        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }
    }
    uint256[49] private __gap;
}

File 18 of 82 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721MetadataUpgradeable is IERC721Upgradeable {

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

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 19 of 82 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^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 20 of 82 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/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 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) {
        return msg.sender;
    }

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

File 21 of 82 : MulticallUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
abstract contract MulticallUpgradeable is Initializable {
    function __Multicall_init() internal initializer {
        __Multicall_init_unchained();
    }

    function __Multicall_init_unchained() internal initializer {
    }
    /**
    * @dev Receives and executes a batch of function calls on this contract.
    */
    function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint i = 0; i < data.length; i++) {
            results[i] = _functionDelegateCall(address(this), data[i]);
        }
        return results;
    }

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, "Address: low-level delegate call failed");
    }

    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);
            }
        }
    }
    uint256[50] private __gap;
}

File 22 of 82 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant alphabet = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = alphabet[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

}

File 23 of 82 : ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

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

    function __ERC165_init_unchained() internal initializer {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }
    uint256[50] private __gap;
}

File 24 of 82 : IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

File 25 of 82 : MathUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    /**
     * @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 26 of 82 : SafeCastUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such 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.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCastUpgradeable {
    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        require(value < 2**255, "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 27 of 82 : Clones.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

File 28 of 82 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The defaut value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * 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) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        _approve(sender, _msgSender(), currentAllowance - amount);

        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 {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + 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 {IERC20-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 virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        _approve(_msgSender(), spender, currentAllowance - subtractedValue);

        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 virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        _balances[sender] = senderBalance - amount;
        _balances[recipient] += 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 virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += 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 virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        _balances[account] = accountBalance - amount;
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This 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 virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

File 29 of 82 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `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 30 of 82 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

File 31 of 82 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(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 32 of 82 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

File 33 of 82 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant alphabet = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = alphabet[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

}

File 34 of 82 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            // solhint-disable-next-line no-inline-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
        } else if (signature.length == 64) {
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            // solhint-disable-next-line no-inline-assembly
            assembly {
                let vs := mload(add(signature, 0x40))
                r := mload(add(signature, 0x20))
                s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
                v := add(shr(255, vs), 27)
            }
        } else {
            revert("ECDSA: invalid signature length");
        }

        return recover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 35 of 82 : base64.sol
// SPDX-License-Identifier: MIT

/// @title Base64
/// @author Brecht Devos - <[email protected]>
/// @notice Provides a function for encoding some bytes in base64
library Base64 {
    string internal constant TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

    function encode(bytes memory data) internal pure returns (string memory) {
        if (data.length == 0) return '';
        
        // load the table into memory
        string memory table = TABLE;

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((data.length + 2) / 3);

        // add some extra buffer at the end required for the writing
        string memory result = new string(encodedLen + 32);

        assembly {
            // set the actual output length
            mstore(result, encodedLen)
            
            // prepare the lookup table
            let tablePtr := add(table, 1)
            
            // input ptr
            let dataPtr := data
            let endPtr := add(dataPtr, mload(data))
            
            // result ptr, jump over length
            let resultPtr := add(result, 32)
            
            // run over the input, 3 bytes at a time
            for {} lt(dataPtr, endPtr) {}
            {
               dataPtr := add(dataPtr, 3)
               
               // read 3 bytes
               let input := mload(dataPtr)
               
               // write 4 characters
               mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
               resultPtr := add(resultPtr, 1)
               mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
               resultPtr := add(resultPtr, 1)
               mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr( 6, input), 0x3F)))))
               resultPtr := add(resultPtr, 1)
               mstore(resultPtr, shl(248, mload(add(tablePtr, and(        input,  0x3F)))))
               resultPtr := add(resultPtr, 1)
            }
            
            // padding with '='
            switch mod(mload(data), 3)
            case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
            case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
        }
        
        return result;
    }
}

File 36 of 82 : DInterest.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "./libs/SafeERC20.sol";
import {
    ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {BoringOwnable} from "./libs/BoringOwnable.sol";
import {
    MulticallUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol";
import {MoneyMarket} from "./moneymarkets/MoneyMarket.sol";
import {IFeeModel} from "./models/fee/IFeeModel.sol";
import {IInterestModel} from "./models/interest/IInterestModel.sol";
import {NFT} from "./tokens/NFT.sol";
import {FundingMultitoken} from "./tokens/FundingMultitoken.sol";
import {MPHMinter} from "./rewards/MPHMinter.sol";
import {IInterestOracle} from "./models/interest-oracle/IInterestOracle.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {Rescuable} from "./libs/Rescuable.sol";
import {console} from "hardhat/console.sol";

/**
    @title DeLorean Interest -- It's coming back from the future!
    @author Zefram Lou
    @notice The main pool contract for fixed-rate deposits
    @dev The contract to interact with for most actions
 */
contract DInterest is
    ReentrancyGuardUpgradeable,
    BoringOwnable,
    Rescuable,
    MulticallUpgradeable
{
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;
    using PRBMathUD60x18 for uint256;

    // Constants
    uint256 internal constant PRECISION = 10**18;
    /**
        @dev used for sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex
     */
    uint256 internal constant EXTRA_PRECISION = 10**27;
    /**
        @dev used for funding.principalPerToken
     */
    uint256 internal constant ULTRA_PRECISION = 2**128;
    /**
        @dev Specifies the threshold for paying out funder interests
     */
    uint256 internal constant FUNDER_PAYOUT_THRESHOLD_DIVISOR = 10**10;

    // User deposit data
    // Each deposit has an ID used in the depositNFT, which is equal to its index in `deposits` plus 1
    struct Deposit {
        uint256 virtualTokenTotalSupply; // depositAmount + interestAmount, behaves like a zero coupon bond
        uint256 interestRate; // interestAmount = interestRate * depositAmount
        uint256 feeRate; // feeAmount = feeRate * depositAmount
        uint256 averageRecordedIncomeIndex; // Average income index at time of deposit, used for computing deposit surplus
        uint64 maturationTimestamp; // Unix timestamp after which the deposit may be withdrawn, in seconds
        uint64 fundingID; // The ID of the associated Funding struct. 0 if not funded.
    }
    Deposit[] internal deposits;

    // Funding data
    // Each funding has an ID used in the fundingMultitoken, which is equal to its index in `fundingList` plus 1
    struct Funding {
        uint64 depositID; // The ID of the associated Deposit struct.
        uint64 lastInterestPayoutTimestamp; // Unix timestamp of the most recent interest payout, in seconds
        uint256 recordedMoneyMarketIncomeIndex; // the income index at the last update (creation or withdrawal)
        uint256 principalPerToken; // The amount of stablecoins that's earning interest for you per funding token you own. Scaled to 18 decimals regardless of stablecoin decimals.
    }
    Funding[] internal fundingList;
    // the sum of (recordedFundedPrincipalAmount / recordedMoneyMarketIncomeIndex) of all fundings
    uint256 public sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex;

    // Params
    /**
        @dev Maximum deposit period, in seconds
     */
    uint64 public MaxDepositPeriod;
    /**
        @dev Minimum deposit amount, in stablecoins
     */
    uint256 public MinDepositAmount;

    // Global variables
    uint256 public totalDeposit;
    uint256 public totalInterestOwed;
    uint256 public totalFeeOwed;
    uint256 public totalFundedPrincipalAmount;

    // External smart contracts
    IFeeModel public feeModel;
    IInterestModel public interestModel;
    IInterestOracle public interestOracle;
    NFT public depositNFT;
    FundingMultitoken public fundingMultitoken;
    MPHMinter public mphMinter;

    // Extra params
    /**
        @dev The maximum amount of deposit in the pool. Set to 0 to disable the cap.
     */
    uint256 public GlobalDepositCap;

    // Events
    event EDeposit(
        address indexed sender,
        uint256 indexed depositID,
        uint256 depositAmount,
        uint256 interestAmount,
        uint256 feeAmount,
        uint64 maturationTimestamp
    );
    event ETopupDeposit(
        address indexed sender,
        uint64 indexed depositID,
        uint256 depositAmount,
        uint256 interestAmount,
        uint256 feeAmount
    );
    event ERolloverDeposit(
        address indexed sender,
        uint64 indexed depositID,
        uint64 indexed newDepositID
    );
    event EWithdraw(
        address indexed sender,
        uint256 indexed depositID,
        bool indexed early,
        uint256 virtualTokenAmount,
        uint256 feeAmount
    );
    event EFund(
        address indexed sender,
        uint64 indexed fundingID,
        uint256 fundAmount,
        uint256 tokenAmount
    );
    event EPayFundingInterest(
        uint256 indexed fundingID,
        uint256 interestAmount,
        uint256 refundAmount
    );
    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
    event ESetParamUint(
        address indexed sender,
        string indexed paramName,
        uint256 newValue
    );

    function __DInterest_init(
        uint64 _MaxDepositPeriod,
        uint256 _MinDepositAmount,
        address _feeModel,
        address _interestModel,
        address _interestOracle,
        address _depositNFT,
        address _fundingMultitoken,
        address _mphMinter
    ) internal initializer {
        __ReentrancyGuard_init();
        __Ownable_init();

        feeModel = IFeeModel(_feeModel);
        interestModel = IInterestModel(_interestModel);
        interestOracle = IInterestOracle(_interestOracle);
        depositNFT = NFT(_depositNFT);
        fundingMultitoken = FundingMultitoken(_fundingMultitoken);
        mphMinter = MPHMinter(_mphMinter);
        MaxDepositPeriod = _MaxDepositPeriod;
        MinDepositAmount = _MinDepositAmount;
    }

    /**
        @param _MaxDepositPeriod The maximum deposit period, in seconds
        @param _MinDepositAmount The minimum deposit amount, in stablecoins
        @param _feeModel Address of the FeeModel contract that determines how fees are charged
        @param _interestModel Address of the InterestModel contract that determines how much interest to offer
        @param _interestOracle Address of the InterestOracle contract that provides the average interest rate
        @param _depositNFT Address of the NFT representing ownership of deposits (owner must be set to this DInterest contract)
        @param _fundingMultitoken Address of the ERC1155 multitoken representing ownership of fundings (this DInterest contract must have the minter-burner role)
        @param _mphMinter Address of the contract for handling minting MPH to users
     */
    function initialize(
        uint64 _MaxDepositPeriod,
        uint256 _MinDepositAmount,
        address _feeModel,
        address _interestModel,
        address _interestOracle,
        address _depositNFT,
        address _fundingMultitoken,
        address _mphMinter
    ) external virtual initializer {
        __DInterest_init(
            _MaxDepositPeriod,
            _MinDepositAmount,
            _feeModel,
            _interestModel,
            _interestOracle,
            _depositNFT,
            _fundingMultitoken,
            _mphMinter
        );
    }

    /**
        Public action functions
     */

    /**
        @notice Create a deposit using `depositAmount` stablecoin that matures at timestamp `maturationTimestamp`.
        @dev The ERC-721 NFT representing deposit ownership is given to msg.sender
        @param depositAmount The amount of deposit, in stablecoin
        @param maturationTimestamp The Unix timestamp of maturation, in seconds
        @return depositID The ID of the created deposit
        @return interestAmount The amount of fixed-rate interest
     */
    function deposit(uint256 depositAmount, uint64 maturationTimestamp)
        external
        nonReentrant
        returns (uint64 depositID, uint256 interestAmount)
    {
        return
            _deposit(
                msg.sender,
                depositAmount,
                maturationTimestamp,
                false,
                0,
                ""
            );
    }

    /**
        @notice Create a deposit using `depositAmount` stablecoin that matures at timestamp `maturationTimestamp`.
        @dev The ERC-721 NFT representing deposit ownership is given to msg.sender
        @param depositAmount The amount of deposit, in stablecoin
        @param maturationTimestamp The Unix timestamp of maturation, in seconds
        @param minimumInterestAmount If the interest amount is less than this, revert
        @param uri The metadata URI for the minted NFT
        @return depositID The ID of the created deposit
        @return interestAmount The amount of fixed-rate interest
     */
    function deposit(
        uint256 depositAmount,
        uint64 maturationTimestamp,
        uint256 minimumInterestAmount,
        string calldata uri
    ) external nonReentrant returns (uint64 depositID, uint256 interestAmount) {
        return
            _deposit(
                msg.sender,
                depositAmount,
                maturationTimestamp,
                false,
                minimumInterestAmount,
                uri
            );
    }

    /**
        @notice Add `depositAmount` stablecoin to the existing deposit with ID `depositID`.
        @dev The interest rate for the topped up funds will be the current oracle rate.
        @param depositID The deposit to top up
        @param depositAmount The amount to top up, in stablecoin
        @return interestAmount The amount of interest that will be earned by the topped up funds at maturation
     */
    function topupDeposit(uint64 depositID, uint256 depositAmount)
        external
        nonReentrant
        returns (uint256 interestAmount)
    {
        return _topupDeposit(msg.sender, depositID, depositAmount, 0);
    }

    /**
        @notice Add `depositAmount` stablecoin to the existing deposit with ID `depositID`.
        @dev The interest rate for the topped up funds will be the current oracle rate.
        @param depositID The deposit to top up
        @param depositAmount The amount to top up, in stablecoin
        @param minimumInterestAmount If the interest amount is less than this, revert
        @return interestAmount The amount of interest that will be earned by the topped up funds at maturation
     */
    function topupDeposit(
        uint64 depositID,
        uint256 depositAmount,
        uint256 minimumInterestAmount
    ) external nonReentrant returns (uint256 interestAmount) {
        return
            _topupDeposit(
                msg.sender,
                depositID,
                depositAmount,
                minimumInterestAmount
            );
    }

    /**
        @notice Withdraw all funds from deposit with ID `depositID` and use them
                to create a new deposit that matures at time `maturationTimestamp`
        @param depositID The deposit to roll over
        @param maturationTimestamp The Unix timestamp of the new deposit, in seconds
        @return newDepositID The ID of the new deposit
     */
    function rolloverDeposit(uint64 depositID, uint64 maturationTimestamp)
        external
        nonReentrant
        returns (uint256 newDepositID, uint256 interestAmount)
    {
        return
            _rolloverDeposit(msg.sender, depositID, maturationTimestamp, 0, "");
    }

    /**
        @notice Withdraw all funds from deposit with ID `depositID` and use them
                to create a new deposit that matures at time `maturationTimestamp`
        @param depositID The deposit to roll over
        @param maturationTimestamp The Unix timestamp of the new deposit, in seconds
        @param minimumInterestAmount If the interest amount is less than this, revert
        @param uri The metadata URI of the NFT
        @return newDepositID The ID of the new deposit
     */
    function rolloverDeposit(
        uint64 depositID,
        uint64 maturationTimestamp,
        uint256 minimumInterestAmount,
        string calldata uri
    )
        external
        nonReentrant
        returns (uint256 newDepositID, uint256 interestAmount)
    {
        return
            _rolloverDeposit(
                msg.sender,
                depositID,
                maturationTimestamp,
                minimumInterestAmount,
                uri
            );
    }

    /**
        @notice Withdraws funds from the deposit with ID `depositID`.
        @dev Virtual tokens behave like zero coupon bonds, after maturation withdrawing 1 virtual token
             yields 1 stablecoin. The total supply is given by deposit.virtualTokenTotalSupply
        @param depositID the deposit to withdraw from
        @param virtualTokenAmount the amount of virtual tokens to withdraw
        @param early True if intend to withdraw before maturation, false otherwise
        @return withdrawnStablecoinAmount the amount of stablecoins withdrawn
     */
    function withdraw(
        uint64 depositID,
        uint256 virtualTokenAmount,
        bool early
    ) external nonReentrant returns (uint256 withdrawnStablecoinAmount) {
        return
            _withdraw(msg.sender, depositID, virtualTokenAmount, early, false);
    }

    /**
        @notice Funds the fixed-rate interest of the deposit with ID `depositID`.
                In exchange, the funder receives the future floating-rate interest
                generated by the portion of the deposit whose interest was funded.
        @dev The sender receives ERC-1155 multitokens (fundingMultitoken) representing
             their funding position.
        @param depositID The deposit whose fixed-rate interest will be funded
        @param fundAmount The amount of stablecoins to pay for the fundingMultitokens.
                          If it exceeds the upper bound, it will be set to
                          the bound value instead. (See {_fund} implementation)
        @return fundingID The ID of the fundingMultitoken the sender received
        @return fundingMultitokensMinted The amount of fundingMultitokens minted to the sender
        @return actualFundAmount The amount of stablecoins paid by the sender
        @return principalFunded The amount of principal the minted fundingMultitokens is earning yield on
     */
    function fund(uint64 depositID, uint256 fundAmount)
        external
        nonReentrant
        returns (
            uint64 fundingID,
            uint256 fundingMultitokensMinted,
            uint256 actualFundAmount,
            uint256 principalFunded
        )
    {
        return _fund(msg.sender, depositID, fundAmount, 0);
    }

    /**
        @notice Funds the fixed-rate interest of the deposit with ID `depositID`.
                In exchange, the funder receives the future floating-rate interest
                generated by the portion of the deposit whose interest was funded.
        @dev The sender receives ERC-1155 multitokens (fundingMultitoken) representing
             their funding position.
        @param depositID The deposit whose fixed-rate interest will be funded
        @param fundAmount The amount of stablecoins to pay for the fundingMultitokens.
                          If it exceeds the upper bound, it will be set to
                          the bound value instead. (See {_fund} implementation)
        @param minPrincipalFunded The minimum amount of principalFunded, below which the tx will revert
        @return fundingID The ID of the fundingMultitoken the sender received
        @return fundingMultitokensMinted The amount of fundingMultitokens minted to the sender
        @return actualFundAmount The amount of stablecoins paid by the sender
        @return principalFunded The amount of principal the minted fundingMultitokens is earning yield on
     */
    function fund(
        uint64 depositID,
        uint256 fundAmount,
        uint256 minPrincipalFunded
    )
        external
        nonReentrant
        returns (
            uint64 fundingID,
            uint256 fundingMultitokensMinted,
            uint256 actualFundAmount,
            uint256 principalFunded
        )
    {
        return _fund(msg.sender, depositID, fundAmount, minPrincipalFunded);
    }

    /**
        @notice Distributes the floating-rate interest accrued by a deposit to the
                floating-rate bond holders.
        @param fundingID The ID of the floating-rate bond
        @return interestAmount The amount of interest distributed, in stablecoins
     */
    function payInterestToFunders(uint64 fundingID)
        external
        nonReentrant
        returns (uint256 interestAmount)
    {
        return _payInterestToFunders(fundingID, moneyMarket().incomeIndex());
    }

    /**
        Public getter functions
     */

    /**
        @notice Computes the amount of fixed-rate interest (before fees) that
                will be given to a deposit of `depositAmount` stablecoins that
                matures in `depositPeriodInSeconds` seconds.
        @param depositAmount The deposit amount, in stablecoins
        @param depositPeriodInSeconds The deposit period, in seconds
        @return interestAmount The amount of fixed-rate interest (before fees)
     */
    function calculateInterestAmount(
        uint256 depositAmount,
        uint256 depositPeriodInSeconds
    ) public virtual returns (uint256 interestAmount) {
        (, uint256 moneyMarketInterestRatePerSecond) =
            interestOracle.updateAndQuery();
        (bool surplusIsNegative, uint256 surplusAmount) = surplus();

        return
            interestModel.calculateInterestAmount(
                depositAmount,
                depositPeriodInSeconds,
                moneyMarketInterestRatePerSecond,
                surplusIsNegative,
                surplusAmount
            );
    }

    /**
        @notice Computes the pool's overall surplus, which is the value of its holdings
                in the `moneyMarket` minus the amount owed to depositors, funders, and
                the fee beneficiary.
        @return isNegative True if the surplus is negative, false otherwise
        @return surplusAmount The absolute value of the surplus, in stablecoins
     */
    function surplus()
        public
        virtual
        returns (bool isNegative, uint256 surplusAmount)
    {
        return _surplus(moneyMarket().incomeIndex());
    }

    /**
        @notice Returns the total number of deposits.
        @return deposits.length
     */
    function depositsLength() external view returns (uint256) {
        return deposits.length;
    }

    /**
        @notice Returns the total number of floating-rate bonds.
        @return fundingList.length
     */
    function fundingListLength() external view returns (uint256) {
        return fundingList.length;
    }

    /**
        @notice Returns the Deposit struct associated with the deposit with ID
                `depositID`.
        @param depositID The ID of the deposit
        @return The deposit struct
     */
    function getDeposit(uint64 depositID)
        external
        view
        returns (Deposit memory)
    {
        return deposits[depositID - 1];
    }

    /**
        @notice Returns the Funding struct associated with the floating-rate bond with ID
                `fundingID`.
        @param fundingID The ID of the floating-rate bond
        @return The Funding struct
     */
    function getFunding(uint64 fundingID)
        external
        view
        returns (Funding memory)
    {
        return fundingList[fundingID - 1];
    }

    /**
        @notice Returns the moneyMarket contract
        @return The moneyMarket
     */
    function moneyMarket() public view returns (MoneyMarket) {
        return interestOracle.moneyMarket();
    }

    /**
        @notice Returns the stablecoin ERC20 token contract
        @return The stablecoin
     */
    function stablecoin() public view returns (ERC20) {
        return moneyMarket().stablecoin();
    }

    /**
        Internal action functions
     */

    /**
        @dev See {deposit}
     */
    function _deposit(
        address sender,
        uint256 depositAmount,
        uint64 maturationTimestamp,
        bool rollover,
        uint256 minimumInterestAmount,
        string memory uri
    ) internal virtual returns (uint64 depositID, uint256 interestAmount) {
        (depositID, interestAmount) = _depositRecordData(
            sender,
            depositAmount,
            maturationTimestamp,
            minimumInterestAmount,
            uri
        );
        _depositTransferFunds(sender, depositAmount, rollover);
    }

    function _depositRecordData(
        address sender,
        uint256 depositAmount,
        uint64 maturationTimestamp,
        uint256 minimumInterestAmount,
        string memory uri
    ) internal virtual returns (uint64 depositID, uint256 interestAmount) {
        // Ensure input is valid
        require(depositAmount >= MinDepositAmount, "BAD_AMOUNT");
        uint256 depositPeriod = maturationTimestamp - block.timestamp;
        require(depositPeriod <= MaxDepositPeriod, "BAD_TIME");

        // Calculate interest
        interestAmount = calculateInterestAmount(depositAmount, depositPeriod);
        require(
            interestAmount > 0 && interestAmount >= minimumInterestAmount,
            "BAD_INTEREST"
        );

        // Calculate fee
        uint256 feeAmount =
            feeModel.getInterestFeeAmount(address(this), interestAmount);
        interestAmount -= feeAmount;

        // Record deposit data
        deposits.push(
            Deposit({
                virtualTokenTotalSupply: depositAmount + interestAmount,
                interestRate: interestAmount.div(depositAmount),
                feeRate: feeAmount.div(depositAmount),
                maturationTimestamp: maturationTimestamp,
                fundingID: 0,
                averageRecordedIncomeIndex: interestOracle
                    .moneyMarket()
                    .incomeIndex()
            })
        );
        require(deposits.length <= type(uint64).max, "OVERFLOW");
        depositID = uint64(deposits.length);

        // Update global values
        totalDeposit += depositAmount;
        {
            uint256 depositCap = GlobalDepositCap;
            require(depositCap == 0 || totalDeposit <= depositCap, "CAP");
        }
        totalInterestOwed += interestAmount;
        totalFeeOwed += feeAmount;

        // Mint depositNFT
        if (bytes(uri).length == 0) {
            depositNFT.mint(sender, depositID);
        } else {
            depositNFT.mint(sender, depositID, uri);
        }

        // Emit event
        emit EDeposit(
            sender,
            depositID,
            depositAmount,
            interestAmount,
            feeAmount,
            maturationTimestamp
        );

        // Vest MPH to sender
        mphMinter.createVestForDeposit(sender, depositID);
    }

    function _depositTransferFunds(
        address sender,
        uint256 depositAmount,
        bool rollover
    ) internal virtual {
        // Only transfer funds from sender if it's not a rollover
        // because if it is the funds are already in the contract
        if (!rollover) {
            ERC20 _stablecoin = stablecoin();

            // Transfer `depositAmount` stablecoin to DInterest
            _stablecoin.safeTransferFrom(sender, address(this), depositAmount);

            // Lend `depositAmount` stablecoin to money market
            MoneyMarket _moneyMarket = moneyMarket();
            _stablecoin.safeIncreaseAllowance(
                address(_moneyMarket),
                depositAmount
            );
            _moneyMarket.deposit(depositAmount);
        }
    }

    /**
        @dev See {topupDeposit}
     */
    function _topupDeposit(
        address sender,
        uint64 depositID,
        uint256 depositAmount,
        uint256 minimumInterestAmount
    ) internal virtual returns (uint256 interestAmount) {
        interestAmount = _topupDepositRecordData(
            sender,
            depositID,
            depositAmount,
            minimumInterestAmount
        );
        _topupDepositTransferFunds(sender, depositAmount);
    }

    function _topupDepositRecordData(
        address sender,
        uint64 depositID,
        uint256 depositAmount,
        uint256 minimumInterestAmount
    ) internal virtual returns (uint256 interestAmount) {
        Deposit storage depositEntry = _getDeposit(depositID);
        require(depositNFT.ownerOf(depositID) == sender, "NOT_OWNER");

        // underflow check prevents topups after maturation
        uint256 depositPeriod =
            depositEntry.maturationTimestamp - block.timestamp;

        // Calculate interest
        interestAmount = calculateInterestAmount(depositAmount, depositPeriod);
        require(
            interestAmount > 0 && interestAmount >= minimumInterestAmount,
            "BAD_INTEREST"
        );

        // Calculate fee
        uint256 feeAmount =
            feeModel.getInterestFeeAmount(address(this), interestAmount);
        interestAmount -= feeAmount;

        // Update deposit struct
        uint256 interestRate = depositEntry.interestRate;
        uint256 currentDepositAmount =
            depositEntry.virtualTokenTotalSupply.div(interestRate + PRECISION);
        depositEntry.virtualTokenTotalSupply += depositAmount + interestAmount;
        depositEntry.interestRate =
            (PRECISION * interestAmount + currentDepositAmount * interestRate) /
            (depositAmount + currentDepositAmount);
        depositEntry.feeRate =
            (PRECISION *
                feeAmount +
                currentDepositAmount *
                depositEntry.feeRate) /
            (depositAmount + currentDepositAmount);
        uint256 sumOfRecordedDepositAmountDivRecordedIncomeIndex =
            (currentDepositAmount * EXTRA_PRECISION) /
                depositEntry.averageRecordedIncomeIndex +
                (depositAmount * EXTRA_PRECISION) /
                moneyMarket().incomeIndex();
        depositEntry.averageRecordedIncomeIndex =
            ((depositAmount + currentDepositAmount) * EXTRA_PRECISION) /
            sumOfRecordedDepositAmountDivRecordedIncomeIndex;

        // Update global values
        totalDeposit += depositAmount;
        {
            uint256 depositCap = GlobalDepositCap;
            require(depositCap == 0 || totalDeposit <= depositCap, "CAP");
        }
        totalInterestOwed += interestAmount;
        totalFeeOwed += feeAmount;

        // Emit event
        emit ETopupDeposit(
            sender,
            depositID,
            depositAmount,
            interestAmount,
            feeAmount
        );

        // Update vest
        mphMinter.updateVestForDeposit(
            depositID,
            currentDepositAmount,
            depositAmount
        );
    }

    function _topupDepositTransferFunds(address sender, uint256 depositAmount)
        internal
        virtual
    {
        ERC20 _stablecoin = stablecoin();

        // Transfer `depositAmount` stablecoin to DInterest
        _stablecoin.safeTransferFrom(sender, address(this), depositAmount);

        // Lend `depositAmount` stablecoin to money market
        MoneyMarket _moneyMarket = moneyMarket();
        _stablecoin.safeIncreaseAllowance(address(_moneyMarket), depositAmount);
        _moneyMarket.deposit(depositAmount);
    }

    /**
        @dev See {rolloverDeposit}
     */
    function _rolloverDeposit(
        address sender,
        uint64 depositID,
        uint64 maturationTimestamp,
        uint256 minimumInterestAmount,
        string memory uri
    ) internal virtual returns (uint64 newDepositID, uint256 interestAmount) {
        // withdraw from existing deposit
        uint256 withdrawnStablecoinAmount =
            _withdraw(sender, depositID, type(uint256).max, false, true);

        // deposit funds into a new deposit
        (newDepositID, interestAmount) = _deposit(
            sender,
            withdrawnStablecoinAmount,
            maturationTimestamp,
            true,
            minimumInterestAmount,
            uri
        );

        emit ERolloverDeposit(sender, depositID, newDepositID);
    }

    /**
        @dev See {withdraw}
        @param rollover True if being called from {_rolloverDeposit}, false otherwise
     */
    function _withdraw(
        address sender,
        uint64 depositID,
        uint256 virtualTokenAmount,
        bool early,
        bool rollover
    ) internal virtual returns (uint256 withdrawnStablecoinAmount) {
        (
            uint256 withdrawAmount,
            uint256 feeAmount,
            uint256 fundingInterestAmount,
            uint256 refundAmount
        ) = _withdrawRecordData(sender, depositID, virtualTokenAmount, early);
        return
            _withdrawTransferFunds(
                sender,
                _getDeposit(depositID).fundingID,
                withdrawAmount,
                feeAmount,
                fundingInterestAmount,
                refundAmount,
                rollover
            );
    }

    function _withdrawRecordData(
        address sender,
        uint64 depositID,
        uint256 virtualTokenAmount,
        bool early
    )
        internal
        virtual
        returns (
            uint256 withdrawAmount,
            uint256 feeAmount,
            uint256 fundingInterestAmount,
            uint256 refundAmount
        )
    {
        // Verify input
        require(virtualTokenAmount > 0, "BAD_AMOUNT");
        Deposit storage depositEntry = _getDeposit(depositID);
        if (early) {
            require(
                block.timestamp < depositEntry.maturationTimestamp,
                "MATURE"
            );
        } else {
            require(
                block.timestamp >= depositEntry.maturationTimestamp,
                "IMMATURE"
            );
        }
        require(depositNFT.ownerOf(depositID) == sender, "NOT_OWNER");

        // Check if withdrawing all funds
        {
            uint256 virtualTokenTotalSupply =
                depositEntry.virtualTokenTotalSupply;
            if (virtualTokenAmount > virtualTokenTotalSupply) {
                virtualTokenAmount = virtualTokenTotalSupply;
            }
        }

        // Compute token amounts
        uint256 interestRate = depositEntry.interestRate;
        uint256 feeRate = depositEntry.feeRate;
        uint256 depositAmount =
            virtualTokenAmount.div(interestRate + PRECISION);
        {
            uint256 interestAmount =
                early ? 0 : virtualTokenAmount - depositAmount;
            withdrawAmount = depositAmount + interestAmount;
        }
        if (early) {
            // apply fee to withdrawAmount
            uint256 earlyWithdrawFee =
                feeModel.getEarlyWithdrawFeeAmount(
                    address(this),
                    depositID,
                    withdrawAmount
                );
            feeAmount = earlyWithdrawFee;
            withdrawAmount -= earlyWithdrawFee;
        } else {
            feeAmount = depositAmount.mul(feeRate);
        }

        // Update global values
        totalDeposit -= depositAmount;
        totalInterestOwed -= virtualTokenAmount - depositAmount;
        totalFeeOwed -= depositAmount.mul(feeRate);

        // If deposit was funded, compute funding interest payout
        uint64 fundingID = depositEntry.fundingID;
        if (fundingID > 0) {
            Funding storage funding = _getFunding(fundingID);

            // Compute funded deposit amount before withdrawal
            uint256 recordedFundedPrincipalAmount =
                (fundingMultitoken.totalSupply(fundingID) *
                    funding.principalPerToken) / ULTRA_PRECISION;

            // Shrink funding principal per token value
            {
                uint256 totalPrincipal =
                    _depositVirtualTokenToPrincipal(
                        depositID,
                        depositEntry.virtualTokenTotalSupply
                    );
                uint256 totalPrincipalDecrease =
                    virtualTokenAmount + depositAmount.mul(feeRate);
                if (
                    totalPrincipal <=
                    totalPrincipalDecrease + recordedFundedPrincipalAmount
                ) {
                    // Not enough unfunded principal, need to decrease funding principal per token value
                    funding.principalPerToken = (totalPrincipal >=
                        totalPrincipalDecrease)
                        ? (funding.principalPerToken *
                            (totalPrincipal - totalPrincipalDecrease)) /
                            recordedFundedPrincipalAmount
                        : 0;
                }
            }

            // Compute interest payout + refund
            // and update relevant state
            (
                fundingInterestAmount,
                refundAmount
            ) = _computeAndUpdateFundingInterestAfterWithdraw(
                fundingID,
                recordedFundedPrincipalAmount,
                early
            );
        }

        // Update vest
        {
            uint256 depositAmountBeforeWithdrawal =
                _getDeposit(depositID).virtualTokenTotalSupply.div(
                    interestRate + PRECISION
                );
            mphMinter.updateVestForDeposit(
                depositID,
                depositAmountBeforeWithdrawal,
                0
            );
        }

        // Burn `virtualTokenAmount` deposit virtual tokens
        _getDeposit(depositID).virtualTokenTotalSupply -= virtualTokenAmount;

        // Emit event
        emit EWithdraw(sender, depositID, early, virtualTokenAmount, feeAmount);
    }

    function _withdrawTransferFunds(
        address sender,
        uint64 fundingID,
        uint256 withdrawAmount,
        uint256 feeAmount,
        uint256 fundingInterestAmount,
        uint256 refundAmount,
        bool rollover
    ) internal virtual returns (uint256 withdrawnStablecoinAmount) {
        ERC20 _stablecoin = stablecoin();

        // Withdraw funds from money market
        // Withdraws principal together with funding interest to save gas
        if (rollover) {
            // Rollover mode, don't withdraw `withdrawAmount` from moneyMarket

            // We do this because feePlusFundingInterest might
            // be slightly less due to rounding
            uint256 feePlusFundingInterest =
                moneyMarket().withdraw(feeAmount + fundingInterestAmount);
            if (feePlusFundingInterest >= feeAmount + fundingInterestAmount) {
                // enough to pay everything, if there's extra give to feeAmount
                feeAmount = feePlusFundingInterest - fundingInterestAmount;
            } else if (feePlusFundingInterest >= feeAmount) {
                // enough to pay fee, give remainder to fundingInterestAmount
                fundingInterestAmount = feePlusFundingInterest - feeAmount;
            } else {
                // not enough to pay fee, give everything to fee
                feeAmount = feePlusFundingInterest;
                fundingInterestAmount = 0;
            }

            // we're keeping the withdrawal amount in the money market
            withdrawnStablecoinAmount = withdrawAmount;
        } else {
            uint256 actualWithdrawnAmount =
                moneyMarket().withdraw(
                    withdrawAmount + feeAmount + fundingInterestAmount
                );

            // We do this because `actualWithdrawnAmount` might
            // be slightly less due to rounding
            withdrawnStablecoinAmount = withdrawAmount;
            if (
                actualWithdrawnAmount >=
                withdrawAmount + feeAmount + fundingInterestAmount
            ) {
                // enough to pay everything, if there's extra give to feeAmount
                feeAmount =
                    actualWithdrawnAmount -
                    withdrawAmount -
                    fundingInterestAmount;
            } else if (actualWithdrawnAmount >= withdrawAmount + feeAmount) {
                // enough to pay withdrawal + fee + remainder
                // give remainder to funding interest
                fundingInterestAmount =
                    actualWithdrawnAmount -
                    withdrawAmount -
                    feeAmount;
            } else if (actualWithdrawnAmount >= withdrawAmount) {
                // enough to pay withdrawal + remainder
                // give remainder to fee
                feeAmount = actualWithdrawnAmount - withdrawAmount;
                fundingInterestAmount = 0;
            } else {
                // not enough to pay withdrawal
                // give everything to withdrawal
                withdrawnStablecoinAmount = actualWithdrawnAmount;
                feeAmount = 0;
                fundingInterestAmount = 0;
            }

            if (withdrawnStablecoinAmount > 0) {
                _stablecoin.safeTransfer(sender, withdrawnStablecoinAmount);
            }
        }

        // Send `feeAmount` stablecoin to feeModel beneficiary
        if (feeAmount > 0) {
            _stablecoin.safeTransfer(feeModel.beneficiary(), feeAmount);
        }

        // Distribute `fundingInterestAmount` stablecoins to funders
        if (fundingInterestAmount > 0) {
            _stablecoin.safeIncreaseAllowance(
                address(fundingMultitoken),
                fundingInterestAmount
            );
            fundingMultitoken.distributeDividends(
                fundingID,
                address(_stablecoin),
                fundingInterestAmount
            );
            // Mint funder rewards
            if (fundingInterestAmount > refundAmount) {
                _distributeFundingRewards(
                    fundingID,
                    fundingInterestAmount - refundAmount
                );
            }
        }
    }

    /**
        @dev See {fund}
     */
    function _fund(
        address sender,
        uint64 depositID,
        uint256 fundAmount,
        uint256 minPrincipalFunded
    )
        internal
        virtual
        returns (
            uint64 fundingID,
            uint256 fundingMultitokensMinted,
            uint256 actualFundAmount,
            uint256 principalFunded
        )
    {
        (
            fundingID,
            fundingMultitokensMinted,
            actualFundAmount,
            principalFunded
        ) = _fundRecordData(sender, depositID, fundAmount, minPrincipalFunded);
        _fundTransferFunds(sender, actualFundAmount);
    }

    function _fundRecordData(
        address sender,
        uint64 depositID,
        uint256 fundAmount,
        uint256 minPrincipalFunded
    )
        internal
        virtual
        returns (
            uint64 fundingID,
            uint256 fundingMultitokensMinted,
            uint256 actualFundAmount,
            uint256 principalFunded
        )
    {
        Deposit storage depositEntry = _getDeposit(depositID);
        fundingID = depositEntry.fundingID;
        uint256 incomeIndex = moneyMarket().incomeIndex();

        // Create funding struct if one doesn't exist
        {
            uint256 virtualTokenTotalSupply =
                depositEntry.virtualTokenTotalSupply;
            uint256 totalPrincipal =
                _depositVirtualTokenToPrincipal(
                    depositID,
                    virtualTokenTotalSupply
                );
            uint256 depositAmount =
                virtualTokenTotalSupply.div(
                    depositEntry.interestRate + PRECISION
                );
            if (
                fundingID == 0 || _getFunding(fundingID).principalPerToken == 0
            ) {
                // The first funder, create struct
                require(block.timestamp <= type(uint64).max, "OVERFLOW");
                fundingList.push(
                    Funding({
                        depositID: depositID,
                        lastInterestPayoutTimestamp: uint64(block.timestamp),
                        recordedMoneyMarketIncomeIndex: incomeIndex,
                        principalPerToken: ULTRA_PRECISION
                    })
                );
                require(fundingList.length <= type(uint64).max, "OVERFLOW");
                fundingID = uint64(fundingList.length);
                depositEntry.fundingID = fundingID;

                // Bound fundAmount upwards by the fixed rate yield amount
                uint256 bound =
                    calculateInterestAmount(
                        depositAmount,
                        depositEntry.maturationTimestamp - block.timestamp
                    );
                if (fundAmount > bound) {
                    fundAmount = bound;
                }

                principalFunded = (totalPrincipal * fundAmount) / bound;
                fundingMultitokensMinted = principalFunded;
            } else {
                // Not the first funder
                // Trigger interest payment for existing funders
                _payInterestToFunders(fundingID, incomeIndex);

                // Compute amount of principal to fund
                uint256 principalPerToken =
                    _getFunding(fundingID).principalPerToken;
                uint256 unfundedPrincipalAmount =
                    totalPrincipal -
                        (fundingMultitoken.totalSupply(fundingID) *
                            principalPerToken) /
                        ULTRA_PRECISION;

                // Bound fundAmount upwards by the fixed rate yield amount
                uint256 bound =
                    calculateInterestAmount(
                        (depositAmount * unfundedPrincipalAmount) /
                            totalPrincipal,
                        depositEntry.maturationTimestamp - block.timestamp
                    );
                if (fundAmount > bound) {
                    fundAmount = bound;
                }
                principalFunded =
                    (unfundedPrincipalAmount * fundAmount) /
                    bound;
                fundingMultitokensMinted =
                    (principalFunded * ULTRA_PRECISION) /
                    principalPerToken;
            }
        }

        // Check principalFunded is at least minPrincipalFunded
        require(principalFunded >= minPrincipalFunded, "MIN");

        // Mint funding multitoken
        fundingMultitoken.mint(sender, fundingID, fundingMultitokensMinted);

        // Update relevant values
        sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex +=
            (principalFunded * EXTRA_PRECISION) /
            incomeIndex;
        totalFundedPrincipalAmount += principalFunded;

        // Emit event
        emit EFund(sender, fundingID, fundAmount, fundingMultitokensMinted);

        actualFundAmount = fundAmount;
    }

    function _fundTransferFunds(address sender, uint256 fundAmount)
        internal
        virtual
    {
        ERC20 _stablecoin = stablecoin();

        // Transfer `fundAmount` stablecoins from sender
        _stablecoin.safeTransferFrom(sender, address(this), fundAmount);

        // Deposit `fundAmount` stablecoins into moneyMarket
        MoneyMarket _moneyMarket = moneyMarket();
        _stablecoin.safeIncreaseAllowance(address(_moneyMarket), fundAmount);
        _moneyMarket.deposit(fundAmount);
    }

    /**
        @dev See {payInterestToFunders}
        @param currentMoneyMarketIncomeIndex The moneyMarket's current incomeIndex
     */
    function _payInterestToFunders(
        uint64 fundingID,
        uint256 currentMoneyMarketIncomeIndex
    ) internal virtual returns (uint256 interestAmount) {
        Funding storage f = _getFunding(fundingID);
        {
            uint256 recordedMoneyMarketIncomeIndex =
                f.recordedMoneyMarketIncomeIndex;
            uint256 fundingTokenTotalSupply =
                fundingMultitoken.totalSupply(fundingID);
            uint256 recordedFundedPrincipalAmount =
                (fundingTokenTotalSupply * f.principalPerToken) /
                    ULTRA_PRECISION;

            // Update funding values
            sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex =
                sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex +
                (recordedFundedPrincipalAmount * EXTRA_PRECISION) /
                currentMoneyMarketIncomeIndex -
                (recordedFundedPrincipalAmount * EXTRA_PRECISION) /
                recordedMoneyMarketIncomeIndex;
            f.recordedMoneyMarketIncomeIndex = currentMoneyMarketIncomeIndex;

            // Compute interest to funders
            interestAmount =
                (recordedFundedPrincipalAmount *
                    currentMoneyMarketIncomeIndex) /
                recordedMoneyMarketIncomeIndex -
                recordedFundedPrincipalAmount;
        }

        // Distribute interest to funders
        if (interestAmount > 0) {
            ERC20 _stablecoin = stablecoin();
            uint256 stablecoinPrecision = 10**uint256(_stablecoin.decimals());
            if (
                interestAmount >
                stablecoinPrecision / FUNDER_PAYOUT_THRESHOLD_DIVISOR
            ) {
                interestAmount = moneyMarket().withdraw(interestAmount);
                if (interestAmount > 0) {
                    _stablecoin.safeIncreaseAllowance(
                        address(fundingMultitoken),
                        interestAmount
                    );
                    fundingMultitoken.distributeDividends(
                        fundingID,
                        address(_stablecoin),
                        interestAmount
                    );

                    _distributeFundingRewards(fundingID, interestAmount);
                }
            } else {
                // interestAmount below minimum payout threshold, pay nothing
                emit EPayFundingInterest(fundingID, 0, 0);
                return 0;
            }
        }

        emit EPayFundingInterest(fundingID, interestAmount, 0);
    }

    /**
        @dev Mints MPH rewards to the holders of an FRB. If past the deposit maturation,
             only mint proportional to the time from the last distribution to the maturation.
        @param fundingID The ID of the funding
        @param rawInterestAmount The interest being distributed
     */
    function _distributeFundingRewards(
        uint64 fundingID,
        uint256 rawInterestAmount
    ) internal {
        Funding storage f = _getFunding(fundingID);

        // Mint funder rewards
        uint256 maturationTimestamp =
            _getDeposit(f.depositID).maturationTimestamp;
        if (block.timestamp > maturationTimestamp) {
            // past maturation, only mint proportionally to maturation - last payout
            uint256 lastInterestPayoutTimestamp = f.lastInterestPayoutTimestamp;
            if (lastInterestPayoutTimestamp < maturationTimestamp) {
                uint256 effectiveInterestAmount =
                    (rawInterestAmount *
                        (maturationTimestamp - lastInterestPayoutTimestamp)) /
                        (block.timestamp - lastInterestPayoutTimestamp);
                mphMinter.distributeFundingRewards(
                    fundingID,
                    effectiveInterestAmount
                );
            }
        } else {
            // before maturation, mint full amount
            mphMinter.distributeFundingRewards(fundingID, rawInterestAmount);
        }
        // update last payout timestamp
        require(block.timestamp <= type(uint64).max, "OVERFLOW");
        f.lastInterestPayoutTimestamp = uint64(block.timestamp);
    }

    /**
        @dev Used in {_withdraw}. Computes the amount of interest to distribute
             to the deposit's floating-rate bond holders. Also updates the Funding
             struct associated with the floating-rate bond.
        @param fundingID The ID of the floating-rate bond
        @param recordedFundedPrincipalAmount The amount of principal funded before the withdrawal
        @param early True if withdrawing before maturation, false otherwise
        @return fundingInterestAmount The amount of interest to distribute to the floating-rate bond holders, plus the refund amount
        @return refundAmount The amount of refund caused by an early withdraw
     */
    function _computeAndUpdateFundingInterestAfterWithdraw(
        uint64 fundingID,
        uint256 recordedFundedPrincipalAmount,
        bool early
    )
        internal
        virtual
        returns (uint256 fundingInterestAmount, uint256 refundAmount)
    {
        Funding storage f = _getFunding(fundingID);
        uint256 currentFundedPrincipalAmount =
            (fundingMultitoken.totalSupply(fundingID) * f.principalPerToken) /
                ULTRA_PRECISION;

        // Update funding values
        {
            uint256 recordedMoneyMarketIncomeIndex =
                f.recordedMoneyMarketIncomeIndex;
            uint256 currentMoneyMarketIncomeIndex = moneyMarket().incomeIndex();
            uint256 currentFundedPrincipalAmountDivRecordedIncomeIndex =
                (currentFundedPrincipalAmount * EXTRA_PRECISION) /
                    currentMoneyMarketIncomeIndex;
            uint256 recordedFundedPrincipalAmountDivRecordedIncomeIndex =
                (recordedFundedPrincipalAmount * EXTRA_PRECISION) /
                    recordedMoneyMarketIncomeIndex;
            if (
                sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex +
                    currentFundedPrincipalAmountDivRecordedIncomeIndex >=
                recordedFundedPrincipalAmountDivRecordedIncomeIndex
            ) {
                sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex =
                    sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex +
                    currentFundedPrincipalAmountDivRecordedIncomeIndex -
                    recordedFundedPrincipalAmountDivRecordedIncomeIndex;
            } else {
                sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex = 0;
            }

            f.recordedMoneyMarketIncomeIndex = currentMoneyMarketIncomeIndex;
            totalFundedPrincipalAmount -=
                recordedFundedPrincipalAmount -
                currentFundedPrincipalAmount;

            // Compute interest to funders
            fundingInterestAmount =
                (recordedFundedPrincipalAmount *
                    currentMoneyMarketIncomeIndex) /
                recordedMoneyMarketIncomeIndex -
                recordedFundedPrincipalAmount;
        }

        // Add refund to interestAmount
        if (early) {
            Deposit storage depositEntry = _getDeposit(f.depositID);
            uint256 interestRate = depositEntry.interestRate;
            uint256 feeRate = depositEntry.feeRate;
            (, uint256 moneyMarketInterestRatePerSecond) =
                interestOracle.updateAndQuery();
            refundAmount = (recordedFundedPrincipalAmount -
                currentFundedPrincipalAmount)
                .mul(
                (moneyMarketInterestRatePerSecond *
                    (depositEntry.maturationTimestamp - block.timestamp))
                    .exp2() - PRECISION
            );
            uint256 maxRefundAmount =
                (recordedFundedPrincipalAmount - currentFundedPrincipalAmount)
                    .div(PRECISION + interestRate + feeRate)
                    .mul(interestRate + feeRate);
            refundAmount = refundAmount <= maxRefundAmount
                ? refundAmount
                : maxRefundAmount;
            fundingInterestAmount += refundAmount;
        }

        emit EPayFundingInterest(
            fundingID,
            fundingInterestAmount,
            refundAmount
        );
    }

    /**
        Internal getter functions
     */

    /**
        @dev See {getDeposit}
     */
    function _getDeposit(uint64 depositID)
        internal
        view
        returns (Deposit storage)
    {
        return deposits[depositID - 1];
    }

    /**
        @dev See {getFunding}
     */
    function _getFunding(uint64 fundingID)
        internal
        view
        returns (Funding storage)
    {
        return fundingList[fundingID - 1];
    }

    /**
        @dev Converts a virtual token value into the corresponding principal value.
             Principal refers to deposit + full interest + fee.
        @param depositID The ID of the deposit of the virtual tokens
        @param virtualTokenAmount The virtual token value
        @return The corresponding principal value
     */
    function _depositVirtualTokenToPrincipal(
        uint64 depositID,
        uint256 virtualTokenAmount
    ) internal view virtual returns (uint256) {
        Deposit storage depositEntry = _getDeposit(depositID);
        uint256 depositInterestRate = depositEntry.interestRate;
        return
            virtualTokenAmount.div(depositInterestRate + PRECISION).mul(
                depositInterestRate + depositEntry.feeRate + PRECISION
            );
    }

    /**
        @dev See {Rescuable._authorizeRescue}
     */
    function _authorizeRescue(
        address, /*token*/
        address /*target*/
    ) internal view override onlyOwner {}

    /**
        @dev See {surplus}
        @param incomeIndex The moneyMarket's current incomeIndex
     */
    function _surplus(uint256 incomeIndex)
        internal
        virtual
        returns (bool isNegative, uint256 surplusAmount)
    {
        // compute totalInterestOwedToFunders
        uint256 currentValue =
            (incomeIndex *
                sumOfRecordedFundedPrincipalAmountDivRecordedIncomeIndex) /
                EXTRA_PRECISION;
        uint256 initialValue = totalFundedPrincipalAmount;
        uint256 totalInterestOwedToFunders;
        if (currentValue > initialValue) {
            totalInterestOwedToFunders = currentValue - initialValue;
        }

        // compute surplus
        uint256 totalValue = moneyMarket().totalValue(incomeIndex);
        uint256 totalOwed =
            totalDeposit +
                totalInterestOwed +
                totalFeeOwed +
                totalInterestOwedToFunders;
        if (totalValue >= totalOwed) {
            // Locked value more than owed deposits, positive surplus
            isNegative = false;
            surplusAmount = totalValue - totalOwed;
        } else {
            // Locked value less than owed deposits, negative surplus
            isNegative = true;
            surplusAmount = totalOwed - totalValue;
        }
    }

    /**
        Param setters (only callable by the owner)
     */
    function setFeeModel(address newValue) external onlyOwner {
        require(newValue.isContract(), "NOT_CONTRACT");
        feeModel = IFeeModel(newValue);
        emit ESetParamAddress(msg.sender, "feeModel", newValue);
    }

    function setInterestModel(address newValue) external onlyOwner {
        require(newValue.isContract(), "NOT_CONTRACT");
        interestModel = IInterestModel(newValue);
        emit ESetParamAddress(msg.sender, "interestModel", newValue);
    }

    function setInterestOracle(address newValue) external onlyOwner {
        require(newValue.isContract(), "NOT_CONTRACT");
        interestOracle = IInterestOracle(newValue);
        emit ESetParamAddress(msg.sender, "interestOracle", newValue);
    }

    function setRewards(address newValue) external onlyOwner {
        require(newValue.isContract(), "NOT_CONTRACT");
        moneyMarket().setRewards(newValue);
        emit ESetParamAddress(msg.sender, "moneyMarket.rewards", newValue);
    }

    function setMPHMinter(address newValue) external onlyOwner {
        require(newValue.isContract(), "NOT_CONTRACT");
        mphMinter = MPHMinter(newValue);
        emit ESetParamAddress(msg.sender, "mphMinter", newValue);
    }

    function setMaxDepositPeriod(uint64 newValue) external onlyOwner {
        require(newValue > 0, "BAD_VAL");
        MaxDepositPeriod = newValue;
        emit ESetParamUint(msg.sender, "MaxDepositPeriod", uint256(newValue));
    }

    function setMinDepositAmount(uint256 newValue) external onlyOwner {
        require(newValue > 0, "BAD_VAL");
        MinDepositAmount = newValue;
        emit ESetParamUint(msg.sender, "MinDepositAmount", newValue);
    }

    function setGlobalDepositCap(uint256 newValue) external onlyOwner {
        GlobalDepositCap = newValue;
        emit ESetParamUint(msg.sender, "GlobalDepositCap", newValue);
    }

    function setDepositNFTBaseURI(string calldata newURI) external onlyOwner {
        depositNFT.setBaseURI(newURI);
    }

    function setDepositNFTContractURI(string calldata newURI)
        external
        onlyOwner
    {
        depositNFT.setContractURI(newURI);
    }

    function skimSurplus(address recipient) external onlyOwner {
        (bool isNegative, uint256 surplusMagnitude) = surplus();
        if (!isNegative) {
            surplusMagnitude = moneyMarket().withdraw(surplusMagnitude);
            stablecoin().safeTransfer(recipient, surplusMagnitude);
        }
    }

    function decreaseFeeForDeposit(uint64 depositID, uint256 newFeeRate)
        external
        onlyOwner
    {
        Deposit storage depositStorage = _getDeposit(depositID);
        uint256 feeRate = depositStorage.feeRate;
        uint256 interestRate = depositStorage.interestRate;
        uint256 virtualTokenTotalSupply =
            depositStorage.virtualTokenTotalSupply;
        require(newFeeRate < feeRate, "BAD_VAL");
        uint256 depositAmount =
            virtualTokenTotalSupply.div(interestRate + PRECISION);

        // update fee rate
        depositStorage.feeRate = newFeeRate;

        // update interest rate
        // fee reduction is allocated to interest
        uint256 reducedFeeAmount = depositAmount.mul(feeRate - newFeeRate);
        depositStorage.interestRate =
            interestRate +
            reducedFeeAmount.div(depositAmount);

        // update global amounts
        totalInterestOwed += reducedFeeAmount;
        totalFeeOwed -= reducedFeeAmount;
    }

    uint256[32] private __gap;
}

File 37 of 82 : DInterestWithDepositFee.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {DInterest} from "./DInterest.sol";

/**
    @dev A variant of DInterest that supports money markets with deposit fees
 */
contract DInterestWithDepositFee is DInterest {
    using PRBMathUD60x18 for uint256;

    uint256 public DepositFee; // The deposit fee charged by the money market

    /**
        @param _MaxDepositPeriod The maximum deposit period, in seconds
        @param _MinDepositAmount The minimum deposit amount, in stablecoins
        @param _DepositFee The fee charged by the underlying money market
        @param _feeModel Address of the FeeModel contract that determines how fees are charged
        @param _interestModel Address of the InterestModel contract that determines how much interest to offer
        @param _interestOracle Address of the InterestOracle contract that provides the average interest rate
        @param _depositNFT Address of the NFT representing ownership of deposits (owner must be set to this DInterest contract)
        @param _fundingMultitoken Address of the ERC1155 multitoken representing ownership of fundings (this DInterest contract must have the minter-burner role)
        @param _mphMinter Address of the contract for handling minting MPH to users
     */
    function initialize(
        uint64 _MaxDepositPeriod,
        uint256 _MinDepositAmount,
        uint256 _DepositFee,
        address _feeModel,
        address _interestModel,
        address _interestOracle,
        address _depositNFT,
        address _fundingMultitoken,
        address _mphMinter
    ) external virtual initializer {
        __DInterest_init(
            _MaxDepositPeriod,
            _MinDepositAmount,
            _feeModel,
            _interestModel,
            _interestOracle,
            _depositNFT,
            _fundingMultitoken,
            _mphMinter
        );
        DepositFee = _DepositFee;
    }

    /**
        Internal action functions
     */

    /**
        @dev See {deposit}
     */
    function _deposit(
        address sender,
        uint256 depositAmount,
        uint64 maturationTimestamp,
        bool rollover,
        uint256 minimumInterestAmount,
        string memory uri
    )
        internal
        virtual
        override
        returns (uint64 depositID, uint256 interestAmount)
    {
        (depositID, interestAmount) = _depositRecordData(
            sender,
            _applyDepositFee(depositAmount),
            maturationTimestamp,
            minimumInterestAmount,
            uri
        );
        _depositTransferFunds(sender, depositAmount, rollover);
    }

    /**
        @dev See {topupDeposit}
     */
    function _topupDeposit(
        address sender,
        uint64 depositID,
        uint256 depositAmount,
        uint256 minimumInterestAmount
    ) internal virtual override returns (uint256 interestAmount) {
        interestAmount = _topupDepositRecordData(
            sender,
            depositID,
            _applyDepositFee(depositAmount),
            minimumInterestAmount
        );
        _topupDepositTransferFunds(sender, depositAmount);
    }

    /**
        @dev See {fund}
     */
    function _fund(
        address sender,
        uint64 depositID,
        uint256 fundAmount,
        uint256 minPrincipalFunded
    )
        internal
        virtual
        override
        returns (
            uint64 fundingID,
            uint256 fundingMultitokensMinted,
            uint256 actualFundAmount,
            uint256 principalFunded
        )
    {
        (
            fundingID,
            fundingMultitokensMinted,
            actualFundAmount,
            principalFunded
        ) = _fundRecordData(
            sender,
            depositID,
            _applyDepositFee(fundAmount),
            minPrincipalFunded
        );
        _fundTransferFunds(sender, _unapplyDepositFee(actualFundAmount));
    }

    /**
        Internal getter functions
     */

    /**
        @dev Applies a flat percentage deposit fee to a value.
        @param amount The before-fee amount
        @return The after-fee amount
     */
    function _applyDepositFee(uint256 amount)
        internal
        view
        virtual
        returns (uint256)
    {
        return amount.mul(PRECISION - DepositFee);
    }

    /**
        @dev Unapplies a flat percentage deposit fee to a value.
        @param amount The after-fee amount
        @return The before-fee amount
     */
    function _unapplyDepositFee(uint256 amount)
        internal
        view
        virtual
        returns (uint256)
    {
        return amount.div(PRECISION - DepositFee);
    }

    /**
        Param setters (only callable by the owner)
     */

    function setDepositFee(uint256 newValue) external onlyOwner {
        require(newValue < PRECISION, "BAD_VALUE");
        DepositFee = newValue;
        emit ESetParamUint(msg.sender, "DepositFee", newValue);
    }

    uint256[49] private __gap;
}

File 38 of 82 : BoringOwnable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

// Audit on 5-Jan-2021 by Keno and BoringCrypto
// Source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol + Claimable.sol
// Edited by BoringCrypto

contract BoringOwnable {
    address private _owner;
    address private _pendingOwner;

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

    /// @notice `owner` defaults to msg.sender on construction.
    function __Ownable_init() internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    function owner() external view returns (address) {
        return _owner;
    }

    function pendingOwner() external view returns (address) {
        return _pendingOwner;
    }

    /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
    /// Can only be invoked by the current `owner`.
    /// @param newOwner Address of the new owner.
    /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
    /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
    function transferOwnership(
        address newOwner,
        bool direct,
        bool renounce
    ) public onlyOwner {
        if (direct) {
            // Checks
            require(
                newOwner != address(0) || renounce,
                "Ownable: zero address"
            );

            // Effects
            emit OwnershipTransferred(_owner, newOwner);
            _owner = newOwner;
            _pendingOwner = address(0);
        } else {
            // Effects
            _pendingOwner = newOwner;
        }
    }

    /// @notice Needs to be called by `pendingOwner` to claim ownership.
    function claimOwnership() public {
        address __pendingOwner = _pendingOwner;

        // Checks
        require(
            msg.sender == __pendingOwner,
            "Ownable: caller != pending owner"
        );

        // Effects
        emit OwnershipTransferred(_owner, __pendingOwner);
        _owner = __pendingOwner;
        _pendingOwner = address(0);
    }

    /// @notice Only allows the `owner` to execute the function.
    modifier onlyOwner() {
        require(msg.sender == _owner, "Ownable: caller is not the owner");
        _;
    }
}

File 39 of 82 : ERC1155Base.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {ERC1155Upgradeable} from "./ERC1155Upgradeable.sol";
import {
    AccessControlUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

/**
    @notice An extension of ERC1155 that provides access-controlled minting and burning,
            as well as a total supply getter for each token ID.
 */
abstract contract ERC1155Base is ERC1155Upgradeable, AccessControlUpgradeable {
    bytes32 public constant MINTER_BURNER_ROLE =
        keccak256("MINTER_BURNER_ROLE");
    bytes32 public constant METADATA_ROLE = keccak256("METADATA_ROLE");
    bytes internal constant NULL_BYTES = bytes("");

    mapping(uint256 => uint256) private _totalSupply;

    function __ERC1155Base_init(address admin, string memory uri)
        internal
        initializer
    {
        __ERC1155_init(uri);
        __ERC1155Base_init_unchained(admin);
    }

    function __ERC1155Base_init_unchained(address admin) internal initializer {
        // admin is granted metadata role and minter-burner role
        // metadata role is managed by itself
        // minter-burner role is managed by itself
        _setupRole(METADATA_ROLE, admin);
        _setupRole(MINTER_BURNER_ROLE, admin);
        _setRoleAdmin(METADATA_ROLE, METADATA_ROLE);
        _setRoleAdmin(MINTER_BURNER_ROLE, MINTER_BURNER_ROLE);
    }

    function mint(
        address to,
        uint256 id,
        uint256 amount
    ) external {
        require(
            hasRole(MINTER_BURNER_ROLE, _msgSender()),
            "ERC1155Base: must have minter-burner role to mint"
        );

        _mint(to, id, amount, NULL_BYTES);
    }

    function mintBatch(
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts
    ) external {
        require(
            hasRole(MINTER_BURNER_ROLE, _msgSender()),
            "ERC1155Base: must have minter-burner role to mint"
        );

        _mintBatch(to, ids, amounts, NULL_BYTES);
    }

    function burn(
        address account,
        uint256 id,
        uint256 amount
    ) external {
        require(
            hasRole(MINTER_BURNER_ROLE, _msgSender()),
            "ERC1155Base: must have minter-burner role to burn"
        );

        _burn(account, id, amount);
    }

    function burnBatch(
        address account,
        uint256[] calldata ids,
        uint256[] calldata amounts
    ) external {
        require(
            hasRole(MINTER_BURNER_ROLE, _msgSender()),
            "ERC1155Base: must have minter-burner role to burn"
        );

        _burnBatch(account, ids, amounts);
    }

    function setURI(string calldata newuri) external {
        require(
            hasRole(METADATA_ROLE, _msgSender()),
            "ERC1155Base: must have metadata role to set URI"
        );

        _setURI(newuri);
    }

    function totalSupply(uint256 id) public view returns (uint256) {
        return _totalSupply[id];
    }

    function totalSupplyBatch(uint256[] calldata ids)
        external
        view
        returns (uint256[] memory totalSupplies)
    {
        totalSupplies = new uint256[](ids.length);
        for (uint256 i = 0; i < ids.length; i++) {
            totalSupplies[i] = _totalSupply[ids[i]];
        }
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC1155Upgradeable, AccessControlUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override(ERC1155Upgradeable) {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        if (from == address(0)) {
            // Mint
            for (uint256 i = 0; i < ids.length; i++) {
                _totalSupply[ids[i]] += amounts[i];
            }
        } else if (to == address(0)) {
            // Burn
            for (uint256 i = 0; i < ids.length; i++) {
                _totalSupply[ids[i]] -= amounts[i];
            }
        }
    }

    uint256[49] private __gap;
}

File 40 of 82 : ERC1155DividendToken.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    SafeCastUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "./SafeERC20.sol";
import {ERC1155Base} from "./ERC1155Base.sol";

/**
    @notice An extension of ERC1155Base that allows distributing dividends to all holders
            of an token ID. Also supports multiple dividend tokens.
 */
abstract contract ERC1155DividendToken is ERC1155Base {
    using SafeERC20 for IERC20;
    using SafeCastUpgradeable for uint256;
    using SafeCastUpgradeable for int256;

    struct DividendTokenData {
        address dividendToken;
        mapping(uint256 => uint256) magnifiedDividendPerShare;
        // About dividendCorrection:
        // If the token balance of a `_user` is never changed, the dividend of `_user` can be computed with:
        //   `dividendOf(_user) = dividendPerShare * balanceOf(_user)`.
        // When `balanceOf(_user)` is changed (via minting/burning/transferring tokens),
        //   `dividendOf(_user)` should not be changed,
        //   but the computed value of `dividendPerShare * balanceOf(_user)` is changed.
        // To keep the `dividendOf(_user)` unchanged, we add a correction term:
        //   `dividendOf(_user) = dividendPerShare * balanceOf(_user) + dividendCorrectionOf(_user)`,
        //   where `dividendCorrectionOf(_user)` is updated whenever `balanceOf(_user)` is changed:
        //   `dividendCorrectionOf(_user) = dividendPerShare * (old balanceOf(_user)) - (new balanceOf(_user))`.
        // So now `dividendOf(_user)` returns the same value before and after `balanceOf(_user)` is changed.
        mapping(uint256 => mapping(address => int256)) magnifiedDividendCorrections;
        mapping(uint256 => mapping(address => uint256)) withdrawnDividends;
    }

    // With `magnitude`, we can properly distribute dividends even if the amount of received target is small.
    // For more discussion about choosing the value of `magnitude`,
    //  see https://github.com/ethereum/EIPs/issues/1726#issuecomment-472352728
    uint256 internal constant magnitude = 2**128;

    /**
        @notice The list of tokens that can be distributed to token holders as dividend. 1-indexed.
     */
    mapping(uint256 => DividendTokenData) public dividendTokenDataList;
    uint256 public dividendTokenDataListLength;
    /**
        @notice The dividend token address to its key in {dividendTokenDataList}
     */
    mapping(address => uint256) public dividendTokenToDataID;

    /// @dev This event MUST emit when target is distributed to token holders.
    /// @param from The address which sends target to this contract.
    /// @param weiAmount The amount of distributed target in wei.
    event DividendsDistributed(
        uint256 indexed tokenID,
        address indexed from,
        address indexed dividendToken,
        uint256 weiAmount
    );

    /// @dev This event MUST emit when an address withdraws their dividend.
    /// @param to The address which withdraws target from this contract.
    /// @param weiAmount The amount of withdrawn target in wei.
    event DividendWithdrawn(
        uint256 indexed tokenID,
        address indexed to,
        address indexed dividendToken,
        uint256 weiAmount
    );

    function __ERC1155DividendToken_init(
        address[] memory dividendTokens,
        address admin,
        string memory uri
    ) internal initializer {
        __ERC1155Base_init(admin, uri);
        __ERC1155DividendToken_init_unchained(dividendTokens);
    }

    function __ERC1155DividendToken_init_unchained(
        address[] memory dividendTokens
    ) internal initializer {
        dividendTokenDataListLength = dividendTokens.length;
        for (uint256 i = 0; i < dividendTokens.length; i++) {
            dividendTokenDataList[i + 1].dividendToken = dividendTokens[i];
            dividendTokenToDataID[dividendTokens[i]] = i + 1;
        }
    }

    /**
        Public getters
     */

    /// @notice View the amount of dividend in wei that an address can withdraw.
    /// @param tokenID The token's ID.
    /// @param dividendToken The token the dividend is in
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` can withdraw.
    function dividendOf(
        uint256 tokenID,
        address dividendToken,
        address _owner
    ) public view returns (uint256) {
        return _withdrawableDividendOf(tokenID, dividendToken, _owner);
    }

    /// @notice View the amount of dividend in wei that an address has withdrawn.
    /// @param tokenID The token's ID.
    /// @param dividendToken The token the dividend is in
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` has withdrawn.
    function withdrawnDividendOf(
        uint256 tokenID,
        address dividendToken,
        address _owner
    ) public view returns (uint256) {
        uint256 dividendTokenDataID = dividendTokenToDataID[dividendToken];
        if (dividendTokenDataID == 0) {
            return 0;
        }
        DividendTokenData storage data =
            dividendTokenDataList[dividendTokenDataID];
        return data.withdrawnDividends[tokenID][_owner];
    }

    /// @notice View the amount of dividend in wei that an address has earned in total.
    /// @dev accumulativeDividendOf(_owner) = _withdrawableDividendOf(_owner) + withdrawnDividendOf(_owner)
    /// = (magnifiedDividendPerShare * balanceOf(_owner) + magnifiedDividendCorrections[_owner]) / magnitude
    /// @param tokenID The token's ID.
    /// @param dividendToken The token the dividend is in
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` has earned in total.
    function accumulativeDividendOf(
        uint256 tokenID,
        address dividendToken,
        address _owner
    ) public view returns (uint256) {
        uint256 dividendTokenDataID = dividendTokenToDataID[dividendToken];
        if (dividendTokenDataID == 0) {
            return 0;
        }
        DividendTokenData storage data =
            dividendTokenDataList[dividendTokenDataID];
        return
            ((data.magnifiedDividendPerShare[tokenID] *
                balanceOf(_owner, tokenID))
                .toInt256() +
                data.magnifiedDividendCorrections[tokenID][_owner])
                .toUint256() / magnitude;
    }

    /**
        Internal functions
     */

    /// @notice View the amount of dividend in wei that an address can withdraw.
    /// @param tokenID The token's ID.
    /// @param dividendToken The token the dividend is in
    /// @param _owner The address of a token holder.
    /// @return The amount of dividend in wei that `_owner` can withdraw.
    function _withdrawableDividendOf(
        uint256 tokenID,
        address dividendToken,
        address _owner
    ) internal view returns (uint256) {
        uint256 dividendTokenDataID = dividendTokenToDataID[dividendToken];
        if (dividendTokenDataID == 0) {
            return 0;
        }
        DividendTokenData storage data =
            dividendTokenDataList[dividendTokenDataID];
        return
            accumulativeDividendOf(tokenID, dividendToken, _owner) -
            data.withdrawnDividends[tokenID][_owner];
    }

    /// @notice Distributes target to token holders as dividends.
    /// @dev It reverts if the total supply of tokens is 0.
    /// It emits the `DividendsDistributed` event if the amount of received target is greater than 0.
    /// About undistributed target tokens:
    ///   In each distribution, there is a small amount of target not distributed,
    ///     the magnified amount of which is
    ///     `(amount * magnitude) % totalSupply()`.
    ///   With a well-chosen `magnitude`, the amount of undistributed target
    ///     (de-magnified) in a distribution can be less than 1 wei.
    ///   We can actually keep track of the undistributed target in a distribution
    ///     and try to distribute it in the next distribution,
    ///     but keeping track of such data on-chain costs much more than
    ///     the saved target, so we don't do that.
    function _distributeDividends(
        uint256 tokenID,
        address dividendToken,
        uint256 amount
    ) internal {
        uint256 tokenTotalSupply = totalSupply(tokenID);
        require(tokenTotalSupply > 0);
        require(amount > 0);

        uint256 dividendTokenDataID = dividendTokenToDataID[dividendToken];
        require(
            dividendTokenDataID != 0,
            "ERC1155DividendToken: invalid dividendToken"
        );
        DividendTokenData storage data =
            dividendTokenDataList[dividendTokenDataID];

        data.magnifiedDividendPerShare[tokenID] +=
            (amount * magnitude) /
            tokenTotalSupply;

        IERC20(dividendToken).safeTransferFrom(
            msg.sender,
            address(this),
            amount
        );

        emit DividendsDistributed(tokenID, msg.sender, dividendToken, amount);
    }

    /// @notice Withdraws the target distributed to the sender.
    /// @dev It emits a `DividendWithdrawn` event if the amount of withdrawn target is greater than 0.
    function _withdrawDividend(
        uint256 tokenID,
        address dividendToken,
        address user
    ) internal {
        uint256 _withdrawableDividend =
            _withdrawableDividendOf(tokenID, dividendToken, user);
        if (_withdrawableDividend > 0) {
            uint256 dividendTokenDataID = dividendTokenToDataID[dividendToken];
            require(
                dividendTokenDataID != 0,
                "ERC1155DividendToken: invalid dividendToken"
            );
            DividendTokenData storage data =
                dividendTokenDataList[dividendTokenDataID];
            data.withdrawnDividends[tokenID][user] += _withdrawableDividend;
            emit DividendWithdrawn(
                tokenID,
                user,
                dividendToken,
                _withdrawableDividend
            );
            IERC20(dividendToken).safeTransfer(user, _withdrawableDividend);
        }
    }

    function _registerDividendToken(address dividendToken)
        internal
        returns (uint256 newDividendTokenDataID)
    {
        require(
            dividendTokenToDataID[dividendToken] == 0,
            "ERC1155DividendToken: already registered"
        );
        dividendTokenDataListLength++;
        newDividendTokenDataID = dividendTokenDataListLength;
        dividendTokenDataList[newDividendTokenDataID]
            .dividendToken = dividendToken;
        dividendTokenToDataID[dividendToken] = newDividendTokenDataID;
    }

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override(ERC1155Base) {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        if (from == address(0)) {
            // Mint
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 tokenID = ids[i];
                uint256 amount = amounts[i];

                for (uint256 j = 1; j <= dividendTokenDataListLength; j++) {
                    DividendTokenData storage dividendTokenData =
                        dividendTokenDataList[j];
                    dividendTokenData.magnifiedDividendCorrections[tokenID][
                        to
                    ] -= (dividendTokenData.magnifiedDividendPerShare[tokenID] *
                        amount)
                        .toInt256();
                }
            }
        } else if (to == address(0)) {
            // Burn
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 tokenID = ids[i];
                uint256 amount = amounts[i];

                for (uint256 j = 1; j <= dividendTokenDataListLength; j++) {
                    DividendTokenData storage dividendTokenData =
                        dividendTokenDataList[j];
                    dividendTokenData.magnifiedDividendCorrections[tokenID][
                        from
                    ] += (dividendTokenData.magnifiedDividendPerShare[tokenID] *
                        amount)
                        .toInt256();
                }
            }
        } else {
            // Transfer
            for (uint256 i = 0; i < ids.length; i++) {
                uint256 tokenID = ids[i];
                uint256 amount = amounts[i];

                for (uint256 j = 1; j <= dividendTokenDataListLength; j++) {
                    DividendTokenData storage dividendTokenData =
                        dividendTokenDataList[j];
                    int256 _magCorrection =
                        (dividendTokenData.magnifiedDividendPerShare[tokenID] *
                            amount)
                            .toInt256();
                    // Retain the rewards
                    dividendTokenData.magnifiedDividendCorrections[tokenID][
                        from
                    ] += _magCorrection;
                    dividendTokenData.magnifiedDividendCorrections[tokenID][
                        to
                    ] -= _magCorrection;
                }
            }
        }
    }

    uint256[47] private __gap;
}

File 41 of 82 : ERC1155Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {
    IERC1155Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol";
import {
    IERC1155ReceiverUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol";
import {
    IERC1155MetadataURIUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/IERC1155MetadataURIUpgradeable.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {
    ContextUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import {
    ERC165Upgradeable,
    IERC165Upgradeable
} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import {
    Initializable
} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155Upgradeable is
    Initializable,
    ContextUpgradeable,
    ERC165Upgradeable,
    IERC1155Upgradeable,
    IERC1155MetadataURIUpgradeable
{
    using AddressUpgradeable for address;

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    function __ERC1155_init(string memory uri_) internal initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __ERC1155_init_unchained(uri_);
    }

    function __ERC1155_init_unchained(string memory uri_) internal initializer {
        _setURI(uri_);
    }

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

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id)
        public
        view
        virtual
        override
        returns (uint256)
    {
        require(
            account != address(0),
            "ERC1155: balance query for the zero address"
        );
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public
        view
        virtual
        override
        returns (uint256[] memory)
    {
        require(
            accounts.length == ids.length,
            "ERC1155: accounts and ids length mismatch"
        );

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved)
        public
        virtual
        override
    {
        require(
            _msgSender() != operator,
            "ERC1155: setting approval status for self"
        );

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator)
        public
        view
        virtual
        override
        returns (bool)
    {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not owner nor approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: transfer caller is not owner nor approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(
            operator,
            from,
            to,
            _asSingletonArray(id),
            _asSingletonArray(amount),
            data
        );

        uint256 fromBalance = _balances[id][from];
        require(
            fromBalance >= amount,
            "ERC1155: insufficient balance for transfer"
        );
        _balances[id][from] = fromBalance - amount;
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(
            ids.length == amounts.length,
            "ERC1155: ids and amounts length mismatch"
        );
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(
                fromBalance >= amount,
                "ERC1155: insufficient balance for transfer"
            );
            _balances[id][from] = fromBalance - amount;
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(
            operator,
            from,
            to,
            ids,
            amounts,
            data
        );
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(
        address account,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(account != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(
            operator,
            address(0),
            account,
            _asSingletonArray(id),
            _asSingletonArray(amount),
            data
        );

        _balances[id][account] += amount;
        emit TransferSingle(operator, address(0), account, id, amount);

        _doSafeTransferAcceptanceCheck(
            operator,
            address(0),
            account,
            id,
            amount,
            data
        );
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(
            ids.length == amounts.length,
            "ERC1155: ids and amounts length mismatch"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(
            operator,
            address(0),
            to,
            ids,
            amounts,
            data
        );
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `account`
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens of token type `id`.
     */
    function _burn(
        address account,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(account != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(
            operator,
            account,
            address(0),
            _asSingletonArray(id),
            _asSingletonArray(amount),
            ""
        );

        uint256 accountBalance = _balances[id][account];
        require(
            accountBalance >= amount,
            "ERC1155: burn amount exceeds balance"
        );
        _balances[id][account] = accountBalance - amount;

        emit TransferSingle(operator, account, address(0), id, amount);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(
        address account,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(account != address(0), "ERC1155: burn from the zero address");
        require(
            ids.length == amounts.length,
            "ERC1155: ids and amounts length mismatch"
        );

        address operator = _msgSender();

        _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 accountBalance = _balances[id][account];
            require(
                accountBalance >= amount,
                "ERC1155: burn amount exceeds balance"
            );
            _balances[id][account] = accountBalance - amount;
        }

        emit TransferBatch(operator, account, address(0), ids, amounts);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
        @dev Override this to return true to skip checking to.onERC1155Received during
             single transfers.
     */
    function _shouldSkipSafeTransferAcceptanceCheck(
        address, /*operator*/
        address, /*from*/
        address, /*to*/
        uint256, /*id*/
        uint256, /*amount*/
        bytes memory /*data*/
    ) internal virtual returns (bool) {
        return false;
    }

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (
            to.isContract() &&
            !_shouldSkipSafeTransferAcceptanceCheck(
                operator,
                from,
                to,
                id,
                amount,
                data
            )
        ) {
            try
                IERC1155ReceiverUpgradeable(to).onERC1155Received(
                    operator,
                    from,
                    id,
                    amount,
                    data
                )
            returns (bytes4 response) {
                if (
                    response !=
                    IERC1155ReceiverUpgradeable(to).onERC1155Received.selector
                ) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try
                IERC1155ReceiverUpgradeable(to).onERC1155BatchReceived(
                    operator,
                    from,
                    ids,
                    amounts,
                    data
                )
            returns (bytes4 response) {
                if (
                    response !=
                    IERC1155ReceiverUpgradeable(to)
                        .onERC1155BatchReceived
                        .selector
                ) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element)
        private
        pure
        returns (uint256[] memory)
    {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }

    uint256[47] private __gap;
}

File 42 of 82 : ERC20Wrapper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    Initializable
} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {WrappedERC1155Token} from "./WrappedERC1155Token.sol";

/**
    @notice An ERC-20 wrapper for a particular tokenID of an ERC-1155 token
 */
contract ERC20Wrapper is Initializable, IERC20 {
    mapping(address => mapping(address => uint256)) private _allowances;

    WrappedERC1155Token public parentMultitoken;
    uint256 public tokenID;
    string public name;
    string public symbol;
    uint8 public decimals;

    function initialize(
        address _parentMultitoken,
        uint256 _tokenID,
        string calldata _name,
        string calldata _symbol,
        uint8 _decimals
    ) external virtual initializer {
        parentMultitoken = WrappedERC1155Token(_parentMultitoken);
        tokenID = _tokenID;
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() external view virtual override returns (uint256) {
        return parentMultitoken.totalSupply(tokenID);
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account)
        external
        view
        virtual
        override
        returns (uint256)
    {
        return parentMultitoken.balanceOf(account, tokenID);
    }

    /**
     * @dev See {IERC20-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
        virtual
        override
        returns (bool)
    {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender)
        external
        view
        virtual
        override
        returns (uint256)
    {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount)
        external
        virtual
        override
        returns (bool)
    {
        _approve(msg.sender, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * 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 virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][msg.sender];
        require(
            currentAllowance >= amount,
            "ERC20Wrapper: transfer amount exceeds allowance"
        );
        _approve(sender, msg.sender, currentAllowance - amount);

        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 {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue)
        external
        virtual
        returns (bool)
    {
        _approve(
            msg.sender,
            spender,
            _allowances[msg.sender][spender] + 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 {IERC20-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)
        external
        virtual
        returns (bool)
    {
        uint256 currentAllowance = _allowances[msg.sender][spender];
        require(
            currentAllowance >= subtractedValue,
            "ERC20Wrapper: decreased allowance below zero"
        );
        _approve(msg.sender, spender, currentAllowance - subtractedValue);

        return true;
    }

    /**
        @dev Only callable by the parentMultitoken. Emits a transfer event when the parent token
             is transferred.
     */
    function emitTransferEvent(
        address from,
        address to,
        uint256 amount
    ) external {
        require(
            msg.sender == address(parentMultitoken),
            "ERC20Wrapper: not parent"
        );
        emit Transfer(from, to, amount);
    }

    /**
     * @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 virtual {
        parentMultitoken.wrapperTransfer(sender, recipient, tokenID, amount);
        emit Transfer(sender, recipient, amount);
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(
            owner != address(0),
            "ERC20Wrapper: approve from the zero address"
        );
        require(
            spender != address(0),
            "ERC20Wrapper: approve to the zero address"
        );

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    uint256[44] private __gap;
}

File 43 of 82 : HexStrings.sol
// SPDX-License-Identifier: MIT
// MODIFIED Uniswap-v3-periphery
pragma solidity 0.8.4;

library HexStrings {
    bytes16 internal constant ALPHABET = "0123456789abcdef";

    function toHexStringNoPrefix(uint256 value, uint256 length)
        internal
        pure
        returns (string memory)
    {
        bytes memory buffer = new bytes(2 * length);
        for (uint256 i = buffer.length; i > 0; i--) {
            buffer[i - 1] = ALPHABET[value & 0xf];
            value >>= 4;
        }
        return string(buffer);
    }
}

File 44 of 82 : NFTDescriptor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

import "@openzeppelin/contracts/utils/Strings.sol";
import "base64-sol/base64.sol";
import "./HexStrings.sol";
import "./NFTSVG.sol";

library NFTDescriptor {
    using Strings for uint256;
    using HexStrings for uint256;

    struct URIParams {
        uint256 tokenId;
        address owner;
        string name;
        string symbol;
    }

    function constructTokenURI(URIParams memory params)
        public
        pure
        returns (string memory)
    {
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            abi.encodePacked(
                                '{"name":"',
                                string(abi.encodePacked(params.name, "-NFT")),
                                '", "description":"',
                                generateDescription(),
                                '", "image": "',
                                "data:image/svg+xml;base64,",
                                Base64.encode(bytes(generateSVGImage(params))),
                                '"}'
                            )
                        )
                    )
                )
            );
    }

    function escapeQuotes(string memory symbol)
        internal
        pure
        returns (string memory)
    {
        bytes memory symbolBytes = bytes(symbol);
        uint8 quotesCount = 0;
        for (uint8 i = 0; i < symbolBytes.length; i++) {
            if (symbolBytes[i] == '"') {
                quotesCount++;
            }
        }
        if (quotesCount > 0) {
            bytes memory escapedBytes =
                new bytes(symbolBytes.length + (quotesCount));
            uint256 index;
            for (uint8 i = 0; i < symbolBytes.length; i++) {
                if (symbolBytes[i] == '"') {
                    escapedBytes[index++] = "\\";
                }
                escapedBytes[index++] = symbolBytes[i];
            }
            return string(escapedBytes);
        }
        return symbol;
    }

    function addressToString(address addr)
        internal
        pure
        returns (string memory)
    {
        return uint256(uint160(addr)).toHexString(20);
    }

    function toColorHex(uint256 base, uint256 offset)
        internal
        pure
        returns (string memory str)
    {
        return string((base >> offset).toHexStringNoPrefix(3));
    }

    function generateDescription() private pure returns (string memory) {
        return
            "This NFT represents a 88mph bond. The owner of this NFT can change URI.\\n";
    }

    function generateSVGImage(URIParams memory params)
        internal
        pure
        returns (string memory svg)
    {
        NFTSVG.SVGParams memory svgParams =
            NFTSVG.SVGParams({tokenId: params.tokenId, name: params.name});

        return NFTSVG.generateSVG(svgParams);
    }
}

File 45 of 82 : NFTSVG.sol
// SPDX-License-Identifier: MIT
///@notice Inspired by Uniswap-v3-periphery NFTSVG.sol
pragma solidity 0.8.4;

import "@openzeppelin/contracts/utils/Strings.sol";
import "base64-sol/base64.sol";
import "./HexStrings.sol";

library NFTSVG {
    using Strings for uint256;

    struct SVGParams {
        uint256 tokenId;
        string name;
    }

    function generateSVG(SVGParams memory params)
        internal
        pure
        returns (string memory svg)
    {
        return
            string(
                abi.encodePacked(
                    generateSVGDefs(params),
                    generateSVGFigures(params),
                    "</svg>"
                )
            );
    }

    function generateSVGDefs(SVGParams memory params)
        private
        pure
        returns (string memory svg)
    {
        svg = string(
            abi.encodePacked(
                '<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" fill="none" xmlns:v="https://vecta.io/nano"><path fill="',
                isRare(params.tokenId, params.name) ? "purple" : "black",
                '" d="M0 0h500v500H0z"/><g fill-rule="evenodd"><path d="M235.903 292.745c6.13 10.812 21.708 10.812 27.838 0l5.902-10.41h-39.641l5.901 10.41zm-12.934-22.816l-14.066-24.811h81.839l-14.066 24.811h-53.707zm-21.1-37.216h95.906l14.066-24.811H187.803l14.066 24.811zm-21.099-37.217l-14.066-24.811H332.94l-14.066 24.811H180.77zm-21.099-37.217h180.302l16.624-29.322h-213.55l16.624 29.322zm-23.657-41.728l-5.476-9.66C124.491 96.225 132.195 83 144.456 83h210.732c12.261 0 19.966 13.225 13.919 23.891l-5.477 9.66H136.014z" fill="url(#D)"/><path d="M230.204 120.322c-12.086-1.977-80.192-10.49-85.513-10.642-2.28-.076-4.713-.228-5.321-.38s-2.66 0-4.56.38c-12.39 2.128-13.226 2.888-12.694 12.086.228 3.648.76 6.537 1.216 6.689s.836.912.836 1.748c0 .76.38 1.444.76 1.444.456 0 .76.532.76 1.14s.304 1.141.608 1.141c.38 0 1.065 1.216 1.673 2.66s1.292 2.66 1.672 2.66c.304 0 .608.532.608 1.141s.38 1.14.76 1.14c.456 0 .76.532.76 1.14s.38 1.14.76 1.14c.456 0 .76.532.76 1.14s.228 1.14.609 1.14c.304 0 1.216 1.293 2.052 2.813.836 1.596 2.128 3.42 2.812 4.104.76.684 1.368 1.445 1.368 1.597 0 1.064 7.602 8.209 10.262 9.577 3.725 1.976 11.934 4.713 14.062 4.713 2.432 0 1.748.988-2.28 3.268-2.053 1.14-4.181 2.889-4.637 3.725-1.064 1.976-.456 11.782.76 12.998.456.456.836 1.596.836 2.584s.38 1.748.76 1.748c.456 0 .761.989.761 2.281 0 1.216.304 2.28.684 2.28s.912 1.748 1.216 3.801c.304 2.128.836 3.8 1.216 3.8s.684.836.684 1.9.304 1.901.684 1.901.912.988 1.216 2.28c.304 1.216.836 2.28 1.217 2.28s.684.38.684.761c0 1.368 6.081 6.689 9.425 8.209 8.285 3.952 29.112 4.788 51.156 2.052 20.523-2.508 38.841-5.017 44.694-6.157 12.39-2.508 20.751-7.373 22.196-13.074.836-3.496-2.357-17.102-5.777-24.019-1.673-3.345-3.725-6.917-4.637-7.906-1.748-1.9-6.157-4.256-9.349-5.016l-2.205-.532 2.205-1.673c4.864-3.724 5.168-6.385 1.748-14.062-5.625-12.314-13.226-22.651-18.851-25.54-3.192-1.596-13.682-4.028-28.656-6.46zm126.483 18.242c-3.877-2.204-13.986-4.18-35.574-6.841-23.487-3.04-26.528-2.964-30.784.608-2.889 2.357-.684 11.098 6.385 25.54 3.268 6.765 5.777 9.578 10.109 11.402l2.889 1.216-1.901 2.28c-2.584 3.041-2.508 6.157.152 17.863 2.737 11.63 4.637 15.202 9.654 17.711 3.572 1.9 4.332 1.976 15.582 1.9 13.53 0 31.469-2.432 37.398-5.017 2.052-.836 4.56-2.66 5.625-3.952 1.9-2.205 1.976-2.661 1.596-8.438-.684-10.869-4.561-20.067-9.426-22.499l-2.432-1.216 1.672-1.672c2.357-2.357 1.977-6.613-1.368-14.823-3.268-8.133-5.929-12.009-9.577-14.062zm-166.237 4.941c-1.976-3.725-1.52-4.789 2.356-5.625 8.058-1.596 35.194 1.216 40.286 4.181 2.357 1.444 6.157 6.385 6.917 9.045.38 1.216.152 1.748-.836 1.976-1.748.38-36.713-1.292-38.233-1.9-.608-.228-2.053-.608-3.193-.836-2.66-.608-5.321-3.116-7.297-6.841zm36.713 38.082c-11.173.532-14.822 2.204-14.822 6.993 0 1.292.304 2.356.684 2.356s.912 1.14 1.141 2.584c.304 1.445 1.596 3.877 2.964 5.397 2.28 2.585 3.04 2.889 7.525 3.345 5.549.608 29.036-.988 32.077-2.205 1.596-.608 1.976-1.216 1.976-3.344 0-2.737-.988-5.929-2.28-7.525-.109-.13-.285-.353-.508-.635l-.003-.004-.002-.002h0l-.001-.001c-.559-.707-1.41-1.782-2.223-2.703-1.14-1.52-3.04-2.736-4.712-3.192-3.193-.76-15.127-1.368-21.816-1.064zm98.891-24.856c-3.192-1.14-5.625-7.677-3.268-8.589 2.66-1.14 17.406.228 20.447 1.824 1.368.76 3.04 3.496 4.028 6.689l.532 1.824-9.881-.456c-5.473-.304-10.794-.836-11.858-1.292zm10.414 22.803c-1.673.532-2.661 1.445-2.965 2.661-.684 2.66 1.901 10.185 3.725 11.173 1.596.837 16.646.304 19.155-.608 2.28-.912-.38-10.793-3.269-12.39-2.204-1.14-13.758-1.672-16.646-.836z" fill="#fff"/></g><g filter="url(#B)"><path fill-rule="evenodd" d="M240.973 255.316c-30.797 5.861-58.486 15.742-74.5 23.023a3.04 3.04 0 0 1-2.517-5.536c16.424-7.467 44.575-17.503 75.881-23.46 31.255-5.948 66.007-7.899 95.437 1.135a3.04 3.04 0 1 1-1.785 5.813c-28.055-8.612-61.668-6.845-92.516-.975z" fill="#8720d2"/></g><g filter="url(#C)"><mask id="A" maskUnits="userSpaceOnUse" x="167.944" y="166.317" width="157" height="115" fill="#000"><path fill="#fff" d="M167.944 166.317h157v115h-157z"/><path d="M174.782 246.284c-1.228.103-2.306-.328-3.233-1.291s-1.455-2.222-1.585-3.773c-.135-1.616.413-3.615 1.646-5.996s2.769-4.626 4.61-6.733c3.936-4.3 7.462-7.655 10.58-10.064l2.011-1.633c.997-.539 2.044-.855 3.143-.947a4.65 4.65 0 0 1 3.2.904c.964.635 1.487 1.437 1.569 2.407s-.008 1.856-.266 2.658-.562 1.447-.912 1.932l-1.623 2.772c-.177.21-.255.444-.234.703.016.194.154.28.412.258.324-.027 1.602-1.078 3.837-3.153 6.622-6.413 11.485-9.749 14.588-10.009 1.228-.103 2.294.198 3.2.903.964.636 1.484 1.406 1.56 2.311s-.05 1.729-.379 2.472c-.333.679-.82 1.468-1.461 2.368-1.05 1.455-1.561 2.344-1.534 2.667.022.259.259.369.711.331.517-.043 2.493-1.706 5.929-4.988s5.863-4.982 7.285-5.101 2.57.37 3.443 1.469c.932 1.028 1.447 2.124 1.545 3.287s-1.326 3.984-4.271 8.462c-2.949 4.413-4.4 6.91-4.351 7.492s.299.854.752.816c.382-.097.818-.329 1.308-.696s.961-.568 1.414-.606c.84-.071 1.29.249 1.35.96.178 2.133-.86 4.108-3.117 5.924-2.261 1.752-4.62 2.731-7.076 2.937-5.171.433-7.946-1.613-8.325-6.137-.076-.905.088-2.058.491-3.459.468-1.406.969-2.424 1.502-3.055.161-.404.236-.67.225-.8-.016-.193-.153-.28-.412-.258-.194.016-.444.135-.751.356-.312.156-1.605 1.436-3.877 3.84-6.218 6.574-10.588 9.967-13.109 10.178-1.357.114-2.432-.284-3.224-1.194-.798-.975-1.243-2.012-1.335-3.11-.167-2.004.618-4.283 2.357-6.837.166-.339.239-.638.217-.897-.027-.323-.17-.474-.428-.452s-.539.175-.84.461c-8.313 7.986-13.827 12.093-16.542 12.321z"/><path d="M214.254 272.169c-.26-3.102.921-7.66 3.541-13.672 2.556-6.007 5.745-12.23 9.566-18.669 8.236-13.578 15.443-24.661 21.623-33.25l4.11-5.715c.894-.986 1.826-1.52 2.795-1.601s1.711.215 2.223.888.792 1.3.841 1.882c.043.517-.245 1.745-.863 3.685-.554 1.934-1.245 4.172-2.074 6.715l-1.412 4.122c-.048.199-.061.428-.04.686s.227.372.614.339c.453-.037 1.427-.835 2.924-2.392 4.62-4.684 8.19-7.131 10.711-7.342 4.589-.384 7.079 1.75 7.468 6.404-.122 2.419-1.317 5.643-3.583 9.673s-5.237 7.794-8.915 11.292c-3.618 3.427-7.075 5.279-10.372 5.556-1.422.119-2.68-.134-3.773-.758s-1.929-1.271-2.506-1.938-.962-.994-1.156-.977c-.129.01-.309.188-.541.533l-16.209 32.602c-2.804 5.377-5.758 8.195-8.86 8.455-1.616.136-3.003-.497-4.162-1.897-1.154-1.335-1.803-2.875-1.95-4.621zm50.457-57.341c-1.616.135-4.83 2.943-9.643 8.424-4.749 5.475-7.069 8.858-6.96 10.151.01.129.145.183.404.161 2.456-.205 5.948-2.809 10.476-7.809 4.522-5.066 6.705-8.536 6.548-10.41-.033-.388-.308-.56-.825-.517z"/><path d="M318.438 230.146c-2.116 1.935-4.722 3.846-7.819 5.732-3.031 1.882-5.646 2.914-7.844 3.099-2.133.178-3.9-.325-5.301-1.509s-2.18-2.714-2.337-4.588c-.26-3.103 1.621-7.459 5.642-13.068.355-.42.525-.727.509-.921-.022-.259-.098-.383-.227-.372s-.6.213-1.414.607c-.754.323-2.827 1.994-6.219 5.012a145.76 145.76 0 0 0-9.853 9.418c-3.177 3.26-5.508 4.952-6.995 5.077-1.422.119-2.54-.406-3.354-1.574-.749-1.174-1.189-2.537-1.319-4.088-.4-4.783 2.707-12.366 9.322-22.749 6.68-10.388 13.773-19.737 21.278-28.047 7.5-8.374 12.381-12.656 14.643-12.845 1.099-.092 2.174.306 3.224 1.194 1.11.818 1.733 2.035 1.869 3.651.13 1.552-1.517 5.204-4.94 10.959s-7.275 11.837-11.554 18.249c-5.383 8.002-8.048 12.326-7.994 12.972a.34.34 0 0 0 .421.356c.258-.022 1.314-.631 3.166-1.828 1.847-1.261 3.864-2.439 6.05-3.534 2.181-1.159 3.687-1.838 4.516-2.038.889-.27 1.915-.453 3.078-.551a8.79 8.79 0 0 1 3.653.475c1.271.415 1.95 1.139 2.037 2.173.081.97-2.01 4.367-6.274 10.192-4.204 5.754-6.274 9.02-6.209 9.795.06.711.413 1.04 1.059.985 1.358-.113 3.889-1.757 7.593-4.932 1.4-1.159 2.224-1.813 2.472-1.964s.533-.24.856-.267c.905-.076 1.395.338 1.471 1.243.043.517-1.025 1.746-3.206 3.686z"/></mask><path d="M174.782 246.284c-1.228.103-2.306-.328-3.233-1.291s-1.455-2.222-1.585-3.773c-.135-1.616.413-3.615 1.646-5.996s2.769-4.626 4.61-6.733c3.936-4.3 7.462-7.655 10.58-10.064l2.011-1.633c.997-.539 2.044-.855 3.143-.947a4.65 4.65 0 0 1 3.2.904c.964.635 1.487 1.437 1.569 2.407s-.008 1.856-.266 2.658-.562 1.447-.912 1.932l-1.623 2.772c-.177.21-.255.444-.234.703.016.194.154.28.412.258.324-.027 1.602-1.078 3.837-3.153 6.622-6.413 11.485-9.749 14.588-10.009 1.228-.103 2.294.198 3.2.903.964.636 1.484 1.406 1.56 2.311s-.05 1.729-.379 2.472c-.333.679-.82 1.468-1.461 2.368-1.05 1.455-1.561 2.344-1.534 2.667.022.259.259.369.711.331.517-.043 2.493-1.706 5.929-4.988s5.863-4.982 7.285-5.101 2.57.37 3.443 1.469c.932 1.028 1.447 2.124 1.545 3.287s-1.326 3.984-4.271 8.462c-2.949 4.413-4.4 6.91-4.351 7.492s.299.854.752.816c.382-.097.818-.329 1.308-.696s.961-.568 1.414-.606c.84-.071 1.29.249 1.35.96.178 2.133-.86 4.108-3.117 5.924-2.261 1.752-4.62 2.731-7.076 2.937-5.171.433-7.946-1.613-8.325-6.137-.076-.905.088-2.058.491-3.459.468-1.406.969-2.424 1.502-3.055.161-.404.236-.67.225-.8-.016-.193-.153-.28-.412-.258-.194.016-.444.135-.751.356-.312.156-1.605 1.436-3.877 3.84-6.218 6.574-10.588 9.967-13.109 10.178-1.357.114-2.432-.284-3.224-1.194-.798-.975-1.243-2.012-1.335-3.11-.167-2.004.618-4.283 2.357-6.837.166-.339.239-.638.217-.897-.027-.323-.17-.474-.428-.452s-.539.175-.84.461c-8.313 7.986-13.827 12.093-16.542 12.321z" fill="url(#E)"/><path d="M214.254 272.169c-.26-3.102.921-7.66 3.541-13.672 2.556-6.007 5.745-12.23 9.566-18.669 8.236-13.578 15.443-24.661 21.623-33.25l4.11-5.715c.894-.986 1.826-1.52 2.795-1.601s1.711.215 2.223.888.792 1.3.841 1.882c.043.517-.245 1.745-.863 3.685-.554 1.934-1.245 4.172-2.074 6.715l-1.412 4.122c-.048.199-.061.428-.04.686s.227.372.614.339c.453-.037 1.427-.835 2.924-2.392 4.62-4.684 8.19-7.131 10.711-7.342 4.589-.384 7.079 1.75 7.468 6.404-.122 2.419-1.317 5.643-3.583 9.673s-5.237 7.794-8.915 11.292c-3.618 3.427-7.075 5.279-10.372 5.556-1.422.119-2.68-.134-3.773-.758s-1.929-1.271-2.506-1.938-.962-.994-1.156-.977c-.129.01-.309.188-.541.533l-16.209 32.602c-2.804 5.377-5.758 8.195-8.86 8.455-1.616.136-3.003-.497-4.162-1.897-1.154-1.335-1.803-2.875-1.95-4.621zm50.457-57.341c-1.616.135-4.83 2.943-9.643 8.424-4.749 5.475-7.069 8.858-6.96 10.151.01.129.145.183.404.161 2.456-.205 5.948-2.809 10.476-7.809 4.522-5.066 6.705-8.536 6.548-10.41-.033-.388-.308-.56-.825-.517z" fill="url(#E)"/><path d="M318.438 230.146c-2.116 1.935-4.722 3.846-7.819 5.732-3.031 1.882-5.646 2.914-7.844 3.099-2.133.178-3.9-.325-5.301-1.509s-2.18-2.714-2.337-4.588c-.26-3.103 1.621-7.459 5.642-13.068.355-.42.525-.727.509-.921-.022-.259-.098-.383-.227-.372s-.6.213-1.414.607c-.754.323-2.827 1.994-6.219 5.012a145.76 145.76 0 0 0-9.853 9.418c-3.177 3.26-5.508 4.952-6.995 5.077-1.422.119-2.54-.406-3.354-1.574-.749-1.174-1.189-2.537-1.319-4.088-.4-4.783 2.707-12.366 9.322-22.749 6.68-10.388 13.773-19.737 21.278-28.047 7.5-8.374 12.381-12.656 14.643-12.845 1.099-.092 2.174.306 3.224 1.194 1.11.818 1.733 2.035 1.869 3.651.13 1.552-1.517 5.204-4.94 10.959s-7.275 11.837-11.554 18.249c-5.383 8.002-8.048 12.326-7.994 12.972a.34.34 0 0 0 .421.356c.258-.022 1.314-.631 3.166-1.828 1.847-1.261 3.864-2.439 6.05-3.534 2.181-1.159 3.687-1.838 4.516-2.038.889-.27 1.915-.453 3.078-.551a8.79 8.79 0 0 1 3.653.475c1.271.415 1.95 1.139 2.037 2.173.081.97-2.01 4.367-6.274 10.192-4.204 5.754-6.274 9.02-6.209 9.795.06.711.413 1.04 1.059.985 1.358-.113 3.889-1.757 7.593-4.932 1.4-1.159 2.224-1.813 2.472-1.964s.533-.24.856-.267c.905-.076 1.395.338 1.471 1.243.043.517-1.025 1.746-3.206 3.686z" fill="url(#E)"/><path d="M174.782 246.284c-1.228.103-2.306-.328-3.233-1.291s-1.455-2.222-1.585-3.773c-.135-1.616.413-3.615 1.646-5.996s2.769-4.626 4.61-6.733c3.936-4.3 7.462-7.655 10.58-10.064l2.011-1.633c.997-.539 2.044-.855 3.143-.947a4.65 4.65 0 0 1 3.2.904c.964.635 1.487 1.437 1.569 2.407s-.008 1.856-.266 2.658-.562 1.447-.912 1.932l-1.623 2.772c-.177.21-.255.444-.234.703.016.194.154.28.412.258.324-.027 1.602-1.078 3.837-3.153 6.622-6.413 11.485-9.749 14.588-10.009 1.228-.103 2.294.198 3.2.903.964.636 1.484 1.406 1.56 2.311s-.05 1.729-.379 2.472c-.333.679-.82 1.468-1.461 2.368-1.05 1.455-1.561 2.344-1.534 2.667.022.259.259.369.711.331.517-.043 2.493-1.706 5.929-4.988s5.863-4.982 7.285-5.101 2.57.37 3.443 1.469c.932 1.028 1.447 2.124 1.545 3.287s-1.326 3.984-4.271 8.462c-2.949 4.413-4.4 6.91-4.351 7.492s.299.854.752.816c.382-.097.818-.329 1.308-.696s.961-.568 1.414-.606c.84-.071 1.29.249 1.35.96.178 2.133-.86 4.108-3.117 5.924-2.261 1.752-4.62 2.731-7.076 2.937-5.171.433-7.946-1.613-8.325-6.137-.076-.905.088-2.058.491-3.459.468-1.406.969-2.424 1.502-3.055.161-.404.236-.67.225-.8-.016-.193-.153-.28-.412-.258-.194.016-.444.135-.751.356-.312.156-1.605 1.436-3.877 3.84-6.218 6.574-10.588 9.967-13.109 10.178-1.357.114-2.432-.284-3.224-1.194-.798-.975-1.243-2.012-1.335-3.11-.167-2.004.618-4.283 2.357-6.837.166-.339.239-.638.217-.897-.027-.323-.17-.474-.428-.452s-.539.175-.84.461c-8.313 7.986-13.827 12.093-16.542 12.321z" stroke="url(#F)" stroke-width="4" mask="url(#A)"/><path d="M214.254 272.169c-.26-3.102.921-7.66 3.541-13.672 2.556-6.007 5.745-12.23 9.566-18.669 8.236-13.578 15.443-24.661 21.623-33.25l4.11-5.715c.894-.986 1.826-1.52 2.795-1.601s1.711.215 2.223.888.792 1.3.841 1.882c.043.517-.245 1.745-.863 3.685-.554 1.934-1.245 4.172-2.074 6.715l-1.412 4.122c-.048.199-.061.428-.04.686s.227.372.614.339c.453-.037 1.427-.835 2.924-2.392 4.62-4.684 8.19-7.131 10.711-7.342 4.589-.384 7.079 1.75 7.468 6.404-.122 2.419-1.317 5.643-3.583 9.673s-5.237 7.794-8.915 11.292c-3.618 3.427-7.075 5.279-10.372 5.556-1.422.119-2.68-.134-3.773-.758s-1.929-1.271-2.506-1.938-.962-.994-1.156-.977c-.129.01-.309.188-.541.533l-16.209 32.602c-2.804 5.377-5.758 8.195-8.86 8.455-1.616.136-3.003-.497-4.162-1.897-1.154-1.335-1.803-2.875-1.95-4.621zm50.457-57.341c-1.616.135-4.83 2.943-9.643 8.424-4.749 5.475-7.069 8.858-6.96 10.151.01.129.145.183.404.161 2.456-.205 5.948-2.809 10.476-7.809 4.522-5.066 6.705-8.536 6.548-10.41-.033-.388-.308-.56-.825-.517z" stroke="url(#F)" stroke-width="4" mask="url(#A)"/><path d="M318.438 230.146c-2.116 1.935-4.722 3.846-7.819 5.732-3.031 1.882-5.646 2.914-7.844 3.099-2.133.178-3.9-.325-5.301-1.509s-2.18-2.714-2.337-4.588c-.26-3.103 1.621-7.459 5.642-13.068.355-.42.525-.727.509-.921-.022-.259-.098-.383-.227-.372s-.6.213-1.414.607c-.754.323-2.827 1.994-6.219 5.012a145.76 145.76 0 0 0-9.853 9.418c-3.177 3.26-5.508 4.952-6.995 5.077-1.422.119-2.54-.406-3.354-1.574-.749-1.174-1.189-2.537-1.319-4.088-.4-4.783 2.707-12.366 9.322-22.749 6.68-10.388 13.773-19.737 21.278-28.047 7.5-8.374 12.381-12.656 14.643-12.845 1.099-.092 2.174.306 3.224 1.194 1.11.818 1.733 2.035 1.869 3.651.13 1.552-1.517 5.204-4.94 10.959s-7.275 11.837-11.554 18.249c-5.383 8.002-8.048 12.326-7.994 12.972a.34.34 0 0 0 .421.356c.258-.022 1.314-.631 3.166-1.828 1.847-1.261 3.864-2.439 6.05-3.534 2.181-1.159 3.687-1.838 4.516-2.038.889-.27 1.915-.453 3.078-.551a8.79 8.79 0 0 1 3.653.475c1.271.415 1.95 1.139 2.037 2.173.081.97-2.01 4.367-6.274 10.192-4.204 5.754-6.274 9.02-6.209 9.795.06.711.413 1.04 1.059.985 1.358-.113 3.889-1.757 7.593-4.932 1.4-1.159 2.224-1.813 2.472-1.964s.533-.24.856-.267c.905-.076 1.395.338 1.471 1.243.043.517-1.025 1.746-3.206 3.686z" stroke="url(#F)" stroke-width="4" mask="url(#A)"/></g><defs><filter id="B" x="158.174" y="244.288" width="183.249" height="42.324" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="A"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="2"/><feColorMatrix values="0 0 0 0 0.898039 0 0 0 0 0.129412 0 0 0 0 0.615686 0 0 0 0.5 0"/><feBlend in2="A"/><feBlend in="SourceGraphic"/></filter><filter id="C" x="155.944" y="159.316" width="180.306" height="137.389" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="A"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="6"/><feColorMatrix values="0 0 0 0 0.898039 0 0 0 0 0.129412 0 0 0 0 0.615686 0 0 0 0.5 0"/><feBlend in2="A"/><feBlend in="SourceGraphic"/></filter><linearGradient id="D" x1="249.822" y1="300.854" x2="249.822" y2="83.787" gradientUnits="userSpaceOnUse"><stop stop-color="#ffe600"/><stop offset=".307" stop-color="#faad14"/><stop offset=".672" stop-color="#f7169c"/><stop offset="1" stop-color="#3435f5"/></linearGradient><linearGradient id="E" x1="252.249" y1="189.416" x2="279.994" y2="284.81" gradientUnits="userSpaceOnUse"><stop offset=".224" stop-color="#ff009d"/><stop offset=".88" stop-color="#3435f5"/></linearGradient><linearGradient id="F" x1="239.561" y1="200.536" x2="241.909" y2="272.268" gradientUnits="userSpaceOnUse"><stop stop-color="#fff"/><stop offset="1" stop-color="#f7169c"/></linearGradient></defs>'
            )
        );
    }

    function generateSVGFigures(SVGParams memory params)
        private
        pure
        returns (string memory svg)
    {
        svg = generateSVGText(params);
    }

    function generateSVGText(SVGParams memory params)
        private
        pure
        returns (string memory svg)
    {
        svg = string(
            abi.encodePacked(
                "<style> .small { font: normal 16px sans-serif; } .large { font: bold 24px sans-serif; }</style>",
                '<text x="50%" y="70%" dominant-baseline="middle" text-anchor="middle" fill="#fff" class="large">',
                params.name,
                '</text><text x="50%" y="80%" dominant-baseline="middle" text-anchor="middle" fill="#fff" class="small">Token ID ',
                params.tokenId.toString(),
                "</text>"
            )
        );
    }

    function isRare(uint256 tokenId, string memory name)
        internal
        pure
        returns (bool)
    {
        return uint256(keccak256(abi.encodePacked(tokenId, name))) > 5**tokenId;
    }
}

File 46 of 82 : Rescuable.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "./SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
    @notice Inherit this to allow for rescuing ERC20 tokens sent to the contract in error.
 */
abstract contract Rescuable {
    using SafeERC20 for IERC20;

    /**
        @notice Rescues ERC20 tokens sent to the contract in error.
        @dev Need to implement {_authorizeRescue} to do access-control for this function.
        @param token The ERC20 token to rescue
        @param target The address to send the tokens to
     */
    function rescue(address token, address target) external virtual {
        // make sure we're not stealing funds or something
        _authorizeRescue(token, target);

        // transfer token to target
        IERC20 tokenContract = IERC20(token);
        tokenContract.safeTransfer(
            target,
            tokenContract.balanceOf(address(this))
        );
    }

    /**
        @dev IMPORTANT MUST READ
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST revert during a call to this function if the token rescue should be stopped.
        2) You MUST implement proper access control to prevent stealing funds.
        3) You MUST revert if `token` is a token your contract holds as user funds.
        @param token The ERC20 token to rescue
        @param target The address to send the tokens to
     */
    function _authorizeRescue(address token, address target) internal virtual;

    uint256[50] private __gap;
}

File 47 of 82 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";

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

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

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

    /**
        @dev Modified from openzeppelin. Instead of reverting when the allowance is non-zero,
        we first set the allowance to 0 and then call approve(spender, currentAllowance + value).
        This provides support for non-standard tokens such as USDT that revert in this scenario. 
     */
    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 currentAllowance = token.allowance(address(this), spender);
        if (currentAllowance > 0) {
            _callOptionalReturn(
                token,
                abi.encodeWithSelector(token.approve.selector, spender, 0)
            );
        }
        _callOptionalReturn(
            token,
            abi.encodeWithSelector(
                token.approve.selector,
                spender,
                currentAllowance + value
            )
        );
    }

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

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

File 48 of 82 : Sponsorable.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {SafeERC20} from "./SafeERC20.sol";

/**
    @notice Add support for meta-txs that use ERC20 tokens to pay for gas
 */
abstract contract Sponsorable {
    using SafeERC20 for IERC20;

    /**
        @dev Using uint256 for all numbers since this struct won't ever be in storage. This saves gas.
        @param sender The user who made the meta-tx
        @param sponsor The account that should receive the sponsor fee
        @param sponsorFeeToken The ERC20 token address the sponsor fee is paid in
        @param sponsorFeeAmount The amount of sponsor fee to transfer from `sender` to `sponsor`
        @param nonce The signature nonce used for preventing replay attacks. Should equal accountNonce[sender].
        @param deadline The timestamp after which the signature is invalid
        @param v ECDSA signature component: Parity of the `y` coordinate of point `R`
        @param r ECDSA signature component: x-coordinate of `R`
        @param s ECDSA signature component: `s` value of the signature
     */
    struct Sponsorship {
        address sender;
        address sponsor;
        address sponsorFeeToken;
        uint256 sponsorFeeAmount;
        uint256 nonce;
        uint256 deadline;
        uint256 v;
        bytes32 r;
        bytes32 s;
    }

    mapping(address => uint256) public accountNonce;

    /**
        @dev Use this for functions that should support meta-txs.
        @param sponsorship The sponsorship information
        @param funcSignature The function signature (selector) of the function being called
        @param encodedParams The parameters of the function, encoded using abi.encode()
     */
    modifier sponsored(
        Sponsorship memory sponsorship,
        bytes4 funcSignature,
        bytes memory encodedParams
    ) {
        _validateSponsorship(sponsorship, funcSignature, encodedParams);
        _paySponsor(
            sponsorship.sender,
            sponsorship.sponsor,
            sponsorship.sponsorFeeToken,
            sponsorship.sponsorFeeAmount
        );
        _;
    }

    /**
        @dev Validates the signature of a meta-tx sponsorship, reverts if the signature is invalid.
        @param sponsorship The sponsorship information
        @param funcSignature The function signature (selector) of the function being called
        @param encodedParams The parameters of the function, encoded using abi.encode()
     */
    function _validateSponsorship(
        Sponsorship memory sponsorship,
        bytes4 funcSignature,
        bytes memory encodedParams
    ) internal virtual {
        require(
            sponsorship.nonce == accountNonce[sponsorship.sender],
            "Sponsorable: BAD_NONCE"
        );
        require(
            block.timestamp <= sponsorship.deadline,
            "Sponsorable: SIG_DEAD"
        );

        uint256 chainId;
        assembly {
            chainId := chainid()
        }

        bytes32 digest =
            keccak256(
                abi.encodePacked(
                    "\x19Ethereum Signed Message:\n32",
                    keccak256(
                        abi.encodePacked(
                            abi.encode(
                                chainId,
                                address(this),
                                sponsorship.sponsor,
                                sponsorship.sponsorFeeToken,
                                sponsorship.sponsorFeeAmount,
                                sponsorship.nonce,
                                sponsorship.deadline,
                                funcSignature
                            ),
                            encodedParams
                        )
                    )
                )
            );

        address recoveredAddress =
            ECDSA.recover(
                digest,
                uint8(sponsorship.v),
                sponsorship.r,
                sponsorship.s
            );
        require(
            recoveredAddress != address(0) &&
                recoveredAddress == sponsorship.sender,
            "Sponsorable: BAD_SIG"
        );

        // update nonce
        accountNonce[sponsorship.sender] = sponsorship.nonce + 1;
    }

    /**
        @dev Transfers `sponsorFeeAmount` of ERC20 token `sponsorFeeToken` from `sender` to `sponsor`.
        @param sender The user who made the meta-tx
        @param sponsor The account that should receive the sponsor fee
        @param sponsorFeeToken The ERC20 token address the sponsor fee is paid in
        @param sponsorFeeAmount The amount of sponsor fee to transfer from `sender` to `sponsor`
     */
    function _paySponsor(
        address sender,
        address sponsor,
        address sponsorFeeToken,
        uint256 sponsorFeeAmount
    ) internal virtual {
        if (sponsorFeeAmount == 0) {
            return;
        }

        IERC20 token = IERC20(sponsorFeeToken);

        // transfer tokens from sender to sponsor
        token.safeTransferFrom(sender, sponsor, sponsorFeeAmount);
    }

    uint256[49] private __gap;
}

File 49 of 82 : WrappedERC1155Token.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    ClonesUpgradeable
} from "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol";
import {
    StringsUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
import {ERC1155Upgradeable} from "./ERC1155Upgradeable.sol";
import {ERC1155Base} from "./ERC1155Base.sol";
import {ERC20Wrapper} from "./ERC20Wrapper.sol";

/**
    @notice An ERC-1155 multitoken where each ID is wrapped in an ERC-20 interface
 */
abstract contract WrappedERC1155Token is ERC1155Base {
    using ClonesUpgradeable for address;
    using StringsUpgradeable for uint256;

    mapping(uint256 => address) public tokenIDToWrapper;
    address public wrapperTemplate;
    bool public deployWrapperOnMint;
    string public baseName;
    string public baseSymbol;
    uint8 public decimals;

    function __WrappedERC1155Token_init(
        address admin,
        string memory uri,
        address _wrapperTemplate,
        bool _deployWrapperOnMint,
        string memory _baseName,
        string memory _baseSymbol,
        uint8 _decimals
    ) internal initializer {
        __ERC1155Base_init(admin, uri);
        __WrappedERC1155Token_init_unchained(
            _wrapperTemplate,
            _deployWrapperOnMint,
            _baseName,
            _baseSymbol,
            _decimals
        );
    }

    function __WrappedERC1155Token_init_unchained(
        address _wrapperTemplate,
        bool _deployWrapperOnMint,
        string memory _baseName,
        string memory _baseSymbol,
        uint8 _decimals
    ) internal initializer {
        wrapperTemplate = _wrapperTemplate;
        deployWrapperOnMint = _deployWrapperOnMint;
        baseName = _baseName;
        baseSymbol = _baseSymbol;
        decimals = _decimals;
    }

    /**
        @notice Called by an ERC20Wrapper contract to handle a transfer call.
        @dev Only callable by a wrapper deployed by this contract.
        @param from Source of transfer
        @param to Target of transfer
        @param tokenID The ERC-1155 token ID of the wrapper
        @param amount The amount to transfer
     */
    function wrapperTransfer(
        address from,
        address to,
        uint256 tokenID,
        uint256 amount
    ) external {
        require(
            msg.sender == tokenIDToWrapper[tokenID],
            "WrappedERC1155Token: not wrapper"
        );
        _safeTransferFrom(from, to, tokenID, amount, bytes(""));
    }

    /**
        @notice Deploys an ERC20Wrapper contract for the ERC-1155 tokens with ID `tokenID`.
        @dev If a wrapper already exists for this tokenID, does nothing and returns the address
             of the existing wrapper.
        @param tokenID The ID of the token to wrap
        @return wrapperAddress The address of the wrapper
     */
    function deployWrapper(uint256 tokenID)
        external
        returns (address wrapperAddress)
    {
        return _deployWrapper(tokenID);
    }

    /**
        @dev See {deployWrapper}
     */
    function _deployWrapper(uint256 tokenID)
        internal
        returns (address wrapperAddress)
    {
        wrapperAddress = tokenIDToWrapper[tokenID];
        if (wrapperAddress == address(0)) {
            // deploy wrapper
            ERC20Wrapper wrapper = ERC20Wrapper(wrapperTemplate.clone());
            string memory tokenIDString = tokenID.toString();
            string memory name =
                string(abi.encodePacked(baseName, tokenIDString));
            string memory symbol =
                string(abi.encodePacked(baseSymbol, tokenIDString));
            wrapper.initialize(address(this), tokenID, name, symbol, decimals);
            tokenIDToWrapper[tokenID] = address(wrapper);
        }
    }

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);

        if (from == address(0)) {
            // Mint
            if (deployWrapperOnMint) {
                for (uint256 i = 0; i < ids.length; i++) {
                    _deployWrapper(ids[i]);
                }
            }
        }

        // Emit transfer event in wrapper
        for (uint256 i = 0; i < ids.length; i++) {
            address wrapperAddress = tokenIDToWrapper[ids[i]];
            if (wrapperAddress != address(0)) {
                ERC20Wrapper wrapper = ERC20Wrapper(wrapperAddress);
                wrapper.emitTransferEvent(from, to, amounts[i]);
            }
        }
    }

    /**
        @dev See {ERC1155Upgradeable._shouldSkipSafeTransferAcceptanceCheck}
     */
    function _shouldSkipSafeTransferAcceptanceCheck(
        address operator,
        address, /*from*/
        address, /*to*/
        uint256 id,
        uint256, /*amount*/
        bytes memory /*data*/
    ) internal virtual override(ERC1155Upgradeable) returns (bool) {
        address wrapperAddress = tokenIDToWrapper[id];
        if (wrapperAddress != address(0)) {
            // has wrapper, check if operator is the wrapper
            return operator == wrapperAddress;
        } else {
            // no wrapper, should do safety checks
            return false;
        }
    }

    /**
        Param setters (need metadata role)
     */
    function setDeployWrapperOnMint(bool newValue) external {
        require(
            hasRole(METADATA_ROLE, msg.sender),
            "WrappedERC1155Token: no metadata role"
        );
        deployWrapperOnMint = newValue;
    }

    function setBaseName(string calldata newValue) external {
        require(
            hasRole(METADATA_ROLE, msg.sender),
            "WrappedERC1155Token: no metadata role"
        );
        baseName = newValue;
    }

    function setBaseSymbol(string calldata newValue) external {
        require(
            hasRole(METADATA_ROLE, msg.sender),
            "WrappedERC1155Token: no metadata role"
        );
        baseSymbol = newValue;
    }

    uint256[44] private __gap;
}

File 50 of 82 : IFeeModel.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

interface IFeeModel {
    function beneficiary() external view returns (address payable);

    function getInterestFeeAmount(address pool, uint256 interestAmount)
        external
        view
        returns (uint256 feeAmount);

    function getEarlyWithdrawFeeAmount(
        address pool,
        uint64 depositID,
        uint256 withdrawnDepositAmount
    ) external view returns (uint256 feeAmount);
}

File 51 of 82 : EMAOracle.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    Initializable
} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {IInterestOracle} from "./IInterestOracle.sol";
import {MoneyMarket} from "../../moneymarkets/MoneyMarket.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";

contract EMAOracle is IInterestOracle, Initializable {
    using PRBMathUD60x18 for uint256;

    uint256 internal constant PRECISION = 10**18;

    /**
        Immutable parameters
     */
    uint256 public UPDATE_INTERVAL;
    uint256 public UPDATE_MULTIPLIER;
    uint256 public ONE_MINUS_UPDATE_MULTIPLIER;

    /**
        Public variables
     */
    uint256 public emaStored;
    uint256 public lastIncomeIndex;
    uint256 public lastUpdateTimestamp;

    /**
        External contracts
     */
    MoneyMarket public override moneyMarket;

    function initialize(
        uint256 _emaInitial,
        uint256 _updateInterval,
        uint256 _smoothingFactor,
        uint256 _averageWindowInIntervals,
        address _moneyMarket
    ) external initializer {
        emaStored = _emaInitial;
        UPDATE_INTERVAL = _updateInterval;
        lastUpdateTimestamp = block.timestamp;

        uint256 updateMultiplier =
            _smoothingFactor / (_averageWindowInIntervals + 1);
        UPDATE_MULTIPLIER = updateMultiplier;
        ONE_MINUS_UPDATE_MULTIPLIER = PRECISION - updateMultiplier;

        moneyMarket = MoneyMarket(_moneyMarket);
        lastIncomeIndex = moneyMarket.incomeIndex();
    }

    function updateAndQuery()
        external
        override
        returns (bool updated, uint256 value)
    {
        uint256 timeElapsed = block.timestamp - lastUpdateTimestamp;
        if (timeElapsed < UPDATE_INTERVAL) {
            return (false, emaStored);
        }

        // save gas by loading storage variables to memory
        uint256 _lastIncomeIndex = lastIncomeIndex;
        uint256 _emaStored = emaStored;

        uint256 newIncomeIndex = moneyMarket.incomeIndex();
        if (newIncomeIndex < _lastIncomeIndex) {
            // Shouldn't revert (which would block execution)
            // Assume no interest was accrued and use the last index as the new one
            // which would push the EMA towards zero if there's e.g. an exploit
            // in the underlying yield protocol
            newIncomeIndex = _lastIncomeIndex;
        }
        // incomingValue = log2(newIncomeIndex / _lastIncomeIndex) * (1 / timeElapsed)
        uint256 incomingValue =
            newIncomeIndex.div(_lastIncomeIndex).log2().mul(
                PRBMathUD60x18.SCALE / timeElapsed
            );

        updated = true;
        value =
            (incomingValue *
                UPDATE_MULTIPLIER +
                _emaStored *
                ONE_MINUS_UPDATE_MULTIPLIER) /
            PRECISION;
        emaStored = value;
        lastIncomeIndex = newIncomeIndex;
        lastUpdateTimestamp = block.timestamp;
    }

    function query() external view override returns (uint256 value) {
        return emaStored;
    }

    uint256[43] private __gap;
}

File 52 of 82 : IInterestOracle.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {MoneyMarket} from "../../moneymarkets/MoneyMarket.sol";

interface IInterestOracle {
    function updateAndQuery() external returns (bool updated, uint256 value);

    function query() external view returns (uint256 value);

    function moneyMarket() external view returns (MoneyMarket);
}

File 53 of 82 : IInterestModel.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

interface IInterestModel {
    function calculateInterestAmount(
        uint256 depositAmount,
        uint256 depositPeriodInSeconds,
        uint256 moneyMarketInterestRatePerSecond,
        bool surplusIsNegative,
        uint256 surplusAmount
    ) external view returns (uint256 interestAmount);
}

File 54 of 82 : MoneyMarket.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {
    AccessControlUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {Rescuable} from "../libs/Rescuable.sol";

// Interface for money market protocols (Compound, Aave, etc.)
abstract contract MoneyMarket is
    Rescuable,
    OwnableUpgradeable,
    AccessControlUpgradeable
{
    bytes32 internal constant RESCUER_ROLE = keccak256("RESCUER_ROLE");

    function __MoneyMarket_init(address rescuer) internal initializer {
        __Ownable_init();
        __AccessControl_init();

        // RESCUER_ROLE is managed by itself
        _setupRole(RESCUER_ROLE, rescuer);
        _setRoleAdmin(RESCUER_ROLE, RESCUER_ROLE);
    }

    function deposit(uint256 amount) external virtual;

    function withdraw(uint256 amountInUnderlying)
        external
        virtual
        returns (uint256 actualAmountWithdrawn);

    /**
        @notice The total value locked in the money market, in terms of the underlying stablecoin
     */
    function totalValue() external returns (uint256) {
        return _totalValue(_incomeIndex());
    }

    /**
        @notice The total value locked in the money market, in terms of the underlying stablecoin
     */
    function totalValue(uint256 currentIncomeIndex)
        external
        view
        returns (uint256)
    {
        return _totalValue(currentIncomeIndex);
    }

    /**
        @notice Used for calculating the interest generated (e.g. cDai's price for the Compound market)
     */
    function incomeIndex() external returns (uint256 index) {
        return _incomeIndex();
    }

    function stablecoin() external view virtual returns (ERC20);

    function claimRewards() external virtual; // Claims farmed tokens (e.g. COMP, CRV) and sends it to the rewards pool

    function setRewards(address newValue) external virtual;

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(
        address, /*token*/
        address /*target*/
    ) internal view virtual override {
        require(hasRole(RESCUER_ROLE, msg.sender), "MoneyMarket: not rescuer");
    }

    function _totalValue(uint256 currentIncomeIndex)
        internal
        view
        virtual
        returns (uint256);

    function _incomeIndex() internal virtual returns (uint256 index);

    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
}

File 55 of 82 : AaveMarket.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "../../libs/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {MoneyMarket} from "../MoneyMarket.sol";
import {ILendingPool} from "./imports/ILendingPool.sol";
import {
    ILendingPoolAddressesProvider
} from "./imports/ILendingPoolAddressesProvider.sol";
import {IAaveMining} from "./imports/IAaveMining.sol";

contract AaveMarket is MoneyMarket {
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;

    uint16 internal constant REFERRALCODE = 20; // Aave referral program code

    ILendingPoolAddressesProvider public provider; // Used for fetching the current address of LendingPool
    ERC20 public override stablecoin;
    ERC20 public aToken;
    IAaveMining public aaveMining;
    address public rewards;

    function initialize(
        address _provider,
        address _aToken,
        address _aaveMining,
        address _rewards,
        address _rescuer,
        address _stablecoin
    ) external initializer {
        __MoneyMarket_init(_rescuer);

        // Verify input addresses
        require(
            _provider.isContract() &&
                _aToken.isContract() &&
                _aaveMining.isContract() &&
                _rewards != address(0) &&
                _stablecoin.isContract(),
            "AaveMarket: An input address is not a contract"
        );

        provider = ILendingPoolAddressesProvider(_provider);
        stablecoin = ERC20(_stablecoin);
        aaveMining = IAaveMining(_aaveMining);
        aToken = ERC20(_aToken);
        rewards = _rewards;
    }

    function deposit(uint256 amount) external override onlyOwner {
        require(amount > 0, "AaveMarket: amount is 0");

        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Approve `amount` stablecoin to lendingPool
        stablecoin.safeIncreaseAllowance(address(lendingPool), amount);

        // Deposit `amount` stablecoin to lendingPool
        lendingPool.deposit(
            address(stablecoin),
            amount,
            address(this),
            REFERRALCODE
        );
    }

    function withdraw(uint256 amountInUnderlying)
        external
        override
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(amountInUnderlying > 0, "AaveMarket: amountInUnderlying is 0");

        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());

        // Redeem `amountInUnderlying` aToken, since 1 aToken = 1 stablecoin
        // Transfer `amountInUnderlying` stablecoin to `msg.sender`
        lendingPool.withdraw(
            address(stablecoin),
            amountInUnderlying,
            msg.sender
        );

        return amountInUnderlying;
    }

    function claimRewards() external override {
        address[] memory assets = new address[](1);
        assets[0] = address(aToken);
        aaveMining.claimRewards(assets, type(uint256).max, rewards);
    }

    /**
        Param setters
     */
    function setRewards(address newValue) external override onlyOwner {
        require(newValue != address(0), "AaveMarket: 0 address");
        rewards = newValue;
        emit ESetParamAddress(msg.sender, "rewards", newValue);
    }

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(address token, address target)
        internal
        view
        override
    {
        super._authorizeRescue(token, target);
        require(token != address(aToken), "AaveMarket: no steal");
    }

    function _totalValue(
        uint256 /*currentIncomeIndex*/
    ) internal view override returns (uint256) {
        return aToken.balanceOf(address(this));
    }

    function _incomeIndex() internal view override returns (uint256 index) {
        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
        index = lendingPool.getReserveNormalizedIncome(address(stablecoin));
        require(index > 0, "AaveMarket: BAD_INDEX");
    }

    uint256[45] private __gap;
}

File 56 of 82 : IAaveMining.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

interface IAaveMining {
    function claimRewards(
        address[] calldata assets,
        uint256 amount,
        address to
    ) external returns (uint256);
}

File 57 of 82 : ILendingPool.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

// Aave lending pool interface
// Documentation: https://docs.aave.com/developers/the-core-protocol/lendingpool/ilendingpool
// refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
interface ILendingPool {
    /**
     * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
     * @param asset The address of the underlying asset to deposit
     * @param amount The amount to be deposited
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function deposit(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16 referralCode
    ) external;

    /**
     * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
     * @param asset The address of the underlying asset to withdraw
     * @param amount The underlying amount to be withdrawn
     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
     * @param to Address that will receive the underlying, same as msg.sender if the user
     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
     *   different wallet
     * @return The final amount withdrawn
     **/
    function withdraw(
        address asset,
        uint256 amount,
        address to
    ) external returns (uint256);

    /**
     * @dev Returns the normalized income normalized income of the reserve
     * @param asset The address of the underlying asset of the reserve
     * @return The reserve's normalized income
     */
    function getReserveNormalizedIncome(address asset)
        external
        view
        returns (uint256);
}

File 58 of 82 : ILendingPoolAddressesProvider.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

/**
 * @title LendingPoolAddressesProvider contract
 * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
 * - Acting also as factory of proxies and admin of those, so with right to change its implementations
 * - Owned by the Aave Governance
 * @author Aave
 **/
interface ILendingPoolAddressesProvider {
    function getLendingPool() external view returns (address);
}

File 59 of 82 : BProtocolMarket.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "../../libs/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {MoneyMarket} from "../MoneyMarket.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {IBToken} from "./imports/IBToken.sol";
import {IBComptroller} from "./imports/IBComptroller.sol";

contract BProtocolMarket is MoneyMarket {
    using PRBMathUD60x18 for uint256;
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;

    uint256 internal constant ERRCODE_OK = 0;

    IBToken public bToken;
    IBComptroller public bComptroller;
    address public rewards;
    ERC20 public override stablecoin;

    function initialize(
        address _bToken,
        address _bComptroller,
        address _rewards,
        address _rescuer,
        address _stablecoin
    ) external initializer {
        __MoneyMarket_init(_rescuer);

        // Verify input addresses
        require(
            _bToken.isContract() &&
                _bComptroller.isContract() &&
                _rewards != address(0) &&
                _stablecoin.isContract(),
            "BProtocolMarket: Invalid input address"
        );

        bToken = IBToken(_bToken);
        bComptroller = IBComptroller(_bComptroller);
        rewards = _rewards;
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external override onlyOwner {
        require(amount > 0, "BProtocolMarket: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Deposit `amount` stablecoin into bToken
        stablecoin.safeIncreaseAllowance(address(bToken), amount);
        require(
            bToken.mint(amount) == ERRCODE_OK,
            "BProtocolMarket: Failed to mint bTokens"
        );
    }

    function withdraw(uint256 amountInUnderlying)
        external
        override
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "BProtocolMarket: amountInUnderlying is 0"
        );

        // Withdraw `amountInUnderlying` stablecoin from bToken
        require(
            bToken.redeemUnderlying(amountInUnderlying) == ERRCODE_OK,
            "BProtocolMarket: Failed to redeem"
        );

        // Transfer `amountInUnderlying` stablecoin to `msg.sender`
        stablecoin.safeTransfer(msg.sender, amountInUnderlying);

        return amountInUnderlying;
    }

    function claimRewards() external override {
        ERC20 comp = ERC20(bComptroller.registry().comp());
        uint256 beforeBalance = comp.balanceOf(address(this));
        bComptroller.claimComp(address(this));
        comp.safeTransfer(
            rewards,
            comp.balanceOf(address(this)) - beforeBalance
        );
    }

    /**
        Param setters
     */
    function setRewards(address newValue) external override onlyOwner {
        require(newValue != address(0), "BProtocolMarket: 0 address");
        rewards = newValue;
        emit ESetParamAddress(msg.sender, "rewards", newValue);
    }

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(address token, address target)
        internal
        view
        override
    {
        super._authorizeRescue(token, target);
        require(token != address(bToken), "BProtocolMarket: no steal");
    }

    function _totalValue(uint256 currentIncomeIndex)
        internal
        view
        override
        returns (uint256)
    {
        uint256 bTokenBalance = bToken.balanceOf(address(this));
        return bTokenBalance.mul(currentIncomeIndex);
    }

    function _incomeIndex() internal override returns (uint256 index) {
        index = bToken.exchangeRateCurrent();
        require(index > 0, "BProtocolMarket: BAD_INDEX");
    }

    uint256[46] private __gap;
}

File 60 of 82 : IBComptroller.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import "./IRegistry.sol";

// B.Protocol BComptroller interface
interface IBComptroller {
    function claimComp(address holder) external;

    function registry() external returns (IRegistry);
}

File 61 of 82 : IBToken.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

// B.Protocol bToken interface
interface IBToken {
    function mint(uint256 mintAmount) external returns (uint256);

    function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

    function balanceOf(address owner) external view returns (uint256);

    function exchangeRateCurrent() external returns (uint256);
}

File 62 of 82 : IRegistry.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

// B.Protocol Registry interface
interface IRegistry {
    function comp() external returns (address);
}

File 63 of 82 : CompoundERC20Market.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "../../libs/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {MoneyMarket} from "../MoneyMarket.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {ICERC20} from "./imports/ICERC20.sol";
import {IComptroller} from "./imports/IComptroller.sol";

contract CompoundERC20Market is MoneyMarket {
    using PRBMathUD60x18 for uint256;
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;

    uint256 internal constant ERRCODE_OK = 0;

    ICERC20 public cToken;
    IComptroller public comptroller;
    address public rewards;
    ERC20 public override stablecoin;

    function initialize(
        address _cToken,
        address _comptroller,
        address _rewards,
        address _rescuer,
        address _stablecoin
    ) external initializer {
        __MoneyMarket_init(_rescuer);

        // Verify input addresses
        require(
            _cToken.isContract() &&
                _comptroller.isContract() &&
                _rewards != address(0) &&
                _stablecoin.isContract(),
            "CompoundERC20Market: Invalid input address"
        );

        cToken = ICERC20(_cToken);
        comptroller = IComptroller(_comptroller);
        rewards = _rewards;
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external override onlyOwner {
        require(amount > 0, "CompoundERC20Market: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Deposit `amount` stablecoin into cToken
        stablecoin.safeIncreaseAllowance(address(cToken), amount);
        require(
            cToken.mint(amount) == ERRCODE_OK,
            "CompoundERC20Market: Failed to mint cTokens"
        );
    }

    function withdraw(uint256 amountInUnderlying)
        external
        override
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "CompoundERC20Market: amountInUnderlying is 0"
        );

        // Withdraw `amountInUnderlying` stablecoin from cToken
        require(
            cToken.redeemUnderlying(amountInUnderlying) == ERRCODE_OK,
            "CompoundERC20Market: Failed to redeem"
        );

        // Transfer `amountInUnderlying` stablecoin to `msg.sender`
        stablecoin.safeTransfer(msg.sender, amountInUnderlying);

        return amountInUnderlying;
    }

    function claimRewards() external override {
        ERC20 comp = ERC20(comptroller.getCompAddress());
        uint256 beforeBalance = comp.balanceOf(address(this));
        comptroller.claimComp(address(this));
        comp.safeTransfer(
            rewards,
            comp.balanceOf(address(this)) - beforeBalance
        );
    }

    /**
        Param setters
     */
    function setRewards(address newValue) external override onlyOwner {
        require(newValue != address(0), "CompoundERC20Market: 0 address");
        rewards = newValue;
        emit ESetParamAddress(msg.sender, "rewards", newValue);
    }

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(address token, address target)
        internal
        view
        override
    {
        super._authorizeRescue(token, target);
        require(token != address(cToken), "CompoundERC20Market: no steal");
    }

    function _totalValue(uint256 currentIncomeIndex)
        internal
        view
        override
        returns (uint256)
    {
        uint256 cTokenBalance = cToken.balanceOf(address(this));
        return cTokenBalance.mul(currentIncomeIndex);
    }

    function _incomeIndex() internal override returns (uint256 index) {
        index = cToken.exchangeRateCurrent();
        require(index > 0, "CompoundERC20Market: BAD_INDEX");
    }

    uint256[46] private __gap;
}

File 64 of 82 : ICERC20.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

// Compound finance ERC20 market interface
// Documentation: https://compound.finance/docs/ctokens
interface ICERC20 {
    function transfer(address dst, uint256 amount) external returns (bool);

    function transferFrom(
        address src,
        address dst,
        uint256 amount
    ) external returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function balanceOf(address owner) external view returns (uint256);

    function balanceOfUnderlying(address owner) external returns (uint256);

    function getAccountSnapshot(address account)
        external
        view
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        );

    function borrowRatePerBlock() external view returns (uint256);

    function supplyRatePerBlock() external view returns (uint256);

    function totalBorrowsCurrent() external returns (uint256);

    function borrowBalanceCurrent(address account) external returns (uint256);

    function borrowBalanceStored(address account)
        external
        view
        returns (uint256);

    function exchangeRateCurrent() external returns (uint256);

    function exchangeRateStored() external view returns (uint256);

    function getCash() external view returns (uint256);

    function accrueInterest() external returns (uint256);

    function seize(
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external returns (uint256);

    function mint(uint256 mintAmount) external returns (uint256);

    function redeem(uint256 redeemTokens) external returns (uint256);

    function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

    function borrow(uint256 borrowAmount) external returns (uint256);

    function repayBorrow(uint256 repayAmount) external returns (uint256);

    function repayBorrowBehalf(address borrower, uint256 repayAmount)
        external
        returns (uint256);

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        address cTokenCollateral
    ) external returns (uint256);
}

File 65 of 82 : IComptroller.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

// Compound finance Comptroller interface
// Documentation: https://compound.finance/docs/comptroller
interface IComptroller {
    function claimComp(address holder) external;

    function getCompAddress() external view returns (address);
}

File 66 of 82 : CreamERC20Market.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "../../libs/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {MoneyMarket} from "../MoneyMarket.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {ICrERC20} from "./imports/ICrERC20.sol";

contract CreamERC20Market is MoneyMarket {
    using PRBMathUD60x18 for uint256;
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;

    uint256 internal constant ERRCODE_OK = 0;

    ICrERC20 public cToken;
    ERC20 public override stablecoin;

    function initialize(
        address _cToken,
        address _rescuer,
        address _stablecoin
    ) external initializer {
        __MoneyMarket_init(_rescuer);
        // Verify input addresses
        require(
            _cToken.isContract() && _stablecoin.isContract(),
            "CreamERC20Market: An input address is not a contract"
        );

        cToken = ICrERC20(_cToken);
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external override onlyOwner {
        require(amount > 0, "CreamERC20Market: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Deposit `amount` stablecoin into cToken
        stablecoin.safeIncreaseAllowance(address(cToken), amount);
        require(
            cToken.mint(amount) == ERRCODE_OK,
            "CreamERC20Market: Failed to mint cTokens"
        );
    }

    function withdraw(uint256 amountInUnderlying)
        external
        override
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "CreamERC20Market: amountInUnderlying is 0"
        );

        // Withdraw `amountInUnderlying` stablecoin from cToken
        require(
            cToken.redeemUnderlying(amountInUnderlying) == ERRCODE_OK,
            "CreamERC20Market: Failed to redeem"
        );

        // Transfer `amountInUnderlying` stablecoin to `msg.sender`
        stablecoin.safeTransfer(msg.sender, amountInUnderlying);

        return amountInUnderlying;
    }

    function claimRewards() external override {}

    function setRewards(address newValue) external override onlyOwner {}

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(address token, address target)
        internal
        view
        override
    {
        super._authorizeRescue(token, target);
        require(token != address(cToken), "CreamERC20Market: no steal");
    }

    function _totalValue(uint256 currentIncomeIndex)
        internal
        view
        override
        returns (uint256)
    {
        uint256 cTokenBalance = cToken.balanceOf(address(this));
        return cTokenBalance.mul(currentIncomeIndex);
    }

    function _incomeIndex() internal override returns (uint256 index) {
        index = cToken.exchangeRateCurrent();
        require(index > 0, "CreamERC20Market: BAD_INDEX");
    }

    uint256[48] private __gap;
}

File 67 of 82 : ICrERC20.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

// Cream ERC20 market interface
interface ICrERC20 {
    function transfer(address dst, uint256 amount) external returns (bool);

    function transferFrom(
        address src,
        address dst,
        uint256 amount
    ) external returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function balanceOf(address owner) external view returns (uint256);

    function balanceOfUnderlying(address owner) external returns (uint256);

    function getAccountSnapshot(address account)
        external
        view
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        );

    function borrowRatePerBlock() external view returns (uint256);

    function supplyRatePerBlock() external view returns (uint256);

    function totalBorrowsCurrent() external returns (uint256);

    function borrowBalanceCurrent(address account) external returns (uint256);

    function borrowBalanceStored(address account)
        external
        view
        returns (uint256);

    function exchangeRateCurrent() external returns (uint256);

    function exchangeRateStored() external view returns (uint256);

    function getCash() external view returns (uint256);

    function accrueInterest() external returns (uint256);

    function seize(
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external returns (uint256);

    function mint(uint256 mintAmount) external returns (uint256);

    function redeem(uint256 redeemTokens) external returns (uint256);

    function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

    function borrow(uint256 borrowAmount) external returns (uint256);

    function repayBorrow(uint256 repayAmount) external returns (uint256);

    function repayBorrowBehalf(address borrower, uint256 repayAmount)
        external
        returns (uint256);

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        address cTokenCollateral
    ) external returns (uint256);
}

File 68 of 82 : HarvestMarket.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "../../libs/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {MoneyMarket} from "../MoneyMarket.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {HarvestVault} from "./imports/HarvestVault.sol";
import {HarvestStaking} from "./imports/HarvestStaking.sol";

contract HarvestMarket is MoneyMarket {
    using PRBMathUD60x18 for uint256;
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;

    HarvestVault public vault;
    address public rewards;
    HarvestStaking public stakingPool;
    ERC20 public override stablecoin;

    function initialize(
        address _vault,
        address _rewards,
        address _stakingPool,
        address _rescuer,
        address _stablecoin
    ) external initializer {
        __MoneyMarket_init(_rescuer);

        // Verify input addresses
        require(
            _vault.isContract() &&
                _rewards != address(0) &&
                _stakingPool.isContract() &&
                _stablecoin.isContract(),
            "HarvestMarket: Invalid input address"
        );

        vault = HarvestVault(_vault);
        rewards = _rewards;
        stakingPool = HarvestStaking(_stakingPool);
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external override onlyOwner {
        require(amount > 0, "HarvestMarket: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Approve `amount` stablecoin to vault
        stablecoin.safeIncreaseAllowance(address(vault), amount);

        // Deposit `amount` stablecoin to vault
        vault.deposit(amount);

        // Stake vault token balance into staking pool
        uint256 vaultShareBalance = vault.balanceOf(address(this));
        vault.approve(address(stakingPool), vaultShareBalance);
        stakingPool.stake(vaultShareBalance);
    }

    function withdraw(uint256 amountInUnderlying)
        external
        override
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "HarvestMarket: amountInUnderlying is 0"
        );

        // Withdraw `amountInShares` shares from vault
        uint256 sharePrice = vault.getPricePerFullShare();
        uint256 amountInShares = amountInUnderlying.div(sharePrice);
        if (amountInShares > 0) {
            stakingPool.withdraw(amountInShares);
            vault.withdraw(amountInShares);
        }

        // Transfer stablecoin to `msg.sender`
        actualAmountWithdrawn = stablecoin.balanceOf(address(this));
        if (actualAmountWithdrawn > 0) {
            stablecoin.safeTransfer(msg.sender, actualAmountWithdrawn);
        }
    }

    function claimRewards() external override {
        stakingPool.getReward();
        ERC20 rewardToken = ERC20(stakingPool.rewardToken());
        rewardToken.safeTransfer(rewards, rewardToken.balanceOf(address(this)));
    }

    /**
        Param setters
     */
    function setRewards(address newValue) external override onlyOwner {
        require(newValue != address(0), "HarvestMarket: 0 address");
        rewards = newValue;
        emit ESetParamAddress(msg.sender, "rewards", newValue);
    }

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(address token, address target)
        internal
        view
        override
    {
        super._authorizeRescue(token, target);
        require(token != address(stakingPool), "HarvestMarket: no steal");
    }

    function _totalValue(uint256 currentIncomeIndex)
        internal
        view
        override
        returns (uint256)
    {
        // not including vault token balance
        // because it should be 0 during normal operation
        // if tokens are sent to contract by mistake
        // they will be rescued
        uint256 shareBalance = stakingPool.balanceOf(address(this));
        return shareBalance.mul(currentIncomeIndex);
    }

    function _incomeIndex() internal view override returns (uint256 index) {
        index = vault.getPricePerFullShare();
        require(index > 0, "HarvestMarket: BAD_INDEX");
    }

    uint256[46] private __gap;
}

File 69 of 82 : HarvestStaking.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

interface HarvestStaking {
    function stake(uint256 amount) external;

    function withdraw(uint256 amount) external;

    function getReward() external;

    function rewardToken() external returns (address);

    function balanceOf(address account) external view returns (uint256);
}

File 70 of 82 : HarvestVault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

interface HarvestVault {
    function deposit(uint256) external;

    function withdraw(uint256) external;

    function getPricePerFullShare() external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

File 71 of 82 : YVaultMarket.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {SafeERC20} from "../../libs/SafeERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {MoneyMarket} from "../MoneyMarket.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {Vault} from "./imports/Vault.sol";

contract YVaultMarket is MoneyMarket {
    using PRBMathUD60x18 for uint256;
    using SafeERC20 for ERC20;
    using AddressUpgradeable for address;

    Vault public vault;
    ERC20 public override stablecoin;

    function initialize(
        address _vault,
        address _rescuer,
        address _stablecoin
    ) external initializer {
        __MoneyMarket_init(_rescuer);

        // Verify input addresses
        require(
            _vault.isContract() && _stablecoin.isContract(),
            "YVaultMarket: An input address is not a contract"
        );

        vault = Vault(_vault);
        stablecoin = ERC20(_stablecoin);
    }

    function deposit(uint256 amount) external override onlyOwner {
        require(amount > 0, "YVaultMarket: amount is 0");

        // Transfer `amount` stablecoin from `msg.sender`
        stablecoin.safeTransferFrom(msg.sender, address(this), amount);

        // Approve `amount` stablecoin to vault
        stablecoin.safeIncreaseAllowance(address(vault), amount);

        // Deposit `amount` stablecoin to vault
        vault.deposit(amount);
    }

    function withdraw(uint256 amountInUnderlying)
        external
        override
        onlyOwner
        returns (uint256 actualAmountWithdrawn)
    {
        require(
            amountInUnderlying > 0,
            "YVaultMarket: amountInUnderlying is 0"
        );

        // Withdraw `amountInShares` shares from vault
        uint256 sharePrice = vault.pricePerShare();
        uint256 amountInShares = amountInUnderlying.div(sharePrice);
        if (amountInShares > 0) {
            // maxLoss = 0
            actualAmountWithdrawn = vault.withdraw(
                amountInShares,
                msg.sender,
                0
            );
        }
    }

    function claimRewards() external override {}

    function setRewards(address newValue) external override {}

    /**
        @dev IMPORTANT MUST READ
        This function is for restricting unauthorized accounts from taking funds
        and ensuring only tokens not used by the MoneyMarket can be rescued.
        IF YOU DON'T GET IT RIGHT YOU WILL LOSE PEOPLE'S MONEY
        MAKE SURE YOU DO ALL OF THE FOLLOWING
        1) You MUST override it in a MoneyMarket implementation.
        2) You MUST make `super._authorizeRescue(token, target);` the first line of your overriding function.
        3) You MUST revert during a call to this function if a token used by the MoneyMarket is being rescued.
        4) You SHOULD look at how existing MoneyMarkets do it as an example.
     */
    function _authorizeRescue(address token, address target)
        internal
        view
        override
    {
        super._authorizeRescue(token, target);
        require(token != address(vault), "YVaultMarket: no steal");
    }

    function _totalValue(uint256 currentIncomeIndex)
        internal
        view
        override
        returns (uint256)
    {
        uint256 shareBalance = vault.balanceOf(address(this));
        return shareBalance.mul(currentIncomeIndex);
    }

    function _incomeIndex() internal view override returns (uint256 index) {
        index = vault.pricePerShare();
        require(index > 0, "YVaultMarket: BAD_INDEX");
    }

    uint256[48] private __gap;
}

File 72 of 82 : Vault.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

interface Vault {
    function deposit(uint256 amount) external;

    function withdraw(
        uint256 shareAmount,
        address recipient,
        uint256 maxLoss
    ) external returns (uint256);

    function pricePerShare() external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);
}

File 73 of 82 : MPHMinter.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    AccessControlUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {
    AddressUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {Vesting02} from "./Vesting02.sol";
import {FundingMultitoken} from "../tokens/FundingMultitoken.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";
import {DInterest} from "../DInterest.sol";
import {MPHToken} from "./MPHToken.sol";

contract MPHMinter is AccessControlUpgradeable {
    using AddressUpgradeable for address;
    using PRBMathUD60x18 for uint256;

    uint256 internal constant PRECISION = 10**18;
    bytes32 public constant WHITELISTER_ROLE = keccak256("WHITELISTER_ROLE");
    bytes32 public constant WHITELISTED_POOL_ROLE =
        keccak256("WHITELISTED_POOL_ROLE");
    bytes32 public constant LEGACY_MINTER_ROLE =
        keccak256("LEGACY_MINTER_ROLE");

    event ESetParamAddress(
        address indexed sender,
        string indexed paramName,
        address newValue
    );
    event ESetParamUint(
        address indexed sender,
        string indexed paramName,
        address pool,
        uint256 newValue
    );
    event MintDepositorReward(
        address indexed sender,
        address indexed to,
        uint256 depositorReward
    );
    event MintFunderReward(
        address indexed sender,
        address indexed to,
        uint256 funderReward
    );

    /**
        @notice The multiplier applied when minting MPH for a pool's depositor reward.
                Unit is MPH-wei per depositToken-wei per second. (wei here is the smallest decimal place)
                Scaled by 10^18.
                NOTE: The depositToken's decimals matter!
     */
    mapping(address => uint256) public poolDepositorRewardMintMultiplier;
    /**
        @notice The multiplier applied when minting MPH for a pool's funder reward.
                Unit is MPH-wei per depositToken-wei. (wei here is the smallest decimal place)
                Scaled by 10^18.
                NOTE: The depositToken's decimals matter!
     */
    mapping(address => uint256) public poolFunderRewardMultiplier;
    /**
        @notice Multiplier used for calculating dev reward
     */
    uint256 public devRewardMultiplier;
    /**
        @notice Multiplier used for calculating gov reward
     */
    uint256 public govRewardMultiplier;

    /**
        External contracts
     */
    MPHToken public mph;
    address public govTreasury;
    address public devWallet;
    Vesting02 public vesting02;

    function __MPHMinter_init(
        address _mph,
        address _govTreasury,
        address _devWallet,
        address _vesting02,
        uint256 _devRewardMultiplier,
        uint256 _govRewardMultiplier
    ) internal initializer {
        __AccessControl_init();
        __MPHMinter_init_unchained(
            _mph,
            _govTreasury,
            _devWallet,
            _vesting02,
            _devRewardMultiplier,
            _govRewardMultiplier
        );
    }

    function __MPHMinter_init_unchained(
        address _mph,
        address _govTreasury,
        address _devWallet,
        address _vesting02,
        uint256 _devRewardMultiplier,
        uint256 _govRewardMultiplier
    ) internal initializer {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        // only accounts with the whitelister role can whitelist pools
        _setRoleAdmin(WHITELISTED_POOL_ROLE, WHITELISTER_ROLE);

        mph = MPHToken(_mph);
        govTreasury = _govTreasury;
        devWallet = _devWallet;
        vesting02 = Vesting02(_vesting02);
        devRewardMultiplier = _devRewardMultiplier;
        govRewardMultiplier = _govRewardMultiplier;
    }

    function initialize(
        address _mph,
        address _govTreasury,
        address _devWallet,
        address _vesting02,
        uint256 _devRewardMultiplier,
        uint256 _govRewardMultiplier
    ) external initializer {
        __MPHMinter_init(
            _mph,
            _govTreasury,
            _devWallet,
            _vesting02,
            _devRewardMultiplier,
            _govRewardMultiplier
        );
    }

    function createVestForDeposit(address account, uint64 depositID)
        external
        onlyRole(WHITELISTED_POOL_ROLE)
    {
        vesting02.createVestForDeposit(
            account,
            msg.sender,
            depositID,
            poolDepositorRewardMintMultiplier[msg.sender]
        );
    }

    function updateVestForDeposit(
        uint64 depositID,
        uint256 currentDepositAmount,
        uint256 depositAmount
    ) external onlyRole(WHITELISTED_POOL_ROLE) {
        vesting02.updateVestForDeposit(
            msg.sender,
            depositID,
            currentDepositAmount,
            depositAmount,
            poolDepositorRewardMintMultiplier[msg.sender]
        );
    }

    function mintVested(address account, uint256 amount)
        external
        returns (uint256 mintedAmount)
    {
        require(msg.sender == address(vesting02), "MPHMinter: not vesting02");
        if (mph.owner() != address(this)) {
            // not the owner of the MPH token, cannot mint
            return 0;
        }
        if (amount > 0) {
            mph.ownerMint(account, amount);
        }
        uint256 devReward = amount.mul(devRewardMultiplier);
        if (devReward > 0) {
            mph.ownerMint(devWallet, devReward);
        }
        uint256 govReward = amount.mul(govRewardMultiplier);
        if (govReward > 0) {
            mph.ownerMint(govTreasury, govReward);
        }
        return amount;
    }

    function distributeFundingRewards(uint64 fundingID, uint256 interestAmount)
        external
        onlyRole(WHITELISTED_POOL_ROLE)
    {
        if (interestAmount == 0 || mph.owner() != address(this)) {
            return;
        }
        uint256 mintMPHAmount =
            interestAmount.mul(poolFunderRewardMultiplier[msg.sender]);
        if (mintMPHAmount == 0) {
            return;
        }
        FundingMultitoken fundingMultitoken =
            DInterest(msg.sender).fundingMultitoken();
        mph.ownerMint(address(this), mintMPHAmount);
        mph.increaseAllowance(address(fundingMultitoken), mintMPHAmount);
        fundingMultitoken.distributeDividends(
            fundingID,
            address(mph),
            mintMPHAmount
        );

        uint256 devReward = mintMPHAmount.mul(devRewardMultiplier);
        if (devReward > 0) {
            mph.ownerMint(devWallet, devReward);
        }
        uint256 govReward = mintMPHAmount.mul(govRewardMultiplier);
        if (govReward > 0) {
            mph.ownerMint(govTreasury, govReward);
        }
    }

    /**
        @dev Used for supporting the v2 MPHMinterLegacy
     */
    function legacyMintFunderReward(
        address pool,
        address to,
        uint256 depositAmount,
        uint256 fundingCreationTimestamp,
        uint256 maturationTimestamp,
        uint256, /*interestPayoutAmount*/
        bool early
    ) external onlyRole(LEGACY_MINTER_ROLE) returns (uint256) {
        require(hasRole(WHITELISTED_POOL_ROLE, pool), "MPHMinter: not pool");

        if (mph.owner() != address(this)) {
            // not the owner of the MPH token, cannot mint
            return 0;
        }

        uint256 funderReward;
        uint256 devReward;
        uint256 govReward;
        if (!early) {
            funderReward = maturationTimestamp > fundingCreationTimestamp
                ? depositAmount *
                    (maturationTimestamp - fundingCreationTimestamp).mul(
                        poolFunderRewardMultiplier[pool]
                    )
                : 0;
            devReward = funderReward.mul(devRewardMultiplier);
            govReward = funderReward.mul(govRewardMultiplier);
        } else {
            return 0;
        }

        // mint and vest funder reward
        if (funderReward > 0) {
            mph.ownerMint(to, funderReward);
        }
        if (devReward > 0) {
            mph.ownerMint(devWallet, devReward);
        }
        if (govReward > 0) {
            mph.ownerMint(govTreasury, govReward);
        }

        return funderReward;
    }

    /**
        Param setters
     */
    function setPoolDepositorRewardMintMultiplier(
        address pool,
        uint256 newMultiplier
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(pool.isContract(), "MPHMinter: pool not contract");
        poolDepositorRewardMintMultiplier[pool] = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "poolDepositorRewardMintMultiplier",
            pool,
            newMultiplier
        );
    }

    function setPoolFunderRewardMultiplier(address pool, uint256 newMultiplier)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(pool.isContract(), "MPHMinter: pool not contract");
        poolFunderRewardMultiplier[pool] = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "poolFunderRewardMultiplier",
            pool,
            newMultiplier
        );
    }

    function setDevRewardMultiplier(uint256 newMultiplier)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(newMultiplier <= PRECISION, "MPHMinter: invalid multiplier");
        devRewardMultiplier = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "devRewardMultiplier",
            address(0),
            newMultiplier
        );
    }

    function setGovRewardMultiplier(uint256 newMultiplier)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(newMultiplier <= PRECISION, "MPHMinter: invalid multiplier");
        govRewardMultiplier = newMultiplier;
        emit ESetParamUint(
            msg.sender,
            "govRewardMultiplier",
            address(0),
            newMultiplier
        );
    }

    function setGovTreasury(address newValue)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(newValue != address(0), "MPHMinter: 0 address");
        govTreasury = newValue;
        emit ESetParamAddress(msg.sender, "govTreasury", newValue);
    }

    function setDevWallet(address newValue)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(newValue != address(0), "MPHMinter: 0 address");
        devWallet = newValue;
        emit ESetParamAddress(msg.sender, "devWallet", newValue);
    }

    function setMPHTokenOwner(address newValue)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(newValue != address(0), "MPHMinter: 0 address");
        mph.transferOwnership(newValue);
        emit ESetParamAddress(msg.sender, "mphTokenOwner", newValue);
    }

    function setMPHTokenOwnerToZero() external onlyRole(DEFAULT_ADMIN_ROLE) {
        mph.renounceOwnership();
        emit ESetParamAddress(msg.sender, "mphTokenOwner", address(0));
    }

    function setVesting02(address newValue)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)
    {
        require(newValue.isContract(), "MPHMinter: not contract");
        vesting02 = Vesting02(newValue);
        emit ESetParamAddress(msg.sender, "vesting02", newValue);
    }

    uint256[42] private __gap;
}

File 74 of 82 : MPHToken.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    ERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {
    ERC20BurnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import {
    OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MPHToken is
    ERC20Upgradeable,
    ERC20BurnableUpgradeable,
    OwnableUpgradeable
{
    function initialize() external initializer {
        __Ownable_init();
        __ERC20Burnable_init();
        __ERC20_init("88mph.app", "MPH");
    }

    function ownerMint(address account, uint256 amount)
        external
        onlyOwner
        returns (bool)
    {
        _mint(account, amount);
        return true;
    }
}

File 75 of 82 : Vesting02.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../libs/SafeERC20.sol";
import {
    ERC721URIStorageUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import {BoringOwnable} from "../libs/BoringOwnable.sol";
import {
    MathUpgradeable
} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import {MPHMinter} from "./MPHMinter.sol";
import {DInterest} from "../DInterest.sol";
import {PRBMathUD60x18} from "prb-math/contracts/PRBMathUD60x18.sol";

contract Vesting02 is ERC721URIStorageUpgradeable, BoringOwnable {
    using SafeERC20 for IERC20;
    using PRBMathUD60x18 for uint256;

    uint256 internal constant PRECISION = 10**18;

    struct Vest {
        address pool;
        uint64 depositID;
        uint64 lastUpdateTimestamp;
        uint256 accumulatedAmount;
        uint256 withdrawnAmount;
        uint256 vestAmountPerStablecoinPerSecond;
    }
    Vest[] public vestList;
    mapping(address => mapping(uint64 => uint64)) public depositIDToVestID;

    MPHMinter public mphMinter;
    IERC20 public token;
    string internal _contractURI;
    string internal __baseURI;

    event ECreateVest(
        address indexed to,
        address indexed pool,
        uint64 depositID,
        uint64 vestID,
        uint256 vestAmountPerStablecoinPerSecond
    );
    event EUpdateVest(
        uint64 indexed vestID,
        address poolAddress,
        uint64 depositID,
        uint256 currentDepositAmount,
        uint256 depositAmount,
        uint256 vestAmountPerStablecoinPerSecond
    );
    event EWithdraw(
        address indexed sender,
        uint64 indexed vestID,
        uint256 withdrawnAmount
    );
    event ESetMPHMinter(address newValue);
    event EBoost(
        uint64 indexed vestID,
        uint256 vestAmountPerStablecoinPerSecond
    );

    function initialize(
        address _token,
        string calldata tokenName,
        string calldata tokenSymbol
    ) external initializer {
        __Ownable_init();
        __ERC721_init(tokenName, tokenSymbol);

        token = IERC20(_token);
    }

    function setMPHMinter(address newValue) external onlyOwner {
        require(newValue != address(0), "Vesting02: 0 address");
        mphMinter = MPHMinter(newValue);
        emit ESetMPHMinter(newValue);
    }

    /**
        MPHMinter only functions
     */

    function createVestForDeposit(
        address to,
        address pool,
        uint64 depositID,
        uint256 vestAmountPerStablecoinPerSecond
    ) external returns (uint64 vestID) {
        require(
            address(msg.sender) == address(mphMinter),
            "Vesting02: not minter"
        );

        // create vest object
        require(block.timestamp <= type(uint64).max, "Vesting02: OVERFLOW");
        vestList.push(
            Vest({
                pool: pool,
                depositID: depositID,
                lastUpdateTimestamp: uint64(block.timestamp),
                accumulatedAmount: 0,
                withdrawnAmount: 0,
                vestAmountPerStablecoinPerSecond: vestAmountPerStablecoinPerSecond
            })
        );
        require(vestList.length <= type(uint64).max, "Vesting02: OVERFLOW");
        vestID = uint64(vestList.length); // 1-indexed
        depositIDToVestID[pool][depositID] = vestID;

        // mint NFT
        _safeMint(to, vestID);

        emit ECreateVest(
            to,
            pool,
            depositID,
            vestID,
            vestAmountPerStablecoinPerSecond
        );
    }

    function updateVestForDeposit(
        address poolAddress,
        uint64 depositID,
        uint256 currentDepositAmount,
        uint256 depositAmount,
        uint256 vestAmountPerStablecoinPerSecond
    ) external {
        require(
            address(msg.sender) == address(mphMinter),
            "Vesting02: not minter"
        );

        uint64 vestID = depositIDToVestID[poolAddress][depositID];
        Vest storage vestEntry = _getVest(vestID);
        DInterest pool = DInterest(poolAddress);
        DInterest.Deposit memory depositEntry =
            pool.getDeposit(vestEntry.depositID);
        uint256 currentTimestamp =
            MathUpgradeable.min(
                block.timestamp,
                depositEntry.maturationTimestamp
            );
        vestEntry.accumulatedAmount += (currentDepositAmount *
            (currentTimestamp - vestEntry.lastUpdateTimestamp))
            .mul(vestEntry.vestAmountPerStablecoinPerSecond);
        require(block.timestamp <= type(uint64).max, "Vesting02: OVERFLOW");
        vestEntry.lastUpdateTimestamp = uint64(block.timestamp);
        vestEntry.vestAmountPerStablecoinPerSecond =
            (vestEntry.vestAmountPerStablecoinPerSecond *
                currentDepositAmount +
                vestAmountPerStablecoinPerSecond *
                depositAmount) /
            (currentDepositAmount + depositAmount);

        emit EUpdateVest(
            vestID,
            poolAddress,
            depositID,
            currentDepositAmount,
            depositAmount,
            vestAmountPerStablecoinPerSecond
        );
    }

    /**
        Public action functions
     */

    function withdraw(uint64 vestID)
        external
        returns (uint256 withdrawnAmount)
    {
        return _withdraw(vestID);
    }

    function multiWithdraw(uint64[] memory vestIDList) external {
        for (uint256 i = 0; i < vestIDList.length; i++) {
            _withdraw(vestIDList[i]);
        }
    }

    function _withdraw(uint64 vestID)
        internal
        returns (uint256 withdrawnAmount)
    {
        require(ownerOf(vestID) == msg.sender, "Vesting02: not owner");

        // compute withdrawable amount
        withdrawnAmount = _getVestWithdrawableAmount(vestID);
        if (withdrawnAmount == 0) {
            return 0;
        }

        // update vest object
        Vest storage vestEntry = _getVest(vestID);
        vestEntry.withdrawnAmount += withdrawnAmount;

        // mint tokens to vest recipient
        mphMinter.mintVested(msg.sender, withdrawnAmount);

        emit EWithdraw(msg.sender, vestID, withdrawnAmount);
    }

    /**
        Public getter functions
     */

    function getVestWithdrawableAmount(uint64 vestID)
        external
        view
        returns (uint256)
    {
        return _getVestWithdrawableAmount(vestID);
    }

    function _getVestWithdrawableAmount(uint64 vestID)
        internal
        view
        returns (uint256 withdrawableAmount)
    {
        // read vest data
        Vest memory vestEntry = _getVest(vestID);
        DInterest pool = DInterest(vestEntry.pool);
        DInterest.Deposit memory depositEntry =
            pool.getDeposit(vestEntry.depositID);

        // compute vested amount
        uint256 currentTimestamp =
            MathUpgradeable.min(
                block.timestamp,
                depositEntry.maturationTimestamp
            );
        if (currentTimestamp < vestEntry.lastUpdateTimestamp) {
            return vestEntry.accumulatedAmount - vestEntry.withdrawnAmount;
        }
        uint256 depositAmount =
            depositEntry.virtualTokenTotalSupply.div(
                PRECISION + depositEntry.interestRate
            );
        return
            vestEntry.accumulatedAmount +
            (depositAmount * (currentTimestamp - vestEntry.lastUpdateTimestamp))
                .mul(vestEntry.vestAmountPerStablecoinPerSecond) -
            vestEntry.withdrawnAmount;
    }

    function getVest(uint64 vestID) external view returns (Vest memory) {
        return _getVest(vestID);
    }

    function _getVest(uint64 vestID) internal view returns (Vest storage) {
        return vestList[vestID - 1];
    }

    function numVests() external view returns (uint256) {
        return vestList.length;
    }

    /**
        NFT metadata
     */

    function contractURI() external view returns (string memory) {
        return _contractURI;
    }

    function _baseURI() internal view override returns (string memory) {
        return __baseURI;
    }

    function setContractURI(string calldata newURI) external onlyOwner {
        _contractURI = newURI;
    }

    function setTokenURI(uint256 tokenId, string calldata newURI) external {
        require(ownerOf(tokenId) == msg.sender, "Vesting02: not token owner");
        _setTokenURI(tokenId, newURI);
    }

    /**
        Owner functions
     */

    function setBaseURI(string calldata newURI) external onlyOwner {
        __baseURI = newURI;
    }

    function boost(uint64 vestID, uint256 vestAmountPerStablecoinPerSecond)
        external
        onlyOwner
    {
        _getVest(vestID)
            .vestAmountPerStablecoinPerSecond = vestAmountPerStablecoinPerSecond;
        emit EBoost(vestID, vestAmountPerStablecoinPerSecond);
    }

    uint256[44] private __gap;
}

File 76 of 82 : FundingMultitoken.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {ERC1155Upgradeable} from "../libs/ERC1155Upgradeable.sol";
import {ERC1155DividendToken} from "../libs/ERC1155DividendToken.sol";
import {WrappedERC1155Token} from "../libs/WrappedERC1155Token.sol";

contract FundingMultitoken is ERC1155DividendToken, WrappedERC1155Token {
    bytes32 public constant DIVIDEND_ROLE = keccak256("DIVIDEND_ROLE");

    function __FundingMultitoken_init(
        address admin,
        string calldata uri,
        address[] memory dividendTokens,
        address _wrapperTemplate,
        bool _deployWrapperOnMint,
        string memory _baseName,
        string memory _baseSymbol,
        uint8 _decimals
    ) internal initializer {
        __ERC1155Base_init(admin, uri);
        __ERC1155DividendToken_init_unchained(dividendTokens);
        __WrappedERC1155Token_init_unchained(
            _wrapperTemplate,
            _deployWrapperOnMint,
            _baseName,
            _baseSymbol,
            _decimals
        );
        __FundingMultitoken_init_unchained(admin);
    }

    function __FundingMultitoken_init_unchained(address admin)
        internal
        initializer
    {
        // DIVIDEND_ROLE is managed by itself
        _setupRole(DIVIDEND_ROLE, admin);
        _setRoleAdmin(DIVIDEND_ROLE, DIVIDEND_ROLE);
    }

    function initialize(
        address admin,
        string calldata uri,
        address[] calldata dividendTokens,
        address _wrapperTemplate,
        bool _deployWrapperOnMint,
        string memory _baseName,
        string memory _baseSymbol,
        uint8 _decimals
    ) external virtual initializer {
        __FundingMultitoken_init(
            admin,
            uri,
            dividendTokens,
            _wrapperTemplate,
            _deployWrapperOnMint,
            _baseName,
            _baseSymbol,
            _decimals
        );
    }

    function distributeDividends(
        uint256 tokenID,
        address dividendToken,
        uint256 amount
    ) external {
        require(
            hasRole(DIVIDEND_ROLE, _msgSender()),
            "FundingMultitoken: must have dividend role"
        );
        _distributeDividends(tokenID, dividendToken, amount);
    }

    function withdrawDividend(uint256 tokenID, address dividendToken) external {
        _withdrawDividend(tokenID, dividendToken, msg.sender);
    }

    function withdrawDividendFor(
        uint256 tokenID,
        address dividendToken,
        address user
    ) external {
        require(
            hasRole(DIVIDEND_ROLE, _msgSender()),
            "FundingMultitoken: must have dividend role"
        );
        _withdrawDividend(tokenID, dividendToken, user);
    }

    function registerDividendToken(address dividendToken) external {
        require(
            hasRole(DIVIDEND_ROLE, _msgSender()),
            "FundingMultitoken: must have dividend role"
        );
        _registerDividendToken(dividendToken);
    }

    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual override(ERC1155DividendToken, WrappedERC1155Token) {
        super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
    }

    /**
        @dev See {ERC1155Upgradeable._shouldSkipSafeTransferAcceptanceCheck}
     */
    function _shouldSkipSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    )
        internal
        override(ERC1155Upgradeable, WrappedERC1155Token)
        returns (bool)
    {
        return
            WrappedERC1155Token._shouldSkipSafeTransferAcceptanceCheck(
                operator,
                from,
                to,
                id,
                amount,
                data
            );
    }

    uint256[50] private __gap;
}

File 77 of 82 : NFT.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    ERC721URIStorageUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import {
    OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract NFT is ERC721URIStorageUpgradeable, OwnableUpgradeable {
    string internal _contractURI;
    string internal __baseURI;

    function initialize(string calldata tokenName, string calldata tokenSymbol)
        external
        initializer
    {
        __Ownable_init();
        __ERC721_init(tokenName, tokenSymbol);
    }

    function contractURI() external view returns (string memory) {
        return _contractURI;
    }

    function _baseURI() internal view override returns (string memory) {
        return __baseURI;
    }

    function mint(address to, uint256 tokenId) external onlyOwner {
        _safeMint(to, tokenId);
    }

    function mint(
        address to,
        uint256 tokenId,
        string calldata uri
    ) external onlyOwner {
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    function burn(uint256 tokenId) external onlyOwner {
        _burn(tokenId);
    }

    function setContractURI(string calldata newURI) external onlyOwner {
        _contractURI = newURI;
    }

    function setTokenURI(uint256 tokenId, string calldata newURI) external {
        require(ownerOf(tokenId) == msg.sender, "NFT: not token owner");
        _setTokenURI(tokenId, newURI);
    }

    function setBaseURI(string calldata newURI) external onlyOwner {
        __baseURI = newURI;
    }

    uint256[48] private __gap;
}

File 78 of 82 : NFTWithSVG.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    ERC721URIStorageUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import {
    OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

import {NFT} from "./NFT.sol";
import {NFTDescriptor} from "../libs/NFTDescriptor.sol";

contract NFTWithSVG is NFT {
    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        string memory _tokenURI = ERC721URIStorageUpgradeable.tokenURI(tokenId);
        return
            bytes(_tokenURI).length > 0
                ? _tokenURI
                : NFTDescriptor.constructTokenURI(
                    NFTDescriptor.URIParams({
                        tokenId: tokenId,
                        owner: ownerOf(tokenId),
                        name: name(),
                        symbol: symbol()
                    })
                );
    }
}

File 79 of 82 : ZeroCouponBond.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.4;

import {
    ERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {SafeERC20} from "../libs/SafeERC20.sol";
import {
    IERC721ReceiverUpgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
import {NFT} from "../tokens/NFT.sol";
import {DInterest} from "../DInterest.sol";
import {Vesting02} from "../rewards/Vesting02.sol";
import {Sponsorable} from "../libs/Sponsorable.sol";

contract ZeroCouponBond is
    ERC20Upgradeable,
    ReentrancyGuardUpgradeable,
    IERC721ReceiverUpgradeable,
    Sponsorable
{
    using SafeERC20 for ERC20;

    DInterest public pool;
    ERC20 public stablecoin;
    NFT public depositNFT;
    Vesting02 public vesting;
    uint64 public maturationTimestamp;
    uint64 public depositID;
    uint8 private _decimals;

    event WithdrawDeposit();
    event RedeemStablecoin(address indexed sender, uint256 amount);

    function initialize(
        address _creator,
        address _pool,
        address _vesting,
        uint64 _maturationTimestamp,
        uint256 _initialDepositAmount,
        string calldata _tokenName,
        string calldata _tokenSymbol
    ) external initializer {
        __ERC20_init(_tokenName, _tokenSymbol);
        __ReentrancyGuard_init();

        pool = DInterest(_pool);
        stablecoin = pool.stablecoin();
        depositNFT = pool.depositNFT();
        maturationTimestamp = _maturationTimestamp;
        vesting = Vesting02(_vesting);

        // set decimals to be the same as the underlying stablecoin
        _decimals = pool.stablecoin().decimals();

        // create deposit
        stablecoin.safeTransferFrom(
            _creator,
            address(this),
            _initialDepositAmount
        );
        stablecoin.safeIncreaseAllowance(address(pool), type(uint256).max);
        uint256 interestAmount;
        (depositID, interestAmount) = pool.deposit(
            _initialDepositAmount,
            maturationTimestamp
        );
        _mint(_creator, _initialDepositAmount + interestAmount);
        vesting.safeTransferFrom(
            address(this),
            _creator,
            vesting.depositIDToVestID(_pool, depositID)
        );
    }

    function decimals() public view override returns (uint8) {
        return _decimals;
    }

    /**
        Public action functions
     */
    /**
        @notice Mint zero coupon bonds by depositing `depositAmount` stablecoins.
        @param depositAmount The amount to deposit for minting zero coupon bonds
        @return mintedAmount The amount of bonds minted
     */
    function mint(uint256 depositAmount)
        external
        nonReentrant
        returns (uint256 mintedAmount)
    {
        return _mintInternal(msg.sender, depositAmount, 0);
    }

    /**
        @notice Mint zero coupon bonds by depositing `depositAmount` stablecoins.
        @param depositAmount The amount to deposit for minting zero coupon bonds
        @param minInterestAmount The minimum amount of fixed rate interest received
        @return mintedAmount The amount of bonds minted
     */
    function mint(uint256 depositAmount, uint256 minInterestAmount)
        external
        nonReentrant
        returns (uint256 mintedAmount)
    {
        return _mintInternal(msg.sender, depositAmount, minInterestAmount);
    }

    /**
        @notice Withdraws the underlying deposit from the DInterest pool.
     */
    function withdrawDeposit() external nonReentrant {
        uint256 balance = pool.getDeposit(depositID).virtualTokenTotalSupply;
        require(balance > 0, "ZeroCouponBond: already withdrawn");
        pool.withdraw(depositID, balance, false);

        emit WithdrawDeposit();
    }

    /**
        @notice Redeems zero coupon bonds 1-for-1 for the underlying stablecoins.
        @param amount The amount of zero coupon bonds to burn
        @param withdrawDepositIfNeeded True if withdrawDeposit() should be called if needed, false otherwise (to save gas)
     */
    function redeem(uint256 amount, bool withdrawDepositIfNeeded)
        external
        nonReentrant
    {
        _redeem(msg.sender, amount, withdrawDepositIfNeeded);
    }

    /**
        Sponsored action functions
     */
    /**
        @dev See {mint}
     */
    function sponsoredMint(
        uint256 depositAmount,
        uint256 minInterestAmount,
        Sponsorship calldata sponsorship
    )
        external
        nonReentrant
        sponsored(
            sponsorship,
            this.sponsoredMint.selector,
            abi.encode(depositAmount)
        )
        returns (uint256 mintedAmount)
    {
        return
            _mintInternal(sponsorship.sender, depositAmount, minInterestAmount);
    }

    /**
        @dev See {redeem}
     */
    function sponsoredRedeem(
        uint256 amount,
        bool withdrawDepositIfNeeded,
        Sponsorship calldata sponsorship
    )
        external
        nonReentrant
        sponsored(
            sponsorship,
            this.sponsoredRedeem.selector,
            abi.encode(amount, withdrawDepositIfNeeded)
        )
    {
        _redeem(sponsorship.sender, amount, withdrawDepositIfNeeded);
    }

    /**
        Public getter functions
     */

    /**
        @notice Checks whether withdrawDeposit() needs to be called.
        @return True if withdrawDeposit() should be called, false otherwise.
     */
    function withdrawDepositNeeded() external view returns (bool) {
        return pool.getDeposit(depositID).virtualTokenTotalSupply > 0;
    }

    /**
        Internal action functions
     */

    /**
        @dev See {mint}
     */
    function _mintInternal(
        address sender,
        uint256 depositAmount,
        uint256 minInterestAmount
    ) internal returns (uint256 mintedAmount) {
        // transfer stablecoins from `sender`
        stablecoin.safeTransferFrom(sender, address(this), depositAmount);

        // topup deposit
        mintedAmount =
            depositAmount +
            pool.topupDeposit(depositID, depositAmount, minInterestAmount);

        // mint zero coupon bonds to `msg.sender`
        _mint(sender, mintedAmount);
    }

    /**
        @dev See {redeem}
     */
    function _redeem(
        address sender,
        uint256 amount,
        bool withdrawDepositIfNeeded
    ) internal {
        require(
            block.timestamp >= maturationTimestamp,
            "ZeroCouponBond: not mature"
        );

        if (withdrawDepositIfNeeded) {
            uint256 balance =
                pool.getDeposit(depositID).virtualTokenTotalSupply;
            if (balance > 0) {
                pool.withdraw(depositID, balance, false);
                emit WithdrawDeposit();
            }
        }

        // burn `amount` zero coupon bonds from `sender`
        _burn(sender, amount);

        // transfer `amount` stablecoins to `sender`
        stablecoin.safeTransfer(sender, amount);

        emit RedeemStablecoin(sender, amount);
    }

    function onERC721Received(
        address, /*operator*/
        address, /*from*/
        uint256, /*tokenId*/
        bytes memory /*data*/
    ) external pure override returns (bytes4) {
        return this.onERC721Received.selector;
    }

    uint256[43] private __gap;
}

File 80 of 82 : 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 81 of 82 : PRBMath.sol
// SPDX-License-Identifier: WTFPL
pragma solidity >=0.8.4;

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivFixedPointOverflow(uint256 prod1);

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);

/// @notice Emitted when one of the inputs is type(int256).min.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows int256.
error PRBMath__MulDivSignedOverflow(uint256 rAbs);

/// @notice Emitted when the input is MIN_SD59x18.
error PRBMathSD59x18__AbsInputTooSmall();

/// @notice Emitted when ceiling a number overflows SD59x18.
error PRBMathSD59x18__CeilOverflow(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__DivInputTooSmall();

/// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
error PRBMathSD59x18__DivOverflow(uint256 rAbs);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathSD59x18__ExpInputTooBig(int256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathSD59x18__Exp2InputTooBig(int256 x);

/// @notice Emitted when flooring a number underflows SD59x18.
error PRBMathSD59x18__FloorUnderflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMathSD59x18__FromIntOverflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMathSD59x18__FromIntUnderflow(int256 x);

/// @notice Emitted when the product of the inputs is negative.
error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);

/// @notice Emitted when multiplying the inputs overflows SD59x18.
error PRBMathSD59x18__GmOverflow(int256 x, int256 y);

/// @notice Emitted when the input is less than or equal to zero.
error PRBMathSD59x18__LogInputTooSmall(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__MulInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__MulOverflow(uint256 rAbs);

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__PowuOverflow(uint256 rAbs);

/// @notice Emitted when the input is negative.
error PRBMathSD59x18__SqrtNegativeInput(int256 x);

/// @notice Emitted when the calculting the square root overflows SD59x18.
error PRBMathSD59x18__SqrtOverflow(int256 x);

/// @notice Emitted when addition overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(uint256 x);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(uint256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(uint256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
error PRBMathUD60x18__FromUintOverflow(uint256 x);

/// @notice Emitted when multiplying the inputs overflows UD60x18.
error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);

/// @notice Emitted when the input is less than 1.
error PRBMathUD60x18__LogInputTooSmall(uint256 x);

/// @notice Emitted when the calculting the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(uint256 x);

/// @notice Emitted when subtraction underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);

/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
/// representation. When it does not, it is explictly mentioned in the NatSpec documentation.
library PRBMath {
    /// STRUCTS ///

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @dev Largest power of two divisor of SCALE.
    uint256 internal constant SCALE_LPOTD = 262144;

    /// @dev SCALE inverted mod 2^256.
    uint256 internal constant SCALE_INVERSE = 78156646155174841979727994598816262306175212592076161876661508869554232690281;

    /// FUNCTIONS ///

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    /// @dev Has to use 192.64-bit fixed-point numbers.
    /// See https://ethereum.stackexchange.com/a/96594/24693.
    /// @param x The exponent as an unsigned 192.64-bit fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // Start from 0.5 in the 192.64-bit fixed-point format.
            result = 0x800000000000000000000000000000000000000000000000;

            // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
            // because the initial result is 2^191 and all magic factors are less than 2^65.
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }

            // We're doing two things at the same time:
            //
            //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
            //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
            //      rather than 192.
            //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
            //
            // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
            result *= SCALE;
            result >>= (191 - (x >> 64));
        }
    }

    /// @notice Finds the zero-based index of the first one in the binary representation of x.
    /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
    /// @param x The uint256 number for which to find the index of the most significant bit.
    /// @return msb The index of the most significant bit as an uint256.
    function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
        if (x >= 2**128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2**64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2**32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2**16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2**8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2**4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2**2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2**1) {
            // No need to shift x any more.
            msb += 1;
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2^256 + prod0.
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division.
        if (prod1 == 0) {
            unchecked {
                result = prod0 / denominator;
            }
            return result;
        }

        // Make sure the result is less than 2^256. Also prevents denominator == 0.
        if (prod1 >= denominator) {
            revert PRBMath__MulDivOverflow(prod1, denominator);
        }

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0].
        uint256 remainder;
        assembly {
            // Compute remainder using mulmod.
            remainder := mulmod(x, y, denominator)

            // Subtract 256 bit number from 512 bit number.
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
        // See https://cs.stackexchange.com/q/138556/92363.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

                // Divide [prod1 prod0] by lpotdod.
                prod0 := div(prod0, lpotdod)

                // Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one.
                lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * lpotdod;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Now use Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /// @notice Calculates floor(x*y÷1e18) with full precision.
    ///
    /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
    /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
    /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
    ///
    /// Requirements:
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
    ///     1. x * y = type(uint256).max * SCALE
    ///     2. (x * y) % SCALE >= SCALE / 2
    ///
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (prod1 >= SCALE) {
            revert PRBMath__MulDivFixedPointOverflow(prod1);
        }

        uint256 remainder;
        uint256 roundUpUnit;
        assembly {
            remainder := mulmod(x, y, SCALE)
            roundUpUnit := gt(remainder, 499999999999999999)
        }

        if (prod1 == 0) {
            unchecked {
                result = (prod0 / SCALE) + roundUpUnit;
                return result;
            }
        }

        assembly {
            result := add(
                mul(
                    or(
                        div(sub(prod0, remainder), SCALE_LPOTD),
                        mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                    ),
                    SCALE_INVERSE
                ),
                roundUpUnit
            )
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - None of the inputs can be type(int256).min.
    /// - The result must fit within int256.
    ///
    /// @param x The multiplicand as an int256.
    /// @param y The multiplier as an int256.
    /// @param denominator The divisor as an int256.
    /// @return result The result as an int256.
    function mulDivSigned(
        int256 x,
        int256 y,
        int256 denominator
    ) internal pure returns (int256 result) {
        if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
            revert PRBMath__MulDivSignedInputTooSmall();
        }

        // Get hold of the absolute values of x, y and the denominator.
        uint256 ax;
        uint256 ay;
        uint256 ad;
        unchecked {
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);
            ad = denominator < 0 ? uint256(-denominator) : uint256(denominator);
        }

        // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
        uint256 rAbs = mulDiv(ax, ay, ad);
        if (rAbs > uint256(type(int256).max)) {
            revert PRBMath__MulDivSignedOverflow(rAbs);
        }

        // Get the signs of x, y and the denominator.
        uint256 sx;
        uint256 sy;
        uint256 sd;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
            sd := sgt(denominator, sub(0, 1))
        }

        // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
        // If yes, the result should be negative.
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The uint256 number for which to calculate the square root.
    /// @return result The result as an uint256.
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        // Set the initial guess to the closest power of two that is higher than x.
        uint256 xAux = uint256(x);
        result = 1;
        if (xAux >= 0x100000000000000000000000000000000) {
            xAux >>= 128;
            result <<= 64;
        }
        if (xAux >= 0x10000000000000000) {
            xAux >>= 64;
            result <<= 32;
        }
        if (xAux >= 0x100000000) {
            xAux >>= 32;
            result <<= 16;
        }
        if (xAux >= 0x10000) {
            xAux >>= 16;
            result <<= 8;
        }
        if (xAux >= 0x100) {
            xAux >>= 8;
            result <<= 4;
        }
        if (xAux >= 0x10) {
            xAux >>= 4;
            result <<= 2;
        }
        if (xAux >= 0x8) {
            result <<= 1;
        }

        // The operations can never overflow because the result is max 2^127 when it enters this block.
        unchecked {
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1; // Seven iterations should be enough
            uint256 roundedDownResult = x / result;
            return result >= roundedDownResult ? roundedDownResult : result;
        }
    }
}

File 82 of 82 : PRBMathUD60x18.sol
// SPDX-License-Identifier: WTFPL
pragma solidity >=0.8.4;

import "./PRBMath.sol";

/// @title PRBMathUD60x18
/// @author Paul Razvan Berg
/// @notice Smart contract library for advanced fixed-point math that works with uint256 numbers considered to have 18
/// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60
/// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the
/// maximum values permitted by the Solidity type uint256.
library PRBMathUD60x18 {
    /// @dev Half the SCALE number.
    uint256 internal constant HALF_SCALE = 5e17;

    /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number.
    uint256 internal constant LOG2_E = 1442695040888963407;

    /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;

    /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457000000000000000000;

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @notice Calculates arithmetic average of x and y, rounding down.
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // The operations can never overflow.
        unchecked {
            // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need
            // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice.
            result = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x.
    ///
    /// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be less than or equal to MAX_WHOLE_UD60x18.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number to ceil.
    /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function ceil(uint256 x) internal pure returns (uint256 result) {
        if (x > MAX_WHOLE_UD60x18) {
            revert PRBMathUD60x18__CeilOverflow(x);
        }
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "SCALE - remainder" but faster.
            let delta := sub(SCALE, remainder)

            // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
            result := add(x, mul(delta, gt(remainder, 0)))
        }
    }

    /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number.
    ///
    /// @dev Uses mulDiv to enable overflow-safe multiplication and division.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    ///
    /// @param x The numerator as an unsigned 60.18-decimal fixed-point number.
    /// @param y The denominator as an unsigned 60.18-decimal fixed-point number.
    /// @param result The quotient as an unsigned 60.18-decimal fixed-point number.
    function div(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDiv(x, SCALE, y);
    }

    /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number.
    /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
    function e() internal pure returns (uint256 result) {
        result = 2718281828459045235;
    }

    /// @notice Calculates the natural exponent of x.
    ///
    /// @dev Based on the insight that e^x = 2^(x * log2(e)).
    ///
    /// Requirements:
    /// - All from "log2".
    /// - x must be less than 133.084258667509499441.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp(uint256 x) internal pure returns (uint256 result) {
        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133084258667509499441) {
            revert PRBMathUD60x18__ExpInputTooBig(x);
        }

        // Do the fixed-point multiplication inline to save gas.
        unchecked {
            uint256 doubleScaleProduct = x * LOG2_E;
            result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
        }
    }

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    ///
    /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
    ///
    /// Requirements:
    /// - x must be 192 or less.
    /// - The result must fit within MAX_UD60x18.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
        if (x >= 192e18) {
            revert PRBMathUD60x18__Exp2InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x192x64 = (x << 64) / SCALE;

            // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation.
            result = PRBMath.exp2(x192x64);
        }
    }

    /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x.
    /// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    /// @param x The unsigned 60.18-decimal fixed-point number to floor.
    /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function floor(uint256 x) internal pure returns (uint256 result) {
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
            result := sub(x, mul(remainder, gt(remainder, 0)))
        }
    }

    /// @notice Yields the excess beyond the floor of x.
    /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part.
    /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of.
    /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number.
    function frac(uint256 x) internal pure returns (uint256 result) {
        assembly {
            result := mod(x, SCALE)
        }
    }

    /// @notice Converts a number from basic integer form to unsigned 60.18-decimal fixed-point representation.
    ///
    /// @dev Requirements:
    /// - x must be less than or equal to MAX_UD60x18 divided by SCALE.
    ///
    /// @param x The basic integer to convert.
    /// @param result The same number in unsigned 60.18-decimal fixed-point representation.
    function fromUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__FromUintOverflow(x);
            }
            result = x * SCALE;
        }
    }

    /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
    ///
    /// @dev Requirements:
    /// - x * y must fit within MAX_UD60x18, lest it overflows.
    ///
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function gm(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        unchecked {
            // Checking for overflow this way is faster than letting Solidity do it.
            uint256 xy = x * y;
            if (xy / x != y) {
                revert PRBMathUD60x18__GmOverflow(x, y);
            }

            // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
            // during multiplication. See the comments within the "sqrt" function.
            result = PRBMath.sqrt(xy);
        }
    }

    /// @notice Calculates 1 / x, rounding towards zero.
    ///
    /// @dev Requirements:
    /// - x cannot be zero.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse.
    /// @return result The inverse as an unsigned 60.18-decimal fixed-point number.
    function inv(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // 1e36 is SCALE * SCALE.
            result = 1e36 / x;
        }
    }

    /// @notice Calculates the natural logarithm of x.
    ///
    /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    /// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm.
    /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number.
    function ln(uint256 x) internal pure returns (uint256 result) {
        // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
        // can return is 196205294292027477728.
        unchecked {
            result = (log2(x) * SCALE) / LOG2_E;
        }
    }

    /// @notice Calculates the common logarithm of x.
    ///
    /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
    /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm.
    /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number.
    function log10(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }

        // Note that the "mul" in this block is the assembly multiplication operation, not the "mul" function defined
        // in this contract.
        // prettier-ignore
        assembly {
            switch x
            case 1 { result := mul(SCALE, sub(0, 18)) }
            case 10 { result := mul(SCALE, sub(1, 18)) }
            case 100 { result := mul(SCALE, sub(2, 18)) }
            case 1000 { result := mul(SCALE, sub(3, 18)) }
            case 10000 { result := mul(SCALE, sub(4, 18)) }
            case 100000 { result := mul(SCALE, sub(5, 18)) }
            case 1000000 { result := mul(SCALE, sub(6, 18)) }
            case 10000000 { result := mul(SCALE, sub(7, 18)) }
            case 100000000 { result := mul(SCALE, sub(8, 18)) }
            case 1000000000 { result := mul(SCALE, sub(9, 18)) }
            case 10000000000 { result := mul(SCALE, sub(10, 18)) }
            case 100000000000 { result := mul(SCALE, sub(11, 18)) }
            case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
            case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
            case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
            case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
            case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
            case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
            case 1000000000000000000 { result := 0 }
            case 10000000000000000000 { result := SCALE }
            case 100000000000000000000 { result := mul(SCALE, 2) }
            case 1000000000000000000000 { result := mul(SCALE, 3) }
            case 10000000000000000000000 { result := mul(SCALE, 4) }
            case 100000000000000000000000 { result := mul(SCALE, 5) }
            case 1000000000000000000000000 { result := mul(SCALE, 6) }
            case 10000000000000000000000000 { result := mul(SCALE, 7) }
            case 100000000000000000000000000 { result := mul(SCALE, 8) }
            case 1000000000000000000000000000 { result := mul(SCALE, 9) }
            case 10000000000000000000000000000 { result := mul(SCALE, 10) }
            case 100000000000000000000000000000 { result := mul(SCALE, 11) }
            case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
            case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
            case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
            case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
            case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
            case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
            case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
            case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
            case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
            case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
            case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
            case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
            case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
            case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
            case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
            case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
            case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
            case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
            case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
            case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
            case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
            case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
            case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
            case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
            case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
            case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
            case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
            case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
            case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
            case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
            case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
            case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
            case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
            case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
            case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
            case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
            case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
            case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
            case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) }
            default {
                result := MAX_UD60x18
            }
        }

        if (result == MAX_UD60x18) {
            // Do the fixed-point division inline to save gas. The denominator is log2(10).
            unchecked {
                result = (log2(x) * SCALE) / 3321928094887362347;
            }
        }
    }

    /// @notice Calculates the binary logarithm of x.
    ///
    /// @dev Based on the iterative approximation algorithm.
    /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
    ///
    /// Requirements:
    /// - x must be greater than or equal to SCALE, otherwise the result would be negative.
    ///
    /// Caveats:
    /// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm.
    /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number.
    function log2(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }
        unchecked {
            // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
            uint256 n = PRBMath.mostSignificantBit(x / SCALE);

            // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow
            // because n is maximum 255 and SCALE is 1e18.
            result = n * SCALE;

            // This is y = x * 2^(-n).
            uint256 y = x >> n;

            // If y = 1, the fractional part is zero.
            if (y == SCALE) {
                return result;
            }

            // Calculate the fractional part via the iterative approximation.
            // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
            for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) {
                y = (y * y) / SCALE;

                // Is y^2 > 2 and so in the range [2,4)?
                if (y >= 2 * SCALE) {
                    // Add the 2^(-m) factor to the logarithm.
                    result += delta;

                    // Corresponds to z/2 on Wikipedia.
                    y >>= 1;
                }
            }
        }
    }

    /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal
    /// fixed-point number.
    /// @dev See the documentation for the "PRBMath.mulDivFixedPoint" function.
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The product as an unsigned 60.18-decimal fixed-point number.
    function mul(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDivFixedPoint(x, y);
    }

    /// @notice Returns PI as an unsigned 60.18-decimal fixed-point number.
    function pi() internal pure returns (uint256 result) {
        result = 3141592653589793238;
    }

    /// @notice Raises x to the power of y.
    ///
    /// @dev Based on the insight that x^y = 2^(log2(x) * y).
    ///
    /// Requirements:
    /// - All from "exp2", "log2" and "mul".
    ///
    /// Caveats:
    /// - All from "exp2", "log2" and "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number.
    /// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number.
    /// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number.
    function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            result = y == 0 ? SCALE : uint256(0);
        } else {
            result = exp2(mul(log2(x), y));
        }
    }

    /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
    /// famous algorithm "exponentiation by squaring".
    ///
    /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
    ///
    /// Requirements:
    /// - The result must fit within MAX_UD60x18.
    ///
    /// Caveats:
    /// - All from "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x The base as an unsigned 60.18-decimal fixed-point number.
    /// @param y The exponent as an uint256.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // Calculate the first iteration of the loop in advance.
        result = y & 1 > 0 ? x : SCALE;

        // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
        for (y >>= 1; y > 0; y >>= 1) {
            x = PRBMath.mulDivFixedPoint(x, x);

            // Equivalent to "y % 2 == 1" but faster.
            if (y & 1 > 0) {
                result = PRBMath.mulDivFixedPoint(result, x);
            }
        }
    }

    /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number.
    function scale() internal pure returns (uint256 result) {
        result = SCALE;
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Requirements:
    /// - x must be less than MAX_UD60x18 / SCALE.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root.
    /// @return result The result as an unsigned 60.18-decimal fixed-point .
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__SqrtOverflow(x);
            }
            // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned
            // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
            result = PRBMath.sqrt(x * SCALE);
        }
    }

    /// @notice Converts a unsigned 60.18-decimal fixed-point number to basic integer form, rounding down in the process.
    /// @param x The unsigned 60.18-decimal fixed-point number to convert.
    /// @return result The same number in basic integer form.
    function toUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            result = x / SCALE;
        }
    }
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"contractName","type":"string"},{"indexed":false,"internalType":"address","name":"template","type":"address"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"address","name":"clone","type":"address"}],"name":"CreateClone","type":"event"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_provider","type":"address"},{"internalType":"address","name":"_aToken","type":"address"},{"internalType":"address","name":"_aaveMining","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"address","name":"_rescuer","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"}],"name":"createAaveMarket","outputs":[{"internalType":"contract AaveMarket","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_bToken","type":"address"},{"internalType":"address","name":"_bComptroller","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"address","name":"_rescuer","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"}],"name":"createBProtocolMarket","outputs":[{"internalType":"contract BProtocolMarket","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_cToken","type":"address"},{"internalType":"address","name":"_comptroller","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"address","name":"_rescuer","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"}],"name":"createCompoundERC20Market","outputs":[{"internalType":"contract CompoundERC20Market","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_cToken","type":"address"},{"internalType":"address","name":"_rescuer","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"}],"name":"createCreamERC20Market","outputs":[{"internalType":"contract CreamERC20Market","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint64","name":"_MaxDepositPeriod","type":"uint64"},{"internalType":"uint256","name":"_MinDepositAmount","type":"uint256"},{"internalType":"address","name":"_feeModel","type":"address"},{"internalType":"address","name":"_interestModel","type":"address"},{"internalType":"address","name":"_interestOracle","type":"address"},{"internalType":"address","name":"_depositNFT","type":"address"},{"internalType":"address","name":"_fundingMultitoken","type":"address"},{"internalType":"address","name":"_mphMinter","type":"address"}],"name":"createDInterest","outputs":[{"internalType":"contract DInterest","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"components":[{"internalType":"uint64","name":"_MaxDepositPeriod","type":"uint64"},{"internalType":"uint256","name":"_MinDepositAmount","type":"uint256"},{"internalType":"uint256","name":"_DepositFee","type":"uint256"},{"internalType":"address","name":"_feeModel","type":"address"},{"internalType":"address","name":"_interestModel","type":"address"},{"internalType":"address","name":"_interestOracle","type":"address"},{"internalType":"address","name":"_depositNFT","type":"address"},{"internalType":"address","name":"_fundingMultitoken","type":"address"},{"internalType":"address","name":"_mphMinter","type":"address"}],"internalType":"struct Factory.DInterestWithDepositFeeParams","name":"params","type":"tuple"}],"name":"createDInterestWithDepositFee","outputs":[{"internalType":"contract DInterestWithDepositFee","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256","name":"_emaInitial","type":"uint256"},{"internalType":"uint256","name":"_updateInterval","type":"uint256"},{"internalType":"uint256","name":"_smoothingFactor","type":"uint256"},{"internalType":"uint256","name":"_averageWindowInIntervals","type":"uint256"},{"internalType":"address","name":"_moneyMarket","type":"address"}],"name":"createEMAOracle","outputs":[{"internalType":"contract EMAOracle","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"string","name":"_uri","type":"string"},{"internalType":"address[]","name":"_dividendTokens","type":"address[]"},{"internalType":"address","name":"_wrapperTemplate","type":"address"},{"internalType":"bool","name":"_deployWrapperOnMint","type":"bool"},{"internalType":"string","name":"_baseName","type":"string"},{"internalType":"string","name":"_baseSymbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"}],"name":"createFundingMultitoken","outputs":[{"internalType":"contract FundingMultitoken","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"address","name":"_stakingPool","type":"address"},{"internalType":"address","name":"_rescuer","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"}],"name":"createHarvestMarket","outputs":[{"internalType":"contract HarvestMarket","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"string","name":"_tokenName","type":"string"},{"internalType":"string","name":"_tokenSymbol","type":"string"}],"name":"createNFT","outputs":[{"internalType":"contract NFTWithSVG","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_rescuer","type":"address"},{"internalType":"address","name":"_stablecoin","type":"address"}],"name":"createYVaultMarket","outputs":[{"internalType":"contract YVaultMarket","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"_pool","type":"address"},{"internalType":"address","name":"_vesting","type":"address"},{"internalType":"uint64","name":"_maturationTimetstamp","type":"uint64"},{"internalType":"uint256","name":"_initialDepositAmount","type":"uint256"},{"internalType":"string","name":"_tokenName","type":"string"},{"internalType":"string","name":"_tokenSymbol","type":"string"}],"name":"createZeroCouponBond","outputs":[{"internalType":"contract ZeroCouponBond","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"template","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"predictAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50611a13806100206000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063afb39af81161008c578063c9d4253c11610066578063c9d4253c1461019b578063cb193942146101ae578063f43f3472146101c1578063fc2c1503146101d457600080fd5b8063afb39af814610162578063bac4e70d14610175578063bd132c1a1461018857600080fd5b8063014ab788146100d45780635e71a2b3146101035780636e85ff8e1461011657806384489afb14610129578063850274d71461013c578063951866131461014f575b600080fd5b6100e76100e23660046115a2565b6101e7565b6040516001600160a01b03909116815260200160405180910390f35b6100e76101113660046116c5565b610316565b6100e7610124366004611671565b61048e565b6100e761013736600461126c565b610578565b6100e761014a3660046112c9565b6106b4565b6100e761015d366004611629565b6107ac565b6100e761017036600461126c565b6109bd565b6100e76101833660046112c9565b610aba565b6100e76101963660046112c9565b610bb4565b6100e76101a936600461149c565b610cb2565b6100e76101bc366004611243565b610da2565b6100e76101cf3660046113d6565b610dc0565b6100e76101e2366004611347565b610e68565b6000806101fd6001600160a01b03891688610fb9565b60405163266c45bb60e11b81529091506001600160a01b03821690634cd88b7690610232908990899089908990600401611975565b600060405180830381600087803b15801561024c57600080fd5b505af1158015610260573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b1580156102a557600080fd5b505af11580156102b9573d6000803e3d6000fd5b505050506040516102da90694e46545769746853564760b01b8152600a0190565b60405180910390206000805160206119be8339815191528989846040516103039392919061189f565b60405180910390a2979650505050505050565b60008061032c6001600160a01b038d168c610fb9565b60405163254950ed60e21b815267ffffffffffffffff8c166004820152602481018b90526001600160a01b038a811660448301528981166064830152888116608483015287811660a483015286811660c483015285811660e48301529192509082169063952543b49061010401600060405180830381600087803b1580156103b357600080fd5b505af11580156103c7573d6000803e3d6000fd5b505060405163078dfbe760e01b815233600482015260016024820152600060448201526001600160a01b038416925063078dfbe79150606401600060405180830381600087803b15801561041a57600080fd5b505af115801561042e573d6000803e3d6000fd5b5050505060405161044e906811125b9d195c995cdd60ba1b815260090190565b60405180910390206000805160206119be8339815191528d8d846040516104779392919061189f565b60405180910390a29b9a5050505050505050505050565b6000806104a46001600160a01b038a1689610fb9565b60405163666b294b60e11b8152600481018990526024810188905260448101879052606481018690526001600160a01b0385811660848301529192509082169063ccd652969060a401600060405180830381600087803b15801561050757600080fd5b505af115801561051b573d6000803e3d6000fd5b5050505060405161053b9068454d414f7261636c6560b81b815260090190565b60405180910390206000805160206119be8339815191528a8a846040516105649392919061189f565b60405180910390a298975050505050505050565b60008061058e6001600160a01b03881687610fb9565b60405163c0c53b8b60e01b81526001600160a01b038781166004830152868116602483015285811660448301529192509082169063c0c53b8b90606401600060405180830381600087803b1580156105e557600080fd5b505af11580156105f9573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b15801561063e57600080fd5b505af1158015610652573d6000803e3d6000fd5b50505050604051610679906f10dc99585b515490cc8c13585c9ad95d60821b815260100190565b60405180910390206000805160206119be8339815191528888846040516106a29392919061189f565b60405180910390a29695505050505050565b6000806106ca6001600160a01b038a1689610fb9565b604051630a2ca2bd60e11b81529091506001600160a01b03821690631459457a90610701908a908a908a908a908a906004016117fe565b600060405180830381600087803b15801561071b57600080fd5b505af115801561072f573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b15801561077457600080fd5b505af1158015610788573d6000803e3d6000fd5b5050505060405161053b906c12185c9d995cdd13585c9ad95d609a1b8152600d0190565b6000806107c26001600160a01b03861685610fb9565b90506001600160a01b03811663f113df526107e06020860186611770565b602086013560408701356107fa6080890160608a01611229565b61080a60a08a0160808b01611229565b61081a60c08b0160a08c01611229565b61082a60e08c0160c08d01611229565b61083b6101008d0160e08e01611229565b61084d6101208e016101008f01611229565b6040516001600160e01b031960e08c901b16815267ffffffffffffffff90991660048a0152602489019790975260448801959095526001600160a01b0393841660648801529183166084870152821660a4860152811660c485015290811660e48401521661010482015261012401600060405180830381600087803b1580156108d557600080fd5b505af11580156108e9573d6000803e3d6000fd5b505060405163078dfbe760e01b815233600482015260016024820152600060448201526001600160a01b038416925063078dfbe79150606401600060405180830381600087803b15801561093c57600080fd5b505af1158015610950573d6000803e3d6000fd5b50505050604051610984907f44496e746572657374576974684465706f736974466565000000000000000000815260170190565b60405180910390206000805160206119be8339815191528686846040516109ad9392919061189f565b60405180910390a2949350505050565b6000806109d36001600160a01b03881687610fb9565b60405163c0c53b8b60e01b81526001600160a01b038781166004830152868116602483015285811660448301529192509082169063c0c53b8b90606401600060405180830381600087803b158015610a2a57600080fd5b505af1158015610a3e573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610a8357600080fd5b505af1158015610a97573d6000803e3d6000fd5b50505050604051610679906b1655985d5b1d13585c9ad95d60a21b8152600c0190565b600080610ad06001600160a01b038a1689610fb9565b604051630a2ca2bd60e11b81529091506001600160a01b03821690631459457a90610b07908a908a908a908a908a906004016117fe565b600060405180830381600087803b158015610b2157600080fd5b505af1158015610b35573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610b7a57600080fd5b505af1158015610b8e573d6000803e3d6000fd5b5050505060405161053b906e10941c9bdd1bd8dbdb13585c9ad95d608a1b8152600f0190565b600080610bca6001600160a01b038a1689610fb9565b604051630a2ca2bd60e11b81529091506001600160a01b03821690631459457a90610c01908a908a908a908a908a906004016117fe565b600060405180830381600087803b158015610c1b57600080fd5b505af1158015610c2f573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610c7457600080fd5b505af1158015610c88573d6000803e3d6000fd5b5050505060405161053b907210dbdb5c1bdd5b99115490cc8c13585c9ad95d606a1b815260130190565b600080610cc86001600160a01b038e168d610fb9565b6040516338141feb60e21b81529091506001600160a01b0382169063e0507fac90610d099033908f908f908f908f908f908f908f908f908f906004016118c2565b600060405180830381600087803b158015610d2357600080fd5b505af1158015610d37573d6000803e3d6000fd5b50505050604051610d5f9070233ab73234b733a6bab63a34ba37b5b2b760791b815260110190565b60405180910390206000805160206119be8339815191528e8e84604051610d889392919061189f565b60405180910390a290505b9b9a5050505050505050505050565b6000610db76001600160a01b0384168361105d565b90505b92915050565b600080610dd66001600160a01b038d168c610fb9565b60405163a2c1910760e01b81529091506001600160a01b0382169063a2c1910790610e159033908e908e908e908e908e908e908e908e90600401611830565b600060405180830381600087803b158015610e2f57600080fd5b505af1158015610e43573d6000803e3d6000fd5b5050505060405161044e906d16995c9bd0dbdd5c1bdb909bdb9960921b8152600e0190565b600080610e7e6001600160a01b038b168a610fb9565b60405163cc2a9a5b60e01b81526001600160a01b038a81166004830152898116602483015288811660448301528781166064830152868116608483015285811660a48301529192509082169063cc2a9a5b9060c401600060405180830381600087803b158015610eed57600080fd5b505af1158015610f01573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610f4657600080fd5b505af1158015610f5a573d6000803e3d6000fd5b50505050604051610f7b906910585d9953585c9ad95d60b21b8152600a0190565b60405180910390206000805160206119be8339815191528b8b84604051610fa49392919061189f565b60405180910390a29998505050505050505050565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b038116610dba5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015260640160405180910390fd5b6000610db7838330604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b80356001600160a01b03811681146110da57600080fd5b919050565b60008083601f8401126110f0578081fd5b50813567ffffffffffffffff811115611107578182fd5b6020830191508360208260051b850101111561112257600080fd5b9250929050565b803580151581146110da57600080fd5b60008083601f84011261114a578182fd5b50813567ffffffffffffffff811115611161578182fd5b60208301915083602082850101111561112257600080fd5b600082601f830112611189578081fd5b813567ffffffffffffffff808211156111a4576111a46119a7565b604051601f8301601f19908116603f011681019082821181831017156111cc576111cc6119a7565b816040528381528660208588010111156111e4578485fd5b8360208701602083013792830160200193909352509392505050565b803567ffffffffffffffff811681146110da57600080fd5b803560ff811681146110da57600080fd5b60006020828403121561123a578081fd5b610db7826110c3565b60008060408385031215611255578081fd5b61125e836110c3565b946020939093013593505050565b600080600080600060a08688031215611283578081fd5b61128c866110c3565b9450602086013593506112a1604087016110c3565b92506112af606087016110c3565b91506112bd608087016110c3565b90509295509295909350565b600080600080600080600060e0888a0312156112e3578182fd5b6112ec886110c3565b965060208801359550611301604089016110c3565b945061130f606089016110c3565b935061131d608089016110c3565b925061132b60a089016110c3565b915061133960c089016110c3565b905092959891949750929550565b600080600080600080600080610100898b031215611363578081fd5b61136c896110c3565b97506020890135965061138160408a016110c3565b955061138f60608a016110c3565b945061139d60808a016110c3565b93506113ab60a08a016110c3565b92506113b960c08a016110c3565b91506113c760e08a016110c3565b90509295985092959890939650565b6000806000806000806000806000806101008b8d0312156113f5578182fd5b6113fe8b6110c3565b995060208b0135985061141360408c016110c3565b975061142160608c016110c3565b965061142f60808c01611200565b955060a08b0135945060c08b013567ffffffffffffffff80821115611452578384fd5b61145e8e838f01611139565b909650945060e08d0135915080821115611476578384fd5b506114838d828e01611139565b915080935050809150509295989b9194979a5092959850565b60008060008060008060008060008060006101208c8e0312156114bd578485fd5b6114c68c6110c3565b9a5060208c0135995067ffffffffffffffff8060408e013511156114e8578586fd5b6114f88e60408f01358f01611139565b909a50985060608d013581101561150d578586fd5b61151d8e60608f01358f016110df565b909850965061152e60808e016110c3565b955061153c60a08e01611129565b94508060c08e0135111561154e578182fd5b61155e8e60c08f01358f01611179565b93508060e08e01351115611570578182fd5b506115818d60e08e01358e01611179565b91506115906101008d01611218565b90509295989b509295989b9093969950565b600080600080600080608087890312156115ba578384fd5b6115c3876110c3565b955060208701359450604087013567ffffffffffffffff808211156115e6578586fd5b6115f28a838b01611139565b9096509450606089013591508082111561160a578384fd5b5061161789828a01611139565b979a9699509497509295939492505050565b600080600083850361016081121561163f578182fd5b611648856110c3565b935060208501359250610120603f1982011215611663578182fd5b506040840190509250925092565b600080600080600080600060e0888a03121561168b578081fd5b611694886110c3565b96506020880135955060408801359450606088013593506080880135925060a0880135915061133960c089016110c3565b6000806000806000806000806000806101408b8d0312156116e4578384fd5b6116ed8b6110c3565b995060208b0135985061170260408c01611200565b975060608b0135965061171760808c016110c3565b955061172560a08c016110c3565b945061173360c08c016110c3565b935061174160e08c016110c3565b92506117506101008c016110c3565b915061175f6101208c016110c3565b90509295989b9194979a5092959850565b600060208284031215611781578081fd5b610db782611200565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008151808452815b818110156117d8576020818501810151868301820152016117bc565b818111156117e95782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b6001600160a01b038a8116825289811660208301528816604082015267ffffffffffffffff871660608201526080810186905260e060a0820181905260009061187c908301868861178a565b82810360c084015261188f81858761178a565b9c9b505050505050505050505050565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b038b81168252610100602083018190526000916118e98483018d8f61178a565b84810360408601528a81528b9250602001835b8b811015611923578261190e856110c3565b168252602093840193909101906001016118fc565b506001600160a01b038a166060860152881515608086015284810360a086015261194d81896117b3565b9250505082810360c084015261196381866117b3565b915050610d9360e083018460ff169052565b60408152600061198960408301868861178a565b828103602084015261199c81858761178a565b979650505050505050565b634e487b7160e01b600052604160045260246000fdfe19ccf2081a0315afed816e5fd1dfd6f9d4809ab697799459f04f55eb483d20f1a26469706673582212205fd4d39379e498a3bbca94f1997c244a624aad911e360214f90caef33c38171a64736f6c63430008040033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063afb39af81161008c578063c9d4253c11610066578063c9d4253c1461019b578063cb193942146101ae578063f43f3472146101c1578063fc2c1503146101d457600080fd5b8063afb39af814610162578063bac4e70d14610175578063bd132c1a1461018857600080fd5b8063014ab788146100d45780635e71a2b3146101035780636e85ff8e1461011657806384489afb14610129578063850274d71461013c578063951866131461014f575b600080fd5b6100e76100e23660046115a2565b6101e7565b6040516001600160a01b03909116815260200160405180910390f35b6100e76101113660046116c5565b610316565b6100e7610124366004611671565b61048e565b6100e761013736600461126c565b610578565b6100e761014a3660046112c9565b6106b4565b6100e761015d366004611629565b6107ac565b6100e761017036600461126c565b6109bd565b6100e76101833660046112c9565b610aba565b6100e76101963660046112c9565b610bb4565b6100e76101a936600461149c565b610cb2565b6100e76101bc366004611243565b610da2565b6100e76101cf3660046113d6565b610dc0565b6100e76101e2366004611347565b610e68565b6000806101fd6001600160a01b03891688610fb9565b60405163266c45bb60e11b81529091506001600160a01b03821690634cd88b7690610232908990899089908990600401611975565b600060405180830381600087803b15801561024c57600080fd5b505af1158015610260573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b1580156102a557600080fd5b505af11580156102b9573d6000803e3d6000fd5b505050506040516102da90694e46545769746853564760b01b8152600a0190565b60405180910390206000805160206119be8339815191528989846040516103039392919061189f565b60405180910390a2979650505050505050565b60008061032c6001600160a01b038d168c610fb9565b60405163254950ed60e21b815267ffffffffffffffff8c166004820152602481018b90526001600160a01b038a811660448301528981166064830152888116608483015287811660a483015286811660c483015285811660e48301529192509082169063952543b49061010401600060405180830381600087803b1580156103b357600080fd5b505af11580156103c7573d6000803e3d6000fd5b505060405163078dfbe760e01b815233600482015260016024820152600060448201526001600160a01b038416925063078dfbe79150606401600060405180830381600087803b15801561041a57600080fd5b505af115801561042e573d6000803e3d6000fd5b5050505060405161044e906811125b9d195c995cdd60ba1b815260090190565b60405180910390206000805160206119be8339815191528d8d846040516104779392919061189f565b60405180910390a29b9a5050505050505050505050565b6000806104a46001600160a01b038a1689610fb9565b60405163666b294b60e11b8152600481018990526024810188905260448101879052606481018690526001600160a01b0385811660848301529192509082169063ccd652969060a401600060405180830381600087803b15801561050757600080fd5b505af115801561051b573d6000803e3d6000fd5b5050505060405161053b9068454d414f7261636c6560b81b815260090190565b60405180910390206000805160206119be8339815191528a8a846040516105649392919061189f565b60405180910390a298975050505050505050565b60008061058e6001600160a01b03881687610fb9565b60405163c0c53b8b60e01b81526001600160a01b038781166004830152868116602483015285811660448301529192509082169063c0c53b8b90606401600060405180830381600087803b1580156105e557600080fd5b505af11580156105f9573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b15801561063e57600080fd5b505af1158015610652573d6000803e3d6000fd5b50505050604051610679906f10dc99585b515490cc8c13585c9ad95d60821b815260100190565b60405180910390206000805160206119be8339815191528888846040516106a29392919061189f565b60405180910390a29695505050505050565b6000806106ca6001600160a01b038a1689610fb9565b604051630a2ca2bd60e11b81529091506001600160a01b03821690631459457a90610701908a908a908a908a908a906004016117fe565b600060405180830381600087803b15801561071b57600080fd5b505af115801561072f573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b15801561077457600080fd5b505af1158015610788573d6000803e3d6000fd5b5050505060405161053b906c12185c9d995cdd13585c9ad95d609a1b8152600d0190565b6000806107c26001600160a01b03861685610fb9565b90506001600160a01b03811663f113df526107e06020860186611770565b602086013560408701356107fa6080890160608a01611229565b61080a60a08a0160808b01611229565b61081a60c08b0160a08c01611229565b61082a60e08c0160c08d01611229565b61083b6101008d0160e08e01611229565b61084d6101208e016101008f01611229565b6040516001600160e01b031960e08c901b16815267ffffffffffffffff90991660048a0152602489019790975260448801959095526001600160a01b0393841660648801529183166084870152821660a4860152811660c485015290811660e48401521661010482015261012401600060405180830381600087803b1580156108d557600080fd5b505af11580156108e9573d6000803e3d6000fd5b505060405163078dfbe760e01b815233600482015260016024820152600060448201526001600160a01b038416925063078dfbe79150606401600060405180830381600087803b15801561093c57600080fd5b505af1158015610950573d6000803e3d6000fd5b50505050604051610984907f44496e746572657374576974684465706f736974466565000000000000000000815260170190565b60405180910390206000805160206119be8339815191528686846040516109ad9392919061189f565b60405180910390a2949350505050565b6000806109d36001600160a01b03881687610fb9565b60405163c0c53b8b60e01b81526001600160a01b038781166004830152868116602483015285811660448301529192509082169063c0c53b8b90606401600060405180830381600087803b158015610a2a57600080fd5b505af1158015610a3e573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610a8357600080fd5b505af1158015610a97573d6000803e3d6000fd5b50505050604051610679906b1655985d5b1d13585c9ad95d60a21b8152600c0190565b600080610ad06001600160a01b038a1689610fb9565b604051630a2ca2bd60e11b81529091506001600160a01b03821690631459457a90610b07908a908a908a908a908a906004016117fe565b600060405180830381600087803b158015610b2157600080fd5b505af1158015610b35573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610b7a57600080fd5b505af1158015610b8e573d6000803e3d6000fd5b5050505060405161053b906e10941c9bdd1bd8dbdb13585c9ad95d608a1b8152600f0190565b600080610bca6001600160a01b038a1689610fb9565b604051630a2ca2bd60e11b81529091506001600160a01b03821690631459457a90610c01908a908a908a908a908a906004016117fe565b600060405180830381600087803b158015610c1b57600080fd5b505af1158015610c2f573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610c7457600080fd5b505af1158015610c88573d6000803e3d6000fd5b5050505060405161053b907210dbdb5c1bdd5b99115490cc8c13585c9ad95d606a1b815260130190565b600080610cc86001600160a01b038e168d610fb9565b6040516338141feb60e21b81529091506001600160a01b0382169063e0507fac90610d099033908f908f908f908f908f908f908f908f908f906004016118c2565b600060405180830381600087803b158015610d2357600080fd5b505af1158015610d37573d6000803e3d6000fd5b50505050604051610d5f9070233ab73234b733a6bab63a34ba37b5b2b760791b815260110190565b60405180910390206000805160206119be8339815191528e8e84604051610d889392919061189f565b60405180910390a290505b9b9a5050505050505050505050565b6000610db76001600160a01b0384168361105d565b90505b92915050565b600080610dd66001600160a01b038d168c610fb9565b60405163a2c1910760e01b81529091506001600160a01b0382169063a2c1910790610e159033908e908e908e908e908e908e908e908e90600401611830565b600060405180830381600087803b158015610e2f57600080fd5b505af1158015610e43573d6000803e3d6000fd5b5050505060405161044e906d16995c9bd0dbdd5c1bdb909bdb9960921b8152600e0190565b600080610e7e6001600160a01b038b168a610fb9565b60405163cc2a9a5b60e01b81526001600160a01b038a81166004830152898116602483015288811660448301528781166064830152868116608483015285811660a48301529192509082169063cc2a9a5b9060c401600060405180830381600087803b158015610eed57600080fd5b505af1158015610f01573d6000803e3d6000fd5b505060405163f2fde38b60e01b81523360048201526001600160a01b038416925063f2fde38b9150602401600060405180830381600087803b158015610f4657600080fd5b505af1158015610f5a573d6000803e3d6000fd5b50505050604051610f7b906910585d9953585c9ad95d60b21b8152600a0190565b60405180910390206000805160206119be8339815191528b8b84604051610fa49392919061189f565b60405180910390a29998505050505050505050565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b038116610dba5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015260640160405180910390fd5b6000610db7838330604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b80356001600160a01b03811681146110da57600080fd5b919050565b60008083601f8401126110f0578081fd5b50813567ffffffffffffffff811115611107578182fd5b6020830191508360208260051b850101111561112257600080fd5b9250929050565b803580151581146110da57600080fd5b60008083601f84011261114a578182fd5b50813567ffffffffffffffff811115611161578182fd5b60208301915083602082850101111561112257600080fd5b600082601f830112611189578081fd5b813567ffffffffffffffff808211156111a4576111a46119a7565b604051601f8301601f19908116603f011681019082821181831017156111cc576111cc6119a7565b816040528381528660208588010111156111e4578485fd5b8360208701602083013792830160200193909352509392505050565b803567ffffffffffffffff811681146110da57600080fd5b803560ff811681146110da57600080fd5b60006020828403121561123a578081fd5b610db7826110c3565b60008060408385031215611255578081fd5b61125e836110c3565b946020939093013593505050565b600080600080600060a08688031215611283578081fd5b61128c866110c3565b9450602086013593506112a1604087016110c3565b92506112af606087016110c3565b91506112bd608087016110c3565b90509295509295909350565b600080600080600080600060e0888a0312156112e3578182fd5b6112ec886110c3565b965060208801359550611301604089016110c3565b945061130f606089016110c3565b935061131d608089016110c3565b925061132b60a089016110c3565b915061133960c089016110c3565b905092959891949750929550565b600080600080600080600080610100898b031215611363578081fd5b61136c896110c3565b97506020890135965061138160408a016110c3565b955061138f60608a016110c3565b945061139d60808a016110c3565b93506113ab60a08a016110c3565b92506113b960c08a016110c3565b91506113c760e08a016110c3565b90509295985092959890939650565b6000806000806000806000806000806101008b8d0312156113f5578182fd5b6113fe8b6110c3565b995060208b0135985061141360408c016110c3565b975061142160608c016110c3565b965061142f60808c01611200565b955060a08b0135945060c08b013567ffffffffffffffff80821115611452578384fd5b61145e8e838f01611139565b909650945060e08d0135915080821115611476578384fd5b506114838d828e01611139565b915080935050809150509295989b9194979a5092959850565b60008060008060008060008060008060006101208c8e0312156114bd578485fd5b6114c68c6110c3565b9a5060208c0135995067ffffffffffffffff8060408e013511156114e8578586fd5b6114f88e60408f01358f01611139565b909a50985060608d013581101561150d578586fd5b61151d8e60608f01358f016110df565b909850965061152e60808e016110c3565b955061153c60a08e01611129565b94508060c08e0135111561154e578182fd5b61155e8e60c08f01358f01611179565b93508060e08e01351115611570578182fd5b506115818d60e08e01358e01611179565b91506115906101008d01611218565b90509295989b509295989b9093969950565b600080600080600080608087890312156115ba578384fd5b6115c3876110c3565b955060208701359450604087013567ffffffffffffffff808211156115e6578586fd5b6115f28a838b01611139565b9096509450606089013591508082111561160a578384fd5b5061161789828a01611139565b979a9699509497509295939492505050565b600080600083850361016081121561163f578182fd5b611648856110c3565b935060208501359250610120603f1982011215611663578182fd5b506040840190509250925092565b600080600080600080600060e0888a03121561168b578081fd5b611694886110c3565b96506020880135955060408801359450606088013593506080880135925060a0880135915061133960c089016110c3565b6000806000806000806000806000806101408b8d0312156116e4578384fd5b6116ed8b6110c3565b995060208b0135985061170260408c01611200565b975060608b0135965061171760808c016110c3565b955061172560a08c016110c3565b945061173360c08c016110c3565b935061174160e08c016110c3565b92506117506101008c016110c3565b915061175f6101208c016110c3565b90509295989b9194979a5092959850565b600060208284031215611781578081fd5b610db782611200565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008151808452815b818110156117d8576020818501810151868301820152016117bc565b818111156117e95782602083870101525b50601f01601f19169290920160200192915050565b6001600160a01b0395861681529385166020850152918416604084015283166060830152909116608082015260a00190565b6001600160a01b038a8116825289811660208301528816604082015267ffffffffffffffff871660608201526080810186905260e060a0820181905260009061187c908301868861178a565b82810360c084015261188f81858761178a565b9c9b505050505050505050505050565b6001600160a01b0393841681526020810192909252909116604082015260600190565b6001600160a01b038b81168252610100602083018190526000916118e98483018d8f61178a565b84810360408601528a81528b9250602001835b8b811015611923578261190e856110c3565b168252602093840193909101906001016118fc565b506001600160a01b038a166060860152881515608086015284810360a086015261194d81896117b3565b9250505082810360c084015261196381866117b3565b915050610d9360e083018460ff169052565b60408152600061198960408301868861178a565b828103602084015261199c81858761178a565b979650505050505050565b634e487b7160e01b600052604160045260246000fdfe19ccf2081a0315afed816e5fd1dfd6f9d4809ab697799459f04f55eb483d20f1a26469706673582212205fd4d39379e498a3bbca94f1997c244a624aad911e360214f90caef33c38171a64736f6c63430008040033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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