Contract 0x3318923722cD52c7c1752A62056532Bef3287720

 
 
Txn Hash
Method
Block
From
To
Value [Txn Fee]
0xd6d872999759d74240b90baa8a75410262ddf85091a3531d670eaf540fa11a54Grant Role407881452023-03-26 11:08:4262 days 23 hrs ago0xe820cc557279acc115d61b7c450f47ff000b4ef6 IN  0x3318923722cd52c7c1752a62056532bef32877200 MATIC0.0041108880
0x3e71aa85400710dd20bb211c5382d8ebe39243f9cc3a3aa6a63eb58252de8e0f_set Marketplace407871842023-03-26 10:34:4062 days 23 hrs ago0xe820cc557279acc115d61b7c450f47ff000b4ef6 IN  0x3318923722cd52c7c1752a62056532bef32877200 MATIC0.003843280
0x902df7802252a215635d8914487767040f6e51ff200dd0783b1b32856e87de7f0x60806040407869042023-03-26 10:24:4462 days 23 hrs ago0xe820cc557279acc115d61b7c450f47ff000b4ef6 IN  Create: Liquidator0 MATIC0.3018044880
[ Download CSV Export 
Parent Txn Hash Block From To Value
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Liquidator

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 300 runs

Other Settings:
default evmVersion
File 1 of 34 : AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

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

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}

File 2 of 34 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @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 AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @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]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

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

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

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @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]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.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 virtual 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.
     *
     * May emit a {RoleGranted} event.
     */
    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.
     *
     * May emit a {RoleRevoked} event.
     */
    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 revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    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.
     *
     * May emit a {RoleGranted} event.
     *
     * [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}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    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 {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 3 of 34 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @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 {AccessControl-_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 Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

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

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

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

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

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 5 of 34 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

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

File 11 of 34 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

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 IERC721Receiver {
    /**
     * @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 `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

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

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) {
        return msg.data;
    }
}

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

pragma solidity ^0.8.0;

import "./IERC165.sol";

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

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

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 IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 16 of 34 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 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) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

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

            // Use the 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 x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

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

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @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] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

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

        return result;
    }
}

File 19 of 34 : IUniswapV2Router01.sol
pragma solidity >=0.6.2;

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

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

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

File 20 of 34 : IUniswapV2Router02.sol
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

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

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

File 21 of 34 : ControllerI.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import "./HTokenI.sol";
import "./PermissionlessOracleI.sol";

/**
 * @title   Interface of Controller
 * @author  Honey Labs Inc.
 * @custom:coauthor     m4rio
 * @custom:contributor  BowTiedPickle
 */
interface ControllerI {
  /**
   * @notice returns the oracle per market
   */
  function oracle(HTokenI _hToken) external view returns (PermissionlessOracleI);

  /**
   * @notice Add assets to be included in account liquidity calculation
   * @param _hTokens The list of addresses of the hToken markets to be enabled
   */
  function enterMarkets(HTokenI[] calldata _hTokens) external;

  /**
   * @notice Removes asset from sender's account liquidity calculation
   * @dev Sender must not have an outstanding borrow balance in the asset,
   *  or be providing necessary collateral for an outstanding borrow.
   * @param _hToken The address of the asset to be removed
   */
  function exitMarket(HTokenI _hToken) external;

  /**
   * @notice Checks if the account should be allowed to deposit underlying in the market
   * @param _hToken The market to verify the redeem against
   * @param _depositor The account which that wants to deposit
   * @param _amount The number of underlying it wants to deposit
   */
  function depositUnderlyingAllowed(
    HTokenI _hToken,
    address _depositor,
    uint256 _amount
  ) external;

  /**
   * @notice Checks if the account should be allowed to borrow the underlying asset of the given market
   * @param _hToken The market to verify the borrow against
   * @param _borrower The account which would borrow the asset
   * @param _collateralId collateral Id, aka the NFT token Id
   * @param _borrowAmount The amount of underlying the account would borrow
   */
  function borrowAllowed(
    HTokenI _hToken,
    address _borrower,
    uint256 _collateralId,
    uint256 _borrowAmount
  ) external;

  /**
   * @notice Checks if the account should be allowed to deposit a collateral
   * @param _hToken The market to verify the deposit of the collateral
   * @param _depositor The account which deposits the collateral
   * @param _collateralId The collateral token id
   */
  function depositCollateralAllowed(
    HTokenI _hToken,
    address _depositor,
    uint256 _collateralId
  ) external;

  /**
   * @notice Checks if the account should be allowed to redeem tokens in the given market
   * @param _hToken The market to verify the redeem against
   * @param _redeemer The account which would redeem the tokens
   * @param _redeemTokens The number of hTokens to exchange for the underlying asset in the market
   */
  function redeemAllowed(
    HTokenI _hToken,
    address _redeemer,
    uint256 _redeemTokens
  ) external view;

  /**
   * @notice Checks if the collateral is at risk of being liquidated
   * @param _hToken The market to verify the liquidation
   * @param _collateralId collateral Id, aka the NFT token Id
   */
  function liquidationAllowed(HTokenI _hToken, uint256 _collateralId) external view;

  /**
   * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
   * @param _hToken The market to hypothetically redeem/borrow in
   * @param _account The account to determine liquidity for
   * @param _redeemTokens The number of tokens to hypothetically redeem
   * @param _borrowAmount The amount of underlying to hypothetically borrow
   * @param _collateralId collateral Id, aka the NFT token Id
   * @return liquidity - hypothetical account liquidity in excess of collateral requirements
   * @return shortfall - hypothetical account shortfall below collateral requirements
   * @return ltvShortfall - Loan to value shortfall, this is the max a user can borrow
   */
  function getHypotheticalAccountLiquidity(
    HTokenI _hToken,
    address _account,
    uint256 _collateralId,
    uint256 _redeemTokens,
    uint256 _borrowAmount
  )
    external
    view
    returns (
      uint256 liquidity,
      uint256 shortfall,
      uint256 ltvShortfall
    );

  /**
   * @notice Returns whether the given account is entered in the given asset
   * @param _hToken The hToken to check
   * @param _account The address of the account to check
   * @return True if the account is in the asset, otherwise false.
   */
  function checkMembership(HTokenI _hToken, address _account) external view returns (bool);

  /**
   * @notice Checks if the account should be allowed to transfer tokens in the given market
   * @param _hToken The market to verify the transfer against
   */
  function transferAllowed(HTokenI _hToken) external;

  /**
   * @notice Checks if the account should be allowed to repay a borrow in the given market
   * @param _hToken The market to verify the repay against
   * @param _repayAmount The amount of the underlying asset the account would repay
   * @param _collateralId collateral Id, aka the NFT token Id
   */
  function repayBorrowAllowed(
    HTokenI _hToken,
    uint256 _repayAmount,
    uint256 _collateralId
  ) external view;

  /**
   * @notice checks if withdrawal are allowed for this token id
   * @param _hToken The market to verify the withdrawal from
   * @param _collateralId what to pay for
   */
  function withdrawCollateralAllowed(HTokenI _hToken, uint256 _collateralId) external view;

  /**
   * @notice checks if a market exists and it's listed
   * @param _hToken the market we check to see if it exists
   * @return bool true or false
   */
  function marketExists(HTokenI _hToken) external view returns (bool);

  /**
   * @notice Returns market data for a specific market
   * @param _hToken the market we want to retrieved Controller data
   * @return bool If the market is listed
   * @return uint256 MAX Factor Mantissa
   * @return uint256 Collateral Factor Mantissa
   */
  function getMarketData(HTokenI _hToken)
    external
    view
    returns (
      bool,
      uint256,
      uint256
    );

  /**
   * @notice checks if an underlying exists in the market
   * @param _underlying the underlying to check if exists
   * @return bool true or false
   */
  function underlyingExistsInMarkets(address _underlying) external view returns (bool);

  /**
   * @notice checks if a collateral exists in the market
   * @param _collateral the collateral to check if exists
   * @return bool true or false
   */
  function collateralExistsInMarkets(address _collateral) external view returns (bool);

  /**
   * @notice  Checks if a certain action is paused within a market
   * @param   _hToken   The market we want to check if an action is paused
   * @param   _target   The action we want to check if it's paused
   * @return  bool true or false
   */
  function isActionPaused(HTokenI _hToken, uint256 _target) external view returns (bool);

  /**
   * @notice returns the borrow fee per market, accounts for referral
   * @param _hToken the market we want the borrow fee for
   * @param _referral referral code for Referral program of Honey Labs
   * @param _signature signed message provided by Honey Labs
   */
  function getBorrowFeePerMarket(
    HTokenI _hToken,
    string calldata _referral,
    bytes calldata _signature
  ) external view returns (uint256, bool);

  /**
   * @notice returns the borrow fee per market if provided a referral code, accounts for referral
   * @param _hToken the market we want the borrow fee for
   */
  function getReferralBorrowFeePerMarket(HTokenI _hToken) external view returns (uint256);

  // ---------- Permissioned Functions ----------

  function _supportMarket(HTokenI _hToken) external;

  function _setPriceOracle(HTokenI _hToken, PermissionlessOracleI _newOracle) external;

  function _setFactors(
    HTokenI _hToken,
    uint256 _newMaxLTVFactorMantissa,
    uint256 _newCollateralFactorMantissa
  ) external;

  function _setBorrowFeePerMarket(
    HTokenI _market,
    uint256 _fee,
    uint256 _referralFee
  ) external;

  function _pauseComponent(
    HTokenI _hToken,
    bool _state,
    uint256 _target
  ) external;
}

File 22 of 34 : HTokenI.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.8.4;
import "./HTokenInternalI.sol";

/**
 * @title   Interface of HToken
 * @author  Honey Labs Inc.
 * @custom:coauthor BowTiedPickle
 * @custom:coauthor m4rio
 */
interface HTokenI is HTokenInternalI {
  /**
   * @notice  Deposit underlying ERC-20 asset and mint hTokens
   * @dev     Pull pattern, user must approve the contract before calling. If _to is address(0) then it becomes msg.sender
   * @param   _amount   Quantity of underlying ERC-20 to transfer in
   * @param   _to       Target address to mint hTokens to
   */
  function depositUnderlying(uint256 _amount, address _to) external;

  /**
   * @notice  Redeem a specified amount of hTokens for their underlying ERC-20 asset
   * @param   _amount   Quantity of hTokens to redeem for underlying ERC-20
   */
  function redeem(uint256 _amount) external;

  /**
   * @notice  Withdraws the specified amount of underlying ERC-20 asset, consuming the minimum amount of hTokens necessary
   * @param   _amount   Quantity of underlying ERC-20 tokens to withdraw
   */
  function withdraw(uint256 _amount) external;

  /**
   * @notice  Deposit multiple specified tokens of the underlying ERC-721 asset and mint ERC-1155 deposit coupon NFTs
   * @dev     Pull pattern, user must approve the contract before calling.
   * @param   _collateralIds  Token IDs of underlying ERC-721 to be transferred in
   */
  function depositCollateral(uint256[] calldata _collateralIds) external;

  /**
   * @notice  Sender borrows assets from the protocol against the specified collateral asset, without a referral code
   * @dev     Collateral must be deposited first.
   * @param   _borrowAmount   Amount of underlying ERC-20 to borrow
   * @param   _collateralId   Token ID of underlying ERC-721 to be borrowed against
   */
  function borrow(uint256 _borrowAmount, uint256 _collateralId) external;

  /**
   * @notice  Sender borrows assets from the protocol against the specified collateral asset, using a referral code
   * @param   _borrowAmount   Amount of underlying ERC-20 to borrow
   * @param   _collateralId   Token ID of underlying ERC-721 to be borrowed against
   * @param   _referral       Referral code as a plain string
   * @param   _signature      Signed message authorizing the referral, provided by Honey Labs
   */
  function borrowReferred(
    uint256 _borrowAmount,
    uint256 _collateralId,
    string calldata _referral,
    bytes calldata _signature
  ) external;

  /**
   * @notice  Sender repays a borrow taken against the specified collateral asset
   * @dev     Pull pattern, user must approve the contract before calling.
   * @param   _repayAmount    Amount of underlying ERC-20 to repay
   * @param   _collateralId   Token ID of underlying ERC-721 to be repaid against
   */
  function repayBorrow(
    uint256 _repayAmount,
    uint256 _collateralId,
    address _to
  ) external;

  /**
   * @notice  Burn deposit coupon NFTs and withdraw the associated underlying ERC-721 NFTs
   * @param   _collateralIds  Token IDs of underlying ERC-721 to be withdrawn
   */
  function withdrawCollateral(uint256[] calldata _collateralIds) external;

  /**
   * @notice  Trigger transfer of an NFT to the liquidation contract
   * @param   _collateralId   Token ID of underlying ERC-721 to be liquidated
   */
  function liquidateBorrow(uint256 _collateralId) external;

  /**
   * @notice  Pay off the entirety of a liquidated debt position and burn the coupon
   * @dev     May only be called by the liquidator
   * @param   _borrower       Owner of the debt position
   * @param   _collateralId   Token ID of underlying ERC-721 to be closed out
   */
  function closeoutLiquidation(address _borrower, uint256 _collateralId) external;

  /**
   * @notice  Accrues all interest due to the protocol
   * @dev     Call this before performing calculations using 'totalBorrows' or other contract-wide quantities
   */
  function accrueInterest() external;

  // ----- Utility functions -----

  /**
   * @notice  Sweep accidental ERC-20 transfers to this contract.
   * @dev     Tokens are sent to the DAO for later distribution
   * @param   _token  The address of the ERC-20 token to sweep
   */
  function sweepToken(IERC20 _token) external;
}

File 23 of 34 : HTokenInternalI.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

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

/**
 * @title   Interface of HToken Internal
 * @author  Honey Labs Inc.
 * @custom:coauthor m4rio
 * @custom:coauthor BowTiedPickle
 */
interface HTokenInternalI is IERC1155, IAccessControl {
  struct Coupon {
    uint32 id; //Coupon's id
    uint8 active; // Coupon activity status
    address owner; // Who is the current owner of this coupon
    uint256 collateralId; // tokenId of the collateral collection that is borrowed against
    uint256 borrowAmount; // Principal borrow balance, denominated in underlying ERC20 token.
    uint256 debtShares; // Debt shares, keeps the shares of total debt by the protocol
  }

  struct Collateral {
    uint256 collateralId; // TokenId of the collateral
    bool active; // Collateral activity status
  }

  // ----- Informational -----

  function decimals() external view returns (uint8);

  function name() external view returns (string memory);

  function symbol() external view returns (string memory);

  // ----- Addresses -----

  function collateralToken() external view returns (IERC721);

  function underlyingToken() external view returns (IERC20);

  // ----- Protocol Accounting -----

  function totalBorrows() external view returns (uint256);

  function totalReserves() external view returns (uint256);

  function totalSupply() external view returns (uint256);

  function totalFuseFees() external view returns (uint256);

  function totalAdminCommission() external view returns (uint256);

  function accrualBlockNumber() external view returns (uint256);

  function interestIndexStored() external view returns (uint256);

  function totalProtocolCommissions() external view returns (uint256);

  function userToCoupons(address _user) external view returns (uint256);

  function collateralPerBorrowCouponId(uint256 _couponId) external view returns (Collateral memory);

  function borrowCoupons(uint256 _collateralId) external view returns (Coupon memory);

  // ----- Views -----

  /**
   * @notice  Get the outstanding debt of a collateral
   * @dev     Simulates accrual of interest
   * @param   _collateralId   Token ID of underlying ERC-721
   * @return  Outstanding debt in units of underlying ERC-20
   */
  function getDebtForCollateral(uint256 _collateralId) external view returns (uint256);

  /**
   * @notice  Returns the current per-block borrow interest rate for this hToken
   * @return  The borrow interest rate per block, scaled by 1e18
   */
  function borrowRatePerBlock() external view returns (uint256);

  /**
   * @notice  Get the outstanding debt of a coupon
   * @dev     Simulates accrual of interest
   * @param   _couponId   ID of the coupon
   * @return  Outstanding debt in units of underlying ERC-20
   */
  function getDebtForCoupon(uint256 _couponId) external view returns (uint256);

  /**
   * @notice  Gets balance of this contract in terms of the underlying excluding the fees
   * @dev     This excludes the value of the current message, if any
   * @return  The quantity of underlying ERC-20 tokens owned by this contract
   */
  function getCashPrior() external view returns (uint256);

  /**
   * @notice  Get a snapshot of the account's balances, and the cached exchange rate
   * @dev     This is used by controller to more efficiently perform liquidity checks.
   * @param   _account  Address of the account to snapshot
   * @return  (token balance, borrow balance, exchange rate mantissa)
   */
  function getAccountSnapshot(address _account)
    external
    view
    returns (
      uint256,
      uint256,
      uint256
    );

  /**
   * @notice  Get the outstanding debt of the protocol
   * @return  Protocol debt
   */
  function getDebt() external view returns (uint256);

  /**
   * @notice  Returns protocol fees
   * @return  Reserve factor mantissa
   * @return  Admin fee mantissa
   * @return  Hive fee mantissa
   * @return  Initial exchange rate mantissa
   * @return  Maximum borrow rate mantissa
   */
  function getProtocolFees()
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256,
      uint256
    );

  /**
   * @notice  Returns different addresses of the protocol
   * @return  Liquidator address
   * @return  HTokenHelper address
   * @return  Controller address
   * @return  Admin Fee Receiver address
   * @return  Hive Fee Receiver address
   * @return  Interest Model address
   * @return  Referral Pool address
   * @return  DAO address
   */
  function getAddresses()
    external
    view
    returns (
      address,
      address,
      address,
      address,
      address,
      address,
      address,
      address
    );

  /**
   * @notice  Get the last minted coupon ID
   * @return  The last minted coupon ID
   */
  function idCounter() external view returns (uint256);

  /**
   * @notice  Get the coupon for a specific collateral NFT
   * @param   _collateralId   Token ID of underlying ERC-721
   * @return  Coupon
   */
  function getSpecificCouponByCollateralId(uint256 _collateralId) external view returns (Coupon memory);

  /**
   * @notice  Calculate the prevailing interest due per token of debt principal
   * @return  Mantissa formatted interest rate per token of debt
   */
  function interestIndex() external view returns (uint256);

  /**
   * @notice  Accrue interest then return the up-to-date exchange rate from the ERC-20 underlying to the HToken
   * @return  Calculated exchange rate scaled by 1e18
   */
  function exchangeRateCurrent() external returns (uint256);

  /**
   * @notice  Calculates the exchange rate from the ERC-20 underlying to the HToken
   * @dev     This function does not accrue interest before calculating the exchange rate
   * @return  Calculated exchange rate scaled by 1e18
   */
  function exchangeRateStored() external view returns (uint256);

  /**
   * @notice  Add to or take away from reserves
   * @dev     Accrues interest
   * @param   _amount  Quantity of underlying ERC-20 token to change the reserves by
   */
  function _modifyReserves(uint256 _amount, bool _add) external;

  /**
   * @notice  Set new admin fee mantissas
   * @dev     Accrues interest
   * @param   _newAdminCommissionMantissa        New admin fee mantissa
   */
  function _setAdminCommission(uint256 _newAdminCommissionMantissa) external;

  /**
   * @notice  Set new protocol commission and reserve factor mantissas
   * @dev     Accrues interest
   * @param   _newProtocolCommissionMantissa         New protocol commission mantissa
   * @param   _newReserveFactorMantissa   New reserve factor mantissa
   */
  function _setProtocolFees(uint256 _newProtocolCommissionMantissa, uint256 _newReserveFactorMantissa) external;

  /**
   * @notice  Sets a new admin fee receiver
   * @param   _newAddress   Address of the new admin fee receiver
   * @param   _target       Target ID of the address to be set
   */
  function _setAddressMarketAdmin(address _newAddress, uint256 _target) external;
}

File 24 of 34 : INFTXVault.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.8.4;

import "./INFTXVaultFactory.sol";

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

interface INFTXEligibility {
  // Read functions.
  function name() external pure returns (string memory);

  function finalized() external view returns (bool);

  function targetAsset() external pure returns (address);

  function checkAllEligible(uint256[] calldata tokenIds) external view returns (bool);

  function checkEligible(uint256[] calldata tokenIds) external view returns (bool[] memory);

  function checkAllIneligible(uint256[] calldata tokenIds) external view returns (bool);

  function checkIsEligible(uint256 tokenId) external view returns (bool);

  // Write functions.
  function __NFTXEligibility_init_bytes(bytes calldata configData) external;

  function beforeMintHook(uint256[] calldata tokenIds) external;

  function afterMintHook(uint256[] calldata tokenIds) external;

  function beforeRedeemHook(uint256[] calldata tokenIds) external;

  function afterRedeemHook(uint256[] calldata tokenIds) external;
}

interface INFTXVault is IERC20Upgradeable {
  function manager() external view returns (address);

  function assetAddress() external view returns (address);

  function vaultFactory() external view returns (INFTXVaultFactory);

  function eligibilityStorage() external view returns (INFTXEligibility);

  function is1155() external view returns (bool);

  function allowAllItems() external view returns (bool);

  function enableMint() external view returns (bool);

  function enableRandomRedeem() external view returns (bool);

  function enableTargetRedeem() external view returns (bool);

  function enableRandomSwap() external view returns (bool);

  function enableTargetSwap() external view returns (bool);

  function vaultId() external view returns (uint256);

  function nftIdAt(uint256 holdingsIndex) external view returns (uint256);

  function allHoldings() external view returns (uint256[] memory);

  function totalHoldings() external view returns (uint256);

  function mintFee() external view returns (uint256);

  function randomRedeemFee() external view returns (uint256);

  function targetRedeemFee() external view returns (uint256);

  function randomSwapFee() external view returns (uint256);

  function targetSwapFee() external view returns (uint256);

  function vaultFees() external view returns (uint256, uint256, uint256, uint256, uint256);

  event VaultInit(uint256 indexed vaultId, address assetAddress, bool is1155, bool allowAllItems);

  event ManagerSet(address manager);
  event EligibilityDeployed(uint256 moduleIndex, address eligibilityAddr);
  // event CustomEligibilityDeployed(address eligibilityAddr);

  event EnableMintUpdated(bool enabled);
  event EnableRandomRedeemUpdated(bool enabled);
  event EnableTargetRedeemUpdated(bool enabled);
  event EnableRandomSwapUpdated(bool enabled);
  event EnableTargetSwapUpdated(bool enabled);

  event Minted(uint256[] nftIds, uint256[] amounts, address to);
  event Redeemed(uint256[] nftIds, uint256[] specificIds, address to);
  event Swapped(uint256[] nftIds, uint256[] amounts, uint256[] specificIds, uint256[] redeemedIds, address to);

  function __NFTXVault_init(
    string calldata _name,
    string calldata _symbol,
    address _assetAddress,
    bool _is1155,
    bool _allowAllItems
  ) external;

  function finalizeVault() external;

  function setVaultMetadata(string memory name_, string memory symbol_) external;

  function setVaultFeatures(
    bool _enableMint,
    bool _enableRandomRedeem,
    bool _enableTargetRedeem,
    bool _enableRandomSwap,
    bool _enableTargetSwap
  ) external;

  function setFees(
    uint256 _mintFee,
    uint256 _randomRedeemFee,
    uint256 _targetRedeemFee,
    uint256 _randomSwapFee,
    uint256 _targetSwapFee
  ) external;

  function disableVaultFees() external;

  // This function allows for an easy setup of any eligibility module contract from the EligibilityManager.
  // It takes in ABI encoded parameters for the desired module. This is to make sure they can all follow
  // a similar interface.
  function deployEligibilityStorage(uint256 moduleIndex, bytes calldata initData) external returns (address);

  // The manager has control over options like fees and features
  function setManager(address _manager) external;

  function mint(
    uint256[] calldata tokenIds,
    uint256[] calldata amounts /* ignored for ERC721 vaults */
  ) external returns (uint256);

  function mintTo(
    uint256[] calldata tokenIds,
    uint256[] calldata amounts /* ignored for ERC721 vaults */,
    address to
  ) external returns (uint256);

  function redeem(uint256 amount, uint256[] calldata specificIds) external returns (uint256[] calldata);

  function redeemTo(uint256 amount, uint256[] calldata specificIds, address to) external returns (uint256[] calldata);

  function swap(
    uint256[] calldata tokenIds,
    uint256[] calldata amounts /* ignored for ERC721 vaults */,
    uint256[] calldata specificIds
  ) external returns (uint256[] calldata);

  function swapTo(
    uint256[] calldata tokenIds,
    uint256[] calldata amounts /* ignored for ERC721 vaults */,
    uint256[] calldata specificIds,
    address to
  ) external returns (uint256[] calldata);

  function allValidNFTs(uint256[] calldata tokenIds) external view returns (bool);
}

File 25 of 34 : INFTXVaultFactory.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.8.4;

interface IBeacon {
  /**
   * @dev Must return an address that can be used as a delegate call target.
   *
   * {BeaconProxy} will check that this address is a contract.
   */
  function childImplementation() external view returns (address);

  function upgradeChildTo(address newImplementation) external;
}

interface INFTXVaultFactory is IBeacon {
  // Read functions.
  function numVaults() external view returns (uint256);

  function zapContract() external view returns (address);

  function feeDistributor() external view returns (address);

  function eligibilityManager() external view returns (address);

  function vault(uint256 vaultId) external view returns (address);

  function allVaults() external view returns (address[] memory);

  function vaultsForAsset(address asset) external view returns (address[] memory);

  function isLocked(uint256 id) external view returns (bool);

  function excludedFromFees(address addr) external view returns (bool);

  function factoryMintFee() external view returns (uint64);

  function factoryRandomRedeemFee() external view returns (uint64);

  function factoryTargetRedeemFee() external view returns (uint64);

  function factoryRandomSwapFee() external view returns (uint64);

  function factoryTargetSwapFee() external view returns (uint64);

  function vaultFees(uint256 vaultId) external view returns (uint256, uint256, uint256, uint256, uint256);

  event NewFeeDistributor(address oldDistributor, address newDistributor);
  event NewZapContract(address oldZap, address newZap);
  event FeeExclusion(address feeExcluded, bool excluded);
  event NewEligibilityManager(address oldEligManager, address newEligManager);
  event NewVault(uint256 indexed vaultId, address vaultAddress, address assetAddress);
  event UpdateVaultFees(
    uint256 vaultId,
    uint256 mintFee,
    uint256 randomRedeemFee,
    uint256 targetRedeemFee,
    uint256 randomSwapFee,
    uint256 targetSwapFee
  );
  event DisableVaultFees(uint256 vaultId);
  event UpdateFactoryFees(
    uint256 mintFee,
    uint256 randomRedeemFee,
    uint256 targetRedeemFee,
    uint256 randomSwapFee,
    uint256 targetSwapFee
  );

  // Write functions.
  function __NFTXVaultFactory_init(address _vaultImpl, address _feeDistributor) external;

  function createVault(
    string calldata name,
    string calldata symbol,
    address _assetAddress,
    bool is1155,
    bool allowAllItems
  ) external returns (uint256);

  function setFeeDistributor(address _feeDistributor) external;

  function setEligibilityManager(address _eligibilityManager) external;

  function setZapContract(address _zapContract) external;

  function setFeeExclusion(address _excludedAddr, bool excluded) external;

  function setFactoryFees(
    uint256 mintFee,
    uint256 randomRedeemFee,
    uint256 targetRedeemFee,
    uint256 randomSwapFee,
    uint256 targetSwapFee
  ) external;

  function setVaultFees(
    uint256 vaultId,
    uint256 mintFee,
    uint256 randomRedeemFee,
    uint256 targetRedeemFee,
    uint256 randomSwapFee,
    uint256 targetSwapFee
  ) external;

  function disableVaultFees(uint256 vaultId) external;
}

File 26 of 34 : LiquidatorI.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.8.4;

import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "../interfaces/HTokenI.sol";

/**
 * @title   Interface of Liquidator
 * @author  Honey Labs Inc.
 * @custom:coauthor     BowTiedPickle
 * @custom:contributor  m4rio
 */
interface LiquidatorI is IERC721Receiver {
  function isRegisteredUnderlying(address _token) external view returns (bool);

  function isRegisteredHToken(address _hToken) external view returns (bool);

  function _initializeHToken(HTokenI _hToken) external;
}

File 27 of 34 : MarketplaceI.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity >=0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import ".././interfaces/HTokenI.sol";

/**
 * @title   Interface of Marketplace
 * @author  Honey Labs Inc.
 * @custom:coauthor BowTiedPickle
 * @custom:coauthor m4rio
 */
interface MarketplaceI {
  struct Auction {
    IERC20 underlying;
    address highestBidder;
    uint256 highestBid;
    address[50] bidders; // Arrays are sorted ascending
    uint256[50] bids;
    uint256[50] unlockTimes;
  }

  function toggleLiquidation(
    HTokenI _hToken,
    uint256 _collateralId,
    bool _enabled
  ) external;

  function bidSingle(
    HTokenI _hToken,
    uint256 _collateralId,
    uint256 _amount
  ) external;

  function increaseBidSingle(
    HTokenI _hToken,
    uint256 _collateralId,
    uint256 _increaseAmount
  ) external;

  function bidCollection(HTokenI _hToken, uint256 _amount) external;

  function increaseBidCollection(HTokenI _hToken, uint256 _increaseAmount) external;

  function settleAuction(
    HTokenI _hToken,
    address _borrower,
    uint256 _collateralId
  ) external;

  function withdrawRefund(IERC20 _token) external returns (uint256);

  function cancelBidSingle(HTokenI _hToken, uint256 _collateralId) external;

  function cancelBidCollection(HTokenI _hToken) external;

  function viewMinimumNextBidSingle(HTokenI _hToken, uint256 _collateralId) external view returns (uint256);

  function viewMinimumNextBidCollection(HTokenI _hToken) external view returns (uint256);

  function viewAuctionSingle(HTokenI _hToken, uint256 _collateralId) external view returns (Auction memory);

  function viewAuctionCollection(HTokenI _hToken) external view returns (Auction memory);

  function viewAvailableRefund(IERC20 _token, address _user) external view returns (uint256);

  function viewUserBidSingle(
    address _user,
    HTokenI _hToken,
    uint256 _collateralId
  ) external view returns (uint256, uint256);

  function viewUserBidCollection(address _user, HTokenI _hToken) external view returns (uint256, uint256);

  function _refundAllBidsPerCollateral(HTokenI _hToken, uint256 _collateralId) external;

  function _refundAllBidsPerCollection(HTokenI _hToken) external;
}

File 28 of 34 : NFT20I.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

interface NFT20Factory {
  function nftToToken(address) external returns (address);
}

interface NFT20Pair {
  function nftAddress() external returns (address);
}

File 29 of 34 : PermissionlessOracleI.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.4;

import "./HTokenI.sol";

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

/**
 * @title   PermissionlessOracleI interface for the Permissionless oracle
 * @author  Honey Labs Inc.
 * @custom:coauthor BowTiedPickle
 * @custom:coauthor m4rio
 */
interface PermissionlessOracleI {
  /**
   * @notice returns the price (in eth) for the floor of a collection
   * @param _collection address of the collection
   * @param _decimals adjust decimals of the returned price
   */
  function getFloorPrice(address _collection, uint256 _decimals) external view returns (uint128, uint128);

  /**
   * @notice returns the latest price for a given pair
   * @param _erc20 the erc20 we want to get the price for in USD
   * @param _decimals decimals to denote the result in
   */
  function getUnderlyingPriceInUSD(IERC20 _erc20, uint256 _decimals) external view returns (uint256);

  /**
   * @notice get price of eth
   * @param _decimals adjust decimals of the returned price
   */
  function getEthPrice(uint256 _decimals) external view returns (uint256);

  /**
   * @notice get price feeds for a token
   * @return returns the Chainlink Aggregator interface
   */
  function priceFeeds(IERC20 _token) external view returns (AggregatorV3Interface);

  /**
   * @notice returns the update threshold for a specific _collection
   */
  function updateThreshold(address _collection) external view returns (uint256);

  /**
   * @notice returns the number of floors for a specific _collection
   * @param _address address of the collection
   *
   */
  function getNoOfFloors(address _address) external view returns (uint256);

  /**
   * @notice returns the last updated timestamp for a specific _collection
   * @param _collection address of the collection
   *
   */
  function getLastUpdated(address _collection) external view returns (uint256);
}

File 30 of 34 : Liquidator.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.15;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import ".././interfaces/HTokenI.sol";
import ".././interfaces/LiquidatorI.sol";
import ".././interfaces/MarketplaceI.sol";
import ".././interfaces/ControllerI.sol";
import ".././utils/ErrorReporter.sol";
import "./LiquidatorModuleNFT20.sol";
import "./LiquidatorModuleNFTX.sol";
import "./LiquidatorStorage.sol";

/**
 * @title Honey Finance Liquidator
 * @notice Execute liquidations for HToken IRM contracts
 * @author Honey Labs inc
 * @custom:coauthor     BowTiedPickle
 * @custom:contributor  m4rio
 */
contract Liquidator is
  IERC721Receiver,
  AccessControl,
  ReentrancyGuard,
  Pausable,
  LiquidatorI,
  LiquidatorStorage,
  LiquidatorModuleNFT20,
  LiquidatorModuleNFTX
{
  using SafeERC20 for IERC20;
  using EnumerableSet for EnumerableSet.AddressSet;

  /// @notice this corresponds to 1.0.0
  uint256 public constant version = 1_000_000;

  // ----- Key External Addresses -----

  /// @notice Honey Finance Treasury
  address public treasury;

  /// @notice Controller address
  ControllerI public controller;

  // ----- State Variables -----

  /// @notice HToken to address of its underlying ERC20
  mapping(HTokenI => IERC20) public poolToUnderlyingToken;

  /// @notice HToken to address of its collateral ERC721
  mapping(HTokenI => IERC721) public poolToCollateralToken;

  /// @notice List of registered underlying tokens
  EnumerableSet.AddressSet internal registeredUnderlyingTokens;

  /// @notice List of registered pools
  EnumerableSet.AddressSet internal registeredPools;

  // ----- Events -----

  event MarketplaceUpdated(MarketplaceI indexed _oldMarketplace, MarketplaceI indexed _newMarketplace);
  event HTokenInitialized(address indexed _hToken, address indexed _underlying, address indexed _collateral);
  event TokenSwept(IERC20 indexed _token, uint256 _qty);
  event ProfitsWithdrawn(IERC20 indexed _token, uint256 _qty);
  event NFTWithdrawn(address indexed _hToken, IERC721 indexed _collateralToken, uint256 _collateralId);
  event LiquidatorPaused(bool _paused);
  event TreasuryUpdated(address _oldTreasury, address _newTreasury);
  event ControllerUpdated(ControllerI _oldController, address _newController);
  event UniswapV2RouterUpdated(IUniswapV2Router02 _oldRouter, IUniswapV2Router02 _newRouter);

  // ----- Construction -----

  /**
   * @param _treasury     Address that will receive profits and swept tokens
   * @param _controller   Controller address
   */
  constructor(address _treasury, ControllerI _controller) LiquidatorStorage() {
    if (_treasury == address(0)) revert WrongParams();
    if (address(_controller) == address(0)) revert WrongParams();

    _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _grantRole(INITIALIZER_ROLE, msg.sender);
    _grantRole(LIQUIDATOR_ROLE, msg.sender);
    _grantRole(SWAPPER_ROLE, msg.sender);
    _grantRole(PAUSER_ROLE, msg.sender);

    treasury = _treasury;
    controller = _controller;
  }

  // ----- Configuration Functions -----

  /**
   * @notice  Perform the setup to handle liquidations from the given HToken
   * @param   _hToken address of the hToken to setup
   */
  function _initializeHToken(HTokenI _hToken) external whenNotPaused nonReentrant onlyRole(INITIALIZER_ROLE) {
    if (!controller.marketExists(_hToken)) revert Uninitialized();
    if (registeredPools.contains(address(_hToken))) revert Initialized();

    if (!_hToken.supportsInterface(type(HTokenI).interfaceId)) revert WrongParams();

    // Add underlyings to mapping
    IERC20 underlyingToken = _hToken.underlyingToken();
    IERC721 collateralToken = _hToken.collateralToken();
    poolToUnderlyingToken[_hToken] = underlyingToken;
    poolToCollateralToken[_hToken] = collateralToken;
    registeredUnderlyingTokens.add(address(underlyingToken));
    registeredPools.add(address(_hToken));

    // Unlimited approve the underlying token of the hToken
    underlyingToken.safeIncreaseAllowance(address(_hToken), type(uint256).max);

    emit HTokenInitialized(address(_hToken), address(underlyingToken), address(collateralToken));
  }

  // ----- View Functions -----

  /**
   * @notice  View whether an ERC-20 token is registered as an underlying token in this contract
   * @param   _token Address of the ERC-20 token in question
   * @return  True if registered
   */
  function isRegisteredUnderlying(address _token) external view override returns (bool) {
    return registeredUnderlyingTokens.contains(address(_token));
  }

  /**
   * @notice  View whether an hToken is registered in this contract
   * @param   _hToken Address of the hToken in question
   * @return  True if registered
   */
  function isRegisteredHToken(address _hToken) external view override returns (bool) {
    return registeredPools.contains(address(_hToken));
  }

  // ----- Transfer Hook -----

  /**
   * @notice ERC721 safeTransferFrom transfer hook
   * @dev Will only return the magic value if data contains a valid encoded address of HToken contract
   * @inheritdoc IERC721Receiver
   */
  function onERC721Received(
    address,
    address,
    uint256 _tokenId,
    bytes calldata data
  ) public virtual override whenNotPaused returns (bytes4) {
    if (data.length > 0) {
      // Data must contain encoded address of the HToken contract
      // Decode data to retrieve address
      address reconstructedAddress = abi.decode(data, (address));

      // Legitimate safeTransferFrom will come from token contract
      IERC721 collateralToken = poolToCollateralToken[HTokenI(reconstructedAddress)];

      if (address(collateralToken) == address(0)) revert Uninitialized();
      if (msg.sender != address(collateralToken)) revert Unauthorized();

      // Check that the NFT was actually transferred
      if (collateralToken.ownerOf(_tokenId) != address(this)) revert Unauthorized();

      marketplace.toggleLiquidation(HTokenI(reconstructedAddress), _tokenId, true);

      // approving the marketplace to transfer this token
      collateralToken.approve(address(marketplace), _tokenId);

      return this.onERC721Received.selector;
    } else {
      return bytes4(0);
    }
  }

  // ----- AMM Liquidation Hooks -----

  /**
   * @dev     Called before NFT20 liquidation. Should handle auctions, refunding, and any other logic.
   * @param   _hToken           Contract address of the hToken
   * @param   _collateralId     NFT tokenId
   * @return  (Collateral ERC-721 address, vault address)
   */
  function _NFT20PreLiquidationHook(HTokenI _hToken, uint256 _collateralId)
    internal
    override
    whenNotPaused
    returns (IERC721, address)
  {
    // Only if HToken has been initialized
    if (address(_hToken) == address(0)) revert LiquidatorError(Error.TOKEN_LOOKUP_ERROR);
    if (!registeredPools.contains(address(_hToken))) revert Uninitialized();

    IERC721 collateralToken = poolToCollateralToken[_hToken];

    // Only if NFT20 pair exists
    address pair = poolToNiftyPair[_hToken];
    if (pair == address(0)) revert LiquidatorError(Error.NFT20_PAIR_NOT_FOUND);

    // Only against active deposit coupons
    if (_hToken.getSpecificCouponByCollateralId(_collateralId).active != COUPON_LIQUIDATED) revert InvalidCoupon();

    // May only execute if this contract owns the relevant NFT
    if (collateralToken.ownerOf(_collateralId) != address(this)) revert LiquidatorError(Error.TOKEN_NOT_PRESENT);

    // Update marketplace
    marketplace.toggleLiquidation(_hToken, _collateralId, false);

    return (collateralToken, pair);
  }

  /**
   * @dev     Called before NFTX droplet swap. Should handle auctions, refunding, and any other logic.
   * @param   _hToken           Contract address of the hToken
   * @param   _collateralId     NFT tokenId
   * @return  (Collateral ERC-721 address, underlying ERC-20 address)
   */
  function _NFT20PreSwapHook(HTokenI _hToken, uint256 _collateralId) internal view override returns (IERC721, IERC20) {
    IERC20 underlyingToken = poolToUnderlyingToken[_hToken];
    IERC721 collateralToken = poolToCollateralToken[_hToken];

    // Only if HToken has been initialized
    if (!registeredPools.contains(address(_hToken))) revert Uninitialized();
    if (address(underlyingToken) == address(0)) revert Uninitialized();

    HTokenI.Coupon memory activeCoupon = _hToken.getSpecificCouponByCollateralId(_collateralId);

    // Only against active coupons
    if (activeCoupon.active != COUPON_LIQUIDATED) revert InvalidCoupon();

    return (collateralToken, underlyingToken);
  }

  /**
   * @dev     Called before NFTX liquidation. Should handle auctions, refunding, and any other logic.
   * @param   _hToken Contract address of the HToken
   * @param   _collateralId NFT tokenId
   * @param   _vaultIndex 0 if only one vault exists, otherwise index of the desired vault in the address[] mapping of poolToNFTXVaults
   * @return  (Collateral ERC-721 address, vault address)
   */
  function _NFTXPreLiquidationHook(
    HTokenI _hToken,
    uint256 _collateralId,
    uint256 _vaultIndex
  ) internal override whenNotPaused returns (IERC721, address) {
    IERC20 underlyingToken = poolToUnderlyingToken[_hToken];
    IERC721 collateralToken = poolToCollateralToken[_hToken];

    // Only if HToken has been initialized
    if (address(_hToken) == address(0)) revert LiquidatorError(Error.TOKEN_LOOKUP_ERROR);
    if (address(underlyingToken) == address(0)) revert Uninitialized();

    // Only if NFTX vault exists
    address[] storage vaults = poolToNFTXVaults[_hToken];
    if (vaults.length == 0) revert LiquidatorError(Error.NFTX_PAIR_NOT_FOUND);
    address pair = vaults[_vaultIndex];

    // Only against active deposit coupons
    HTokenI.Coupon memory activeCoupon = _hToken.getSpecificCouponByCollateralId(_collateralId);
    if (activeCoupon.active != COUPON_LIQUIDATED) revert InvalidCoupon();

    // May only execute if this contract owns the relevant NFT
    if (collateralToken.ownerOf(_collateralId) != address(this)) revert LiquidatorError(Error.TOKEN_NOT_PRESENT);

    // Update marketplace
    marketplace.toggleLiquidation(_hToken, _collateralId, false);

    return (collateralToken, pair);
  }

  /**
   * @dev     Called before NFTX droplet swap. Should handle auctions, refunding, and any other logic.
   * @param   _hToken         Contract address of the hToken
   * @param   _collateralId   NFT tokenId
   * @return  (Collateral ERC-721 address, underlying ERC-20 address)
   */
  function _NFTXPreSwapHook(HTokenI _hToken, uint256 _collateralId) internal view override returns (IERC721, IERC20) {
    IERC20 underlyingToken = poolToUnderlyingToken[_hToken];
    IERC721 collateralToken = poolToCollateralToken[_hToken];

    // Only if HToken has been initialized
    if (!registeredPools.contains(address(_hToken))) revert Uninitialized();
    if (address(underlyingToken) == address(0)) revert Uninitialized();

    HTokenI.Coupon memory activeCoupon = _hToken.getSpecificCouponByCollateralId(_collateralId);

    // Only against active coupons
    if (activeCoupon.active != COUPON_LIQUIDATED) revert InvalidCoupon();

    return (collateralToken, underlyingToken);
  }

  function postSwapHook(
    HTokenI _hToken,
    IERC20 _underlyingToken,
    uint256 _collateralId
  ) internal override(LiquidatorModuleNFT20, LiquidatorModuleNFTX) {
    super.postSwapHook(_hToken, _underlyingToken, _collateralId);
  }

  function swapExactUniV2(
    uint256 _amountIn,
    uint256 _amountOutMin,
    address[] memory _path
  ) internal override(LiquidatorModuleNFT20, LiquidatorModuleNFTX) returns (uint256[] memory) {
    return super.swapExactUniV2(_amountIn, _amountOutMin, _path);
  }

  // ----- Utility Functions -----

  /**
   * @dev     Transfer the given amount of the given underlying token to this contract
   * @dev     Requires this contract to be adequately approved to transfer the amount
   * @param   _underlyingToken  The ERC20 token to transfer
   * @param   _from             Address to transfer from
   * @param   _amount           The quantity of tokens to transfer
   * @return  Quantity of tokens actually transferred
   */
  function doUnderlyingTransferIn(
    IERC20 _underlyingToken,
    address _from,
    uint256 _amount
  ) internal returns (uint256) {
    uint256 balanceBefore = _underlyingToken.balanceOf(address(this));
    _underlyingToken.safeTransferFrom(_from, address(this), _amount);
    uint256 balanceAfter = _underlyingToken.balanceOf(address(this));

    if (balanceAfter < balanceBefore) revert Unexpected("Transfer invariant error");
    unchecked {
      return balanceAfter - balanceBefore;
    }
  }

  // ----- Administrative Functions -----

  function _manualCloseout(
    HTokenI _hToken,
    address _borrower,
    uint256 _collateralId
  ) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
    if (!registeredPools.contains(address(_hToken))) revert Uninitialized();

    _hToken.accrueInterest();
    uint256 debt = _hToken.getDebtForCollateral(_collateralId);
    if (debt == 0) revert LiquidatorError(Error.TOKEN_DEBT_NONEXISTENT);

    IERC20 underlyingToken = poolToUnderlyingToken[_hToken];

    // Intake funds
    doUnderlyingTransferIn(underlyingToken, msg.sender, debt);

    // Closeout and repay funds
    _hToken.closeoutLiquidation(_borrower, _collateralId);

    emit BorrowRepaid(address(_hToken), _borrower, _collateralId);
  }

  /**
   * @notice  Sweep accidental ERC-20 transfers to this contract, or withdraw droplets for OTC. Tokens are sent to treasury
   * @dev     Cannot be used to withdraw underlying tokens
   * @param   _token The address of the ERC-20 token to sweep
   */
  function _sweepToken(IERC20 _token) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
    if (registeredUnderlyingTokens.contains(address(_token))) revert Unauthorized();

    uint256 balance = _token.balanceOf(address(this));
    if (balance > 0) {
      _token.safeTransfer(treasury, balance);
    }
    emit TokenSwept(_token, balance);
  }

  /**
   * @notice  Withdraw the profits earned by the protocol to the treasury
   * @dev     Cannot be used to withdraw dust tokens
   * @param   _token ERC-20 token to withdraw
   */
  function _withdrawProfits(IERC20 _token) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
    if (!registeredUnderlyingTokens.contains(address(_token))) revert Unauthorized();

    uint256 profits = _token.balanceOf(address(this));

    if (profits > 0) {
      _token.safeTransfer(treasury, profits);
    }

    emit ProfitsWithdrawn(_token, profits);
  }

  /**
   * @notice  Withdraw an NFT to the treasury for use in OTC or manual closeout.
   * @param   _hToken         The hToken used to determine the collection
   * @param   _collateralId   The collateral id that we must withdraw
   */
  function _withdrawNFT(HTokenI _hToken, uint256 _collateralId) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
    IERC721 collateralToken = poolToCollateralToken[_hToken];

    marketplace.toggleLiquidation(_hToken, _collateralId, false);
    collateralToken.safeTransferFrom(address(this), treasury, _collateralId);

    emit NFTWithdrawn(address(_hToken), collateralToken, _collateralId);
  }

  /**
   * @notice Set the marketplace module address
   * @param  _marketplace The new marketplace
   */
  function _setMarketplace(MarketplaceI _marketplace) external onlyRole(DEFAULT_ADMIN_ROLE) {
    if (address(_marketplace) == address(0)) revert WrongParams();
    emit MarketplaceUpdated(marketplace, _marketplace);
    marketplace = _marketplace;
  }

  /**
   * @notice Set the new UniV2 compatible router address
   * @param  _uniswapV2Router The new router
   */
  function _setUniswapV2Router(IUniswapV2Router02 _uniswapV2Router) external onlyRole(DEFAULT_ADMIN_ROLE) {
    emit UniswapV2RouterUpdated(swapRouter, _uniswapV2Router);
    swapRouter = _uniswapV2Router;
  }

  /**
   * @notice Set the marketplace treasury address
   * @param  _newTreasury The new treasury address
   */
  function _setTreasury(address _newTreasury) external onlyRole(DEFAULT_ADMIN_ROLE) {
    if (_newTreasury == address(0)) revert WrongParams();
    emit TreasuryUpdated(treasury, _newTreasury);
    treasury = _newTreasury;
  }

  /**
   * @notice Set the controller address
   * @param  _newController The new controller address
   */
  function _setController(address _newController) external onlyRole(DEFAULT_ADMIN_ROLE) {
    if (_newController == address(0)) revert WrongParams();
    emit ControllerUpdated(controller, _newController);
    controller = ControllerI(_newController);
  }

  /**
   * @notice  Pause the AMM liquidation functionality
   * @param   _state True to pause, false to unpause
   */
  function _pauseLiquidator(bool _state) external onlyRole(PAUSER_ROLE) {
    if (_state) _pause();
    else _unpause();
    emit LiquidatorPaused(_state);
  }
}

File 31 of 34 : LiquidatorModuleNFT20.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.15;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

import ".././interfaces/HTokenI.sol";
import "./LiquidatorStorage.sol";
import ".././utils/ErrorReporter.sol";

import { NFT20Factory, NFT20Pair } from ".././interfaces/NFT20I.sol";

/**
 * @title   Honey Finance Liquidator Module - NFT20
 * @notice  Execute liquidations via NFT20 for HToken IRM contracts
 * @author  Honey Labs inc
 * @custom:coauthor     BowTiedPickle
 * @custom:contributor  m4rio
 */
abstract contract LiquidatorModuleNFT20 is AccessControl, ReentrancyGuard, LiquidatorStorage {
  using SafeERC20 for IERC20;

  /// @notice NFT20 pool factory
  NFT20Factory public niftyFactory;

  /// @notice HToken to NFT20 pool for that collateral
  mapping(HTokenI => address) public poolToNiftyPair;

  event NFTFactoryInitialized(address _factory);
  event NFT20PairInitialized(address _pair, address _hToken);
  event NFT20LiquidationExecuted(address indexed _pair, address indexed _hToken, uint256 _collateralId);

  /**
   * @notice  Initializes the NFT20Factory that will be queried
   * @dev     May only be called once
   * @param   _NFT20Factory address of NFT20 factory proxy contract
   * @return  True on success
   */
  function initializeNFT20Factory(
    NFT20Factory _NFT20Factory
  ) external nonReentrant onlyRole(INITIALIZER_ROLE) returns (bool) {
    // May only initialize once
    if (address(niftyFactory) != address(0)) revert Unauthorized();
    niftyFactory = _NFT20Factory;
    emit NFTFactoryInitialized(address(_NFT20Factory));
    return true;
  }

  /**
   * @notice  Setup an NFT20 pair if one exists for the collateral asset underlying the given HToken
   * @dev     Does not create an NFT20 pool if one does not exist.
   * @param   _hToken address of the target hToken.
   * @return  True on success
   */
  function initializeNFT20Pair(HTokenI _hToken) external nonReentrant onlyRole(INITIALIZER_ROLE) returns (bool) {
    IERC721 collateralToken = _hToken.collateralToken();

    // Check for existence of NFT20 pool
    address pair = niftyFactory.nftToToken(address(collateralToken));

    // If NFT20 pair exists, add it to mapping
    if (pair != address(0)) {
      // Sanity check
      if (NFT20Pair(pair).nftAddress() == address(collateralToken)) {
        poolToNiftyPair[_hToken] = pair;
        emit NFT20PairInitialized(pair, address(_hToken));
        return true;
      }
    }

    return false;
  }

  /**
   * @dev     Called before NFT20 liquidation. Should handle auctions, refunding, and any other logic.
   * @param   _hToken           Contract address of the hToken
   * @param   _collateralId     NFT tokenId
   * @return  (Collateral ERC-721 address, vault address)
   */
  function _NFT20PreLiquidationHook(
    HTokenI _hToken,
    uint256 _collateralId
  ) internal virtual returns (IERC721, address) {}

  /**
   * @dev     Called before NFTX droplet swap. Should handle auctions, refunding, and any other logic.
   * @param   _hToken           Contract address of the hToken
   * @param   _collateralId     NFT tokenId
   * @return  (Collateral ERC-721 address, underlying ERC-20 address)
   */
  function _NFT20PreSwapHook(HTokenI _hToken, uint256 _collateralId) internal view virtual returns (IERC721, IERC20) {}

  /**
   * @dev     Called after DEX swap to ensure we have enough to repay without dipping into protected funds
   * @param   _hToken           hToken we're repaying against
   * @param   _underlyingToken  ERC-20 token to be used for repayment
   * @param   _collateralId      NFT token ID which we are repaying against
   */
  function postSwapHook(HTokenI _hToken, IERC20 _underlyingToken, uint256 _collateralId) internal virtual {}

  /**
   * @notice  Sends a given NFT to the NFT20 platform in exchange for droplet tokens
   * @dev     This will leave the protocol underwater in ERC20 terms unless the droplets are liquidated.
   *          Business logic needs to understand and account for this.
   * @param   _hToken           Contract address of the hToken
   * @param   _collateralId     NFT Token ID
   */
  function liquidateViaNFT20(
    HTokenI _hToken,
    uint256 _collateralId
  ) external nonReentrant onlyRole(LIQUIDATOR_ROLE) returns (bool) {
    // TODO add incentivisation on executing this
    (IERC721 collateralToken, address pair) = _NFT20PreLiquidationHook(_hToken, _collateralId);

    // Straight token transfer, IERC721Receiver hook in NFT20 pair will mint pair tokens.
    collateralToken.safeTransferFrom(address(this), pair, _collateralId);

    emit NFT20LiquidationExecuted(pair, address(_hToken), _collateralId);

    return true;
  }

  /**
   * @notice  Swap droplets for ERC20 using Uniswap V2 pools, and use the funds to repay a borrow
   * @param   _hToken             The hToken contract address
   * @param   _borrower           The address of the current owner of the deposit coupon
   * @param   _collateralId       Token ID of the collateral token
   * @param   _path               Array of tokens to swap through, starting at droplet and ending at HToken's underlying
   * @param   _amountIn           Quantity of droplets to liquidate
   * @param   _amountOutMinimum   Minimum acceptable output quantity of output token
   * @return  True upon success
   */
  function swapNFT20DropletsAndRepayBorrow(
    HTokenI _hToken,
    address _borrower,
    uint256 _collateralId,
    address[] memory _path,
    uint256 _amountIn,
    uint256 _amountOutMinimum
  ) external nonReentrant onlyRole(SWAPPER_ROLE) returns (bool) {
    (, IERC20 underlying) = _NFT20PreSwapHook(_hToken, _collateralId);

    // Retrieve address
    address droplet = poolToNiftyPair[_hToken];

    // Arbitrary swap path is allowed, but it must start and end in the right place
    if (_path[0] != droplet) revert WrongParams();
    if (_path[_path.length - 1] != address(underlying)) revert WrongParams();

    // Set approval
    address cachedSwapRouter = address(swapRouter);
    uint256 currentAllowance = IERC20(droplet).allowance(address(this), cachedSwapRouter);
    if (currentAllowance != _amountIn) {
      // Decrease to 0 first for tokens mitigating the race condition
      IERC20(droplet).safeDecreaseAllowance(cachedSwapRouter, currentAllowance);
      IERC20(droplet).safeIncreaseAllowance(cachedSwapRouter, _amountIn);
    }

    // Dex swap
    swapExactUniV2(_amountIn, _amountOutMinimum, _path);

    postSwapHook(_hToken, underlying, _collateralId);

    // Closeout and repay funds
    _hToken.closeoutLiquidation(_borrower, _collateralId);

    marketplace.toggleLiquidation(_hToken, _collateralId, false);

    emit DropletsSwapped(droplet, _path, _amountIn, _amountOutMinimum);
    emit BorrowRepaid(address(_hToken), _borrower, _collateralId);

    return true;
  }

  function swapExactUniV2(
    uint256 _amountIn,
    uint256 _amountOutMin,
    address[] memory _path
  ) internal virtual returns (uint256[] memory) {
    uint256[] memory amounts = swapRouter.swapExactTokensForTokens(
      _amountIn,
      _amountOutMin,
      _path,
      address(this),
      block.timestamp
    );
    return amounts;
  }
}

File 32 of 34 : LiquidatorModuleNFTX.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.15;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

import ".././interfaces/HTokenI.sol";
import "./LiquidatorStorage.sol";
import ".././utils/ErrorReporter.sol";

import { INFTXVault } from ".././interfaces/INFTXVault.sol";
import { INFTXVaultFactory } from ".././interfaces/INFTXVaultFactory.sol";

/**
 * @title   Honey Finance Liquidator Module - NFTX
 * @notice  Execute liquidations via NFTX for HToken IRM contracts
 * @author  Honey Labs inc
 * @custom:coauthor     BowTiedPickle
 * @custom:contributor  m4rio
 */
abstract contract LiquidatorModuleNFTX is AccessControl, ReentrancyGuard, LiquidatorStorage {
  using SafeERC20 for IERC20;

  /// @notice NFTX pool factory
  INFTXVaultFactory public NFTXFactory;

  /// @notice HToken to NFTX vaults for that collateral
  mapping(HTokenI => address[]) public poolToNFTXVaults;

  event NFTXFactoryInitialized(address _factory);
  event NFTXVaultsInitialized(address _pair, address _hToken);
  event NFTXLiquidationExecuted(address indexed _pair, address indexed _hToken, uint256 _collateralId);

  /**
   * @notice  Initializes the NFTXFactory that will be queried
   * @dev     May only be called once
   * @param   _NFTXFactory address of NFTX factory proxy contract
   * @return  True on success
   */
  function initializeNFTXFactory(
    INFTXVaultFactory _NFTXFactory
  ) external nonReentrant onlyRole(INITIALIZER_ROLE) returns (bool) {
    // May only initialize once
    if (address(NFTXFactory) != address(0)) revert Unauthorized();
    NFTXFactory = _NFTXFactory;
    emit NFTXFactoryInitialized(address(_NFTXFactory));
    return true;
  }

  /**
   * @notice  Setup NFTX vault(s) if they exists for the collateral asset underlying the given HToken
   * @dev     Does not create an NFTX vault if one does not exist.
   * @param   _hToken address of the target hToken.
   * @return  True on success
   */
  function initializeNFTXVault(HTokenI _hToken) external nonReentrant onlyRole(INITIALIZER_ROLE) returns (bool) {
    IERC721 collateralToken = _hToken.collateralToken();

    // Check for existence of NFTX pool
    address[] memory vaults = NFTXFactory.vaultsForAsset(address(collateralToken));

    uint256 length = vaults.length;

    if (length > 0) {
      address vault;
      for (uint256 i; i < length; ) {
        vault = vaults[i];

        // Sanity check
        if (INFTXVault(vault).assetAddress() == address(collateralToken)) {
          poolToNFTXVaults[_hToken].push(vault);

          emit NFTXVaultsInitialized(vault, address(_hToken));
        }

        unchecked {
          ++i;
        }
      }

      return true;
    } else {
      return false;
    }
  }

  /**
   * @dev     Called before NFTX liquidation. Should handle auctions, refunding, and any other logic.
   * @param   _hToken Contract address of the HToken
   * @param   _collateralId NFT tokenId
   * @param   _vaultIndex 0 if only one vault exists, otherwise index of the desired vault in the address[] mapping of poolToNFTXVaults
   * @return  (Collateral ERC-721 address, vault address)
   */
  function _NFTXPreLiquidationHook(
    HTokenI _hToken,
    uint256 _collateralId,
    uint256 _vaultIndex
  ) internal virtual returns (IERC721, address) {}

  /**
   * @dev     Called before NFTX droplet swap. Should handle auctions, refunding, and any other logic.
   * @param   _hToken Contract address of the HToken
   * @param   _collateralId NFT tokenId
   * @return  (Collateral ERC-721 address, underlying ERC-20 address)
   */
  function _NFTXPreSwapHook(HTokenI _hToken, uint256 _collateralId) internal virtual returns (IERC721, IERC20) {}

  /**
   * @dev     Called after dex swap to ensure we have enough to repay without dipping into protected funds
   * @param   _hToken hToken we're repaying against
   * @param   _underlyingToken ERC-20 token to be used for repayment
   * @param   _collateralId NFT tokenId which we are repaying against
   */
  function postSwapHook(HTokenI _hToken, IERC20 _underlyingToken, uint256 _collateralId) internal virtual {}

  /**
   * @notice  Sends a given NFT to the NFTX platform in exchange for droplet tokens
   * @dev     This will leave the protocol underwater in ERC20 terms unless the droplets are liquidated.
              Business logic needs to understand and account for this.
   * @dev     May only be called after clawback window expires
   * @param   _hToken           Contract address of the HToken
   * @param   _collateralId     NFT Token Id
   * @param   _vaultIndex       Index of the NFTX vault in poolToNFTXVaults. 0 if only one NFTX vault exists for this collateral.
   */
  function liquidateViaNFTX(
    HTokenI _hToken,
    uint256 _collateralId,
    uint256 _vaultIndex
  ) external nonReentrant onlyRole(LIQUIDATOR_ROLE) returns (bool) {
    (IERC721 collateralToken, address pair) = _NFTXPreLiquidationHook(_hToken, _collateralId, _vaultIndex);

    uint256[] memory assets = new uint256[](1);
    uint256[] memory amounts = new uint256[](1);
    assets[0] = _collateralId;
    amounts[0] = 1;

    collateralToken.approve(pair, _collateralId);

    INFTXVault(pair).mint(assets, amounts);

    emit NFTXLiquidationExecuted(pair, address(_hToken), _collateralId);

    return true;
  }

  /**
   * @notice  Swap droplets for ERC20 using Uniswap V2 pools, and use the funds to repay a borrow
   * @param   _hToken hToken contract address
   * @param   _vaultIndex Which of the NFTX vaults for that collateral should be used
   * @param   _borrower address of the current owner of the deposit coupon
   * @param   _collateralId collateral Id, aka the NFT token ID
   * @param   _path tokens to swap through, starting at droplet and ending at HToken's underlying
   * @param   _amountIn quantity of droplets to liquidate
   * @param   _amountOutMinimum minimum acceptable output quantity of output token
   * @return  True upon success
   */
  function swapNFTXDropletsAndRepayBorrow(
    HTokenI _hToken,
    uint256 _vaultIndex,
    address _borrower,
    uint256 _collateralId,
    address[] memory _path,
    uint256 _amountIn,
    uint256 _amountOutMinimum
  ) external nonReentrant onlyRole(SWAPPER_ROLE) returns (bool) {
    (, IERC20 underlying) = _NFTXPreSwapHook(_hToken, _collateralId);

    // Retrieve address
    address droplet = poolToNFTXVaults[_hToken][_vaultIndex];

    // Arbitrary swap path is allowed, but it must start and end in the right place
    if (_path[0] != droplet) revert WrongParams();
    if (_path[_path.length - 1] != address(underlying)) revert WrongParams();

    // Set approval
    address cachedSwapRouter = address(swapRouter);
    uint256 currentAllowance = IERC20(droplet).allowance(address(this), cachedSwapRouter);
    if (currentAllowance != _amountIn) {
      // Decrease to 0 first for tokens mitigating the race condition
      IERC20(droplet).safeDecreaseAllowance(cachedSwapRouter, currentAllowance);
      IERC20(droplet).safeIncreaseAllowance(cachedSwapRouter, _amountIn);
    }

    // Dex swap
    swapExactUniV2(_amountIn, _amountOutMinimum, _path);

    postSwapHook(_hToken, underlying, _collateralId);

    // Closeout and repay funds
    _hToken.closeoutLiquidation(_borrower, _collateralId);

    marketplace.toggleLiquidation(_hToken, _collateralId, false);

    emit DropletsSwapped(droplet, _path, _amountIn, _amountOutMinimum);
    emit BorrowRepaid(address(_hToken), _borrower, _collateralId);

    return true;
  }

  function swapExactUniV2(
    uint256 _amountIn,
    uint256 _amountOutMin,
    address[] memory _path
  ) internal virtual returns (uint256[] memory) {
    uint256[] memory amounts = swapRouter.swapExactTokensForTokens(
      _amountIn,
      _amountOutMin,
      _path,
      address(this),
      block.timestamp
    );
    return amounts;
  }
}

File 33 of 34 : LiquidatorStorage.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity 0.8.15;

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

import ".././interfaces/MarketplaceI.sol";

import ".././utils/ErrorReporter.sol";

/**
 * @title   Liquidator Storage
 * @author  Honey Labs inc
 * @custom:coauthor BowTiedPickle
 * @custom:coauthor m4rio
 */
contract LiquidatorStorage {
  event DropletsSwapped(address indexed _droplet, address[] _path, uint256 _amountIn, uint256 _amountOut);

  event BorrowRepaid(address indexed _hToken, address indexed _borrower, uint256 _collateralId);

  /// @notice Uniswap router used for swapping droplets
  IUniswapV2Router02 public swapRouter;

  /// @notice The Marketplace contract used for selling the NFTs
  MarketplaceI public marketplace;

  // ----- Roles -----
  bytes32 public constant SWAPPER_ROLE = keccak256("SWAPPER_ROLE");
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant INITIALIZER_ROLE = keccak256("INITIALIZER_ROLE");
  bytes32 public constant LIQUIDATOR_ROLE = keccak256("LIQUIDATOR_ROLE");

  // ----- Constants -----
  uint8 internal constant COUPON_UNINITIALIZED = 0;
  uint8 internal constant COUPON_INACTIVE = 1;
  uint8 internal constant COUPON_ACTIVE = 2;
  uint8 internal constant COUPON_LIQUIDATED = 3;

  constructor() {}
}

File 34 of 34 : ErrorReporter.sol
//SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.15;

error Unauthorized();
error AccrueInterestError(Error error);
error WrongParams();
error Unexpected(string error);
error InvalidCoupon();
error ControllerError(Error error);
error AdminError(Error error);
error MarketError(Error error);
error HTokenError(Error error);
error LiquidatorError(Error error);
error ControlPanelError(Error error);
error HTokenFactoryError(Error error);
error PausedAction();
error NotOwner();
error ExternalFailure(string error);
error Initialized();
error Uninitialized();
error OracleNotUpdated();
error TransferError();
error StalePrice();

/**
 * @title   Errors reported across Honey Labs Inc. contracts
 * @author  Honey Labs Inc.
 * @custom:coauthor BowTiedPickle
 * @custom:coauthor m4rio
 */
enum Error {
  UNAUTHORIZED, //0
  INSUFFICIENT_LIQUIDITY,
  INVALID_COLLATERAL_FACTOR,
  MAX_MARKETS_IN,
  MARKET_NOT_LISTED,
  MARKET_ALREADY_LISTED, //5
  MARKET_CAP_BORROW_REACHED,
  MARKET_NOT_FRESH,
  PRICE_ERROR,
  BAD_INPUT,
  AMOUNT_ZERO, //10
  NO_DEBT,
  LIQUIDATION_NOT_ALLOWED,
  WITHDRAW_NOT_ALLOWED,
  INITIAL_EXCHANGE_MANTISSA,
  TRANSFER_ERROR, //15
  COUPON_LOOKUP,
  TOKEN_INSUFFICIENT_CASH,
  BORROW_RATE_TOO_BIG,
  NONZERO_BORROW_BALANCE,
  AMOUNT_TOO_BIG, //20
  AUCTION_NOT_ACTIVE,
  AUCTION_FINISHED,
  AUCTION_NOT_FINISHED,
  AUCTION_BID_TOO_LOW,
  AUCTION_NO_BIDS, //25
  CLAWBACK_WINDOW_EXPIRED,
  CLAWBACK_WINDOW_NOT_EXPIRED,
  REFUND_NOT_OWED,
  TOKEN_LOOKUP_ERROR,
  INSUFFICIENT_WINNING_BID, //30
  TOKEN_DEBT_NONEXISTENT,
  AUCTION_SETTLE_FORBIDDEN,
  NFT20_PAIR_NOT_FOUND,
  NFTX_PAIR_NOT_FOUND,
  TOKEN_NOT_PRESENT, //35
  CANCEL_TOO_SOON,
  AUCTION_USER_NOT_FOUND,
  NOT_FOUND,
  INVALID_MAX_LTV_FACTOR,
  BALANCE_INSUFFICIENT, //40
  ORACLE_NOT_SET,
  MARKET_INVALID,
  FACTORY_INVALID_COLLATERAL,
  FACTORY_INVALID_UNDERLYING,
  FACTORY_INVALID_ORACLE, //45
  FACTORY_DEPLOYMENT_FAILED,
  REPAY_NOT_ALLOWED,
  NONZERO_UNDERLYING_BALANCE,
  INVALID_ACTION,
  ORACLE_IS_PRESENT, //50
  FACTORY_INVALID_UNDERLYING_DECIMALS,
  FACTORY_INVALID_INTEREST_RATE_MODEL
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_treasury","type":"address"},{"internalType":"contract ControllerI","name":"_controller","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Initialized","type":"error"},{"inputs":[],"name":"InvalidCoupon","type":"error"},{"inputs":[{"internalType":"enum Error","name":"error","type":"uint8"}],"name":"LiquidatorError","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"string","name":"error","type":"string"}],"name":"Unexpected","type":"error"},{"inputs":[],"name":"Uninitialized","type":"error"},{"inputs":[],"name":"WrongParams","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_hToken","type":"address"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"BorrowRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ControllerI","name":"_oldController","type":"address"},{"indexed":false,"internalType":"address","name":"_newController","type":"address"}],"name":"ControllerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_droplet","type":"address"},{"indexed":false,"internalType":"address[]","name":"_path","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"_amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountOut","type":"uint256"}],"name":"DropletsSwapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_hToken","type":"address"},{"indexed":true,"internalType":"address","name":"_underlying","type":"address"},{"indexed":true,"internalType":"address","name":"_collateral","type":"address"}],"name":"HTokenInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_paused","type":"bool"}],"name":"LiquidatorPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract MarketplaceI","name":"_oldMarketplace","type":"address"},{"indexed":true,"internalType":"contract MarketplaceI","name":"_newMarketplace","type":"address"}],"name":"MarketplaceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_pair","type":"address"},{"indexed":true,"internalType":"address","name":"_hToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"NFT20LiquidationExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_pair","type":"address"},{"indexed":false,"internalType":"address","name":"_hToken","type":"address"}],"name":"NFT20PairInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_factory","type":"address"}],"name":"NFTFactoryInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_hToken","type":"address"},{"indexed":true,"internalType":"contract IERC721","name":"_collateralToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"NFTWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_factory","type":"address"}],"name":"NFTXFactoryInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_pair","type":"address"},{"indexed":true,"internalType":"address","name":"_hToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"NFTXLiquidationExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_pair","type":"address"},{"indexed":false,"internalType":"address","name":"_hToken","type":"address"}],"name":"NFTXVaultsInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_qty","type":"uint256"}],"name":"ProfitsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_qty","type":"uint256"}],"name":"TokenSwept","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_oldTreasury","type":"address"},{"indexed":false,"internalType":"address","name":"_newTreasury","type":"address"}],"name":"TreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IUniswapV2Router02","name":"_oldRouter","type":"address"},{"indexed":false,"internalType":"contract IUniswapV2Router02","name":"_newRouter","type":"address"}],"name":"UniswapV2RouterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIALIZER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFTXFactory","outputs":[{"internalType":"contract INFTXVaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWAPPER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"}],"name":"_initializeHToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"_manualCloseout","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_state","type":"bool"}],"name":"_pauseLiquidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newController","type":"address"}],"name":"_setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract MarketplaceI","name":"_marketplace","type":"address"}],"name":"_setMarketplace","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newTreasury","type":"address"}],"name":"_setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IUniswapV2Router02","name":"_uniswapV2Router","type":"address"}],"name":"_setUniswapV2Router","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"_sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"},{"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"_withdrawNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"_withdrawProfits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"contract ControllerI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract NFT20Factory","name":"_NFT20Factory","type":"address"}],"name":"initializeNFT20Factory","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"}],"name":"initializeNFT20Pair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract INFTXVaultFactory","name":"_NFTXFactory","type":"address"}],"name":"initializeNFTXFactory","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"}],"name":"initializeNFTXVault","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_hToken","type":"address"}],"name":"isRegisteredHToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"isRegisteredUnderlying","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"},{"internalType":"uint256","name":"_collateralId","type":"uint256"}],"name":"liquidateViaNFT20","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"},{"internalType":"uint256","name":"_collateralId","type":"uint256"},{"internalType":"uint256","name":"_vaultIndex","type":"uint256"}],"name":"liquidateViaNFTX","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"marketplace","outputs":[{"internalType":"contract MarketplaceI","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"niftyFactory","outputs":[{"internalType":"contract NFT20Factory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"","type":"address"}],"name":"poolToCollateralToken","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolToNFTXVaults","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"","type":"address"}],"name":"poolToNiftyPair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"","type":"address"}],"name":"poolToUnderlyingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collateralId","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMinimum","type":"uint256"}],"name":"swapNFT20DropletsAndRepayBorrow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract HTokenI","name":"_hToken","type":"address"},{"internalType":"uint256","name":"_vaultIndex","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_collateralId","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMinimum","type":"uint256"}],"name":"swapNFTXDropletsAndRepayBorrow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapRouter","outputs":[{"internalType":"contract IUniswapV2Router02","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b506040516200422b3803806200422b83398101604081905262000034916200023b565b600180556002805460ff191690556001600160a01b0382166200006a57604051635863f78960e01b815260040160405180910390fd5b6001600160a01b0381166200009257604051635863f78960e01b815260040160405180910390fd5b6200009f60003362000181565b620000cb7f30d41a597cac127d8249d31298b50e481ee82c3f4a49ff93c76a22735aa9f3ad3362000181565b620000f77f5e17fc5225d4a099df75359ce1f405503ca79498a8dc46a7d583235a0ee45c163362000181565b620001237f724f6a44d576143e18c60911798b2b15551ca96bd8f7cb7524b8fa36253a26d83362000181565b6200014f7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a3362000181565b600880546001600160a01b039384166001600160a01b031991821617909155600980549290931691161790556200027a565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166200021e576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620001dd3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6001600160a01b03811681146200023857600080fd5b50565b600080604083850312156200024f57600080fd5b82516200025c8162000222565b60208401519092506200026f8162000222565b809150509250929050565b613fa1806200028a6000396000f3fe608060405234801561001057600080fd5b50600436106102b45760003560e01c806383de424e11610171578063c31c9c07116100d3578063df668eca11610097578063e8756be511610071578063e8756be5146106a1578063f77c4791146106ca578063fecabba1146106dd57600080fd5b8063df668eca14610640578063e398d0e014610667578063e63ab1e91461067a57600080fd5b8063c31c9c07146105dc578063c5b71eb6146105f4578063d17005af14610607578063d547741f1461061a578063da5c4dbd1461062d57600080fd5b8063a217fddf11610135578063b014f5ec1161010f578063b014f5ec146105a3578063b705e0ac146105b6578063bb614aaa146105c957600080fd5b8063a217fddf14610575578063a7b213461461057d578063abc8c7af1461059057600080fd5b806383de424e146104f257806391050b9814610505578063918bf7851461051857806391d148541461052b578063923b4d051461056257600080fd5b80632f2ff15d1161021a5780635c975abb116101de5780636cce3a5c116101b85780636cce3a5c146104b95780637c7f477b146104cc57806382f6201d146104df57600080fd5b80635c975abb1461048857806361d027b3146104935780636a06fd48146104a657600080fd5b80632f2ff15d1461043257806336568abe146104455780633db9021514610458578063522550dc1461046b57806354fd4d501461047e57600080fd5b8063207c96671161027c5780632900a13c116102565780632900a13c146103f75780632a08922c1461040a5780632afc512e1461041f57600080fd5b8063207c966714610396578063248a9ca3146103bf57806327df3d3e146103e257600080fd5b806301ffc9a7146102b95780630ce2e587146102e1578063150b7a021461030c57806316d8887a14610338578063201b0bc51461036d575b600080fd5b6102cc6102c7366004613693565b6106f0565b60405190151581526020015b60405180910390f35b6004546102f4906001600160a01b031681565b6040516001600160a01b0390911681526020016102d8565b61031f61031a3660046136d2565b610727565b6040516001600160e01b031990911681526020016102d8565b61035f7f5e17fc5225d4a099df75359ce1f405503ca79498a8dc46a7d583235a0ee45c1681565b6040519081526020016102d8565b6102f461037b366004613771565b6005602052600090815260409020546001600160a01b031681565b6102f46103a4366004613771565b600b602052600090815260409020546001600160a01b031681565b61035f6103cd36600461378e565b60009081526020819052604090206001015490565b61035f600080516020613f4c83398151915281565b6102cc6104053660046137a7565b61092c565b61041d610418366004613771565b610b34565b005b61041d61042d366004613771565b610bd0565b61041d6104403660046137dc565b610ce8565b61041d6104533660046137dc565b610d12565b6102cc610466366004613914565b610d95565b61041d610479366004613992565b6110e2565b61035f620f424081565b60025460ff166102cc565b6008546102f4906001600160a01b031681565b6102cc6104b4366004613771565b61123b565b6102cc6104c7366004613771565b611248565b6006546102f4906001600160a01b031681565b61041d6104ed366004613771565b611499565b61041d610500366004613771565b611528565b6102f4610513366004613992565b6115c4565b6102cc610526366004613771565b6115fc565b6102cc6105393660046137dc565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6102cc610570366004613771565b611809565b61035f600081565b6102cc61058b3660046139be565b6118b2565b6003546102f4906001600160a01b031681565b61041d6105b1366004613771565b611c37565b61041d6105c4366004613a47565b611d3a565b6102cc6105d7366004613771565b611f3c565b6002546102f49061010090046001600160a01b031681565b61041d610602366004613771565b611fd5565b6102cc610615366004613992565b612062565b61041d6106283660046137dc565b61216f565b61041d61063b366004613a96565b612194565b61035f7f724f6a44d576143e18c60911798b2b15551ca96bd8f7cb7524b8fa36253a26d881565b61041d610675366004613771565b612212565b61035f7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6102f46106af366004613771565b600a602052600090815260409020546001600160a01b031681565b6009546102f4906001600160a01b031681565b6102cc6106eb366004613771565b612511565b60006001600160e01b03198216637965db0b60e01b148061072157506301ffc9a760e01b6001600160e01b03198316145b92915050565b600061073161251e565b811561091f57600061074583850185613771565b6001600160a01b038082166000908152600b602052604090205491925016806107815760405163071cbeb560e21b815260040160405180910390fd5b336001600160a01b038216146107a9576040516282b42960e81b815260040160405180910390fd5b6040516331a9108f60e11b81526004810187905230906001600160a01b03831690636352211e90602401602060405180830381865afa1580156107f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108149190613abe565b6001600160a01b03161461083a576040516282b42960e81b815260040160405180910390fd5b600354604051631590aeb960e31b81526001600160a01b03848116600483015260248201899052600160448301529091169063ac8575c890606401600060405180830381600087803b15801561088f57600080fd5b505af11580156108a3573d6000803e3d6000fd5b505060035460405163095ea7b360e01b81526001600160a01b039182166004820152602481018a9052908416925063095ea7b39150604401600060405180830381600087803b1580156108f557600080fd5b505af1158015610909573d6000803e3d6000fd5b50630a85bd0160e11b9550610923945050505050565b5060005b95945050505050565b6000610936612566565b7f5e17fc5225d4a099df75359ce1f405503ca79498a8dc46a7d583235a0ee45c16610960816125bf565b60008061096e8787876125c9565b6040805160018082528183019092529294509092506000919060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833701905050905087826000815181106109ce576109ce613adb565b6020026020010181815250506001816000815181106109ef576109ef613adb565b602090810291909101015260405163095ea7b360e01b81526001600160a01b038481166004830152602482018a905285169063095ea7b390604401600060405180830381600087803b158015610a4457600080fd5b505af1158015610a58573d6000803e3d6000fd5b5050604051630f57464360e21b81526001600160a01b0386169250633d5d190c9150610a8a9085908590600401613b2c565b6020604051808303816000875af1158015610aa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610acd9190613b51565b50886001600160a01b0316836001600160a01b03167ffc123b9d521335a51e3b7fc63e8fb674f54b370ad7c5223b7818dddde8b969178a604051610b1391815260200190565b60405180910390a3600195505050505050610b2d60018055565b9392505050565b6000610b3f816125bf565b6001600160a01b038216610b6657604051635863f78960e01b815260040160405180910390fd5b600854604080516001600160a01b03928316815291841660208301527f4ab5be82436d353e61ca18726e984e561f5c1cc7c6d38b29d2553c790434705a910160405180910390a150600880546001600160a01b0319166001600160a01b0392909216919091179055565b610bd8612566565b6000610be3816125bf565b610bee600c8361284f565b610c0a576040516282b42960e81b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015610c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c759190613b51565b90508015610c9757600854610c97906001600160a01b03858116911683612871565b826001600160a01b03167f124fd12bda4dcb813885ca782fb4ea9350d4f752b1a881cadf8742d8f128bb8e82604051610cd291815260200190565b60405180910390a25050610ce560018055565b50565b600082815260208190526040902060010154610d03816125bf565b610d0d83836128d4565b505050565b6001600160a01b0381163314610d875760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610d918282612972565b5050565b6000610d9f612566565b7f724f6a44d576143e18c60911798b2b15551ca96bd8f7cb7524b8fa36253a26d8610dc9816125bf565b6000610dd589886129f1565b6001600160a01b03808c166000908152600560205260408120548a5193955090911692508291899190610e0a57610e0a613adb565b60200260200101516001600160a01b031614610e3957604051635863f78960e01b815260040160405180910390fd5b816001600160a01b03168760018951610e529190613b80565b81518110610e6257610e62613adb565b60200260200101516001600160a01b031614610e9157604051635863f78960e01b815260040160405180910390fd5b600254604051636eb1769f60e11b81523060048201526101009091046001600160a01b03908116602483018190529160009184169063dd62ed3e90604401602060405180830381865afa158015610eec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f109190613b51565b9050878114610f4157610f2d6001600160a01b0384168383612b10565b610f416001600160a01b038416838a612c23565b610f4c88888b612cdb565b50604051633208a6db60e21b81526001600160a01b038c81166004830152602482018c90528d169063c8229b6c90604401600060405180830381600087803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b5050600354604051631590aeb960e31b81526001600160a01b03909116925063ac8575c89150611002908f908e906000906004016001600160a01b0393909316835260208301919091521515604082015260600190565b600060405180830381600087803b15801561101c57600080fd5b505af1158015611030573d6000803e3d6000fd5b50505050826001600160a01b03167f8339bcaa4cc3ca092b10ce5d9ce7116b7095b7a877aada50822b321a0f861dfd8a8a8a60405161107193929190613bd0565b60405180910390a28a6001600160a01b03168c6001600160a01b03167f17c3ae1f3b8f172f8151ed702c7f85988479e1bc2a3a831c142ce751aea4a9e38c6040516110be91815260200190565b60405180910390a36001955050505050506110d860018055565b9695505050505050565b6110ea612566565b60006110f5816125bf565b6001600160a01b038381166000818152600b6020526040808220546003549151631590aeb960e31b8152600481019490945260248401879052604484019290925290831692169063ac8575c890606401600060405180830381600087803b15801561115f57600080fd5b505af1158015611173573d6000803e3d6000fd5b5050600854604051632142170760e11b81523060048201526001600160a01b0391821660248201526044810187905290841692506342842e0e9150606401600060405180830381600087803b1580156111cb57600080fd5b505af11580156111df573d6000803e3d6000fd5b50505050806001600160a01b0316846001600160a01b03167fbbde41973f9ce4890f7ad9762c23d8191f261fd643bdf13ed8bbc10549b49fcb8560405161122891815260200190565b60405180910390a35050610d9160018055565b6000610721600c8361284f565b6000611252612566565b600080516020613f4c83398151915261126a816125bf565b6000836001600160a01b031663b2016bd46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ce9190613abe565b60065460405163121e869b60e31b81526001600160a01b038084166004830152929350600092909116906390f434d890602401600060405180830381865afa15801561131e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113469190810190613bf5565b80519091508015611482576000805b828110156114745783818151811061136f5761136f613adb565b60200260200101519150846001600160a01b0316826001600160a01b0316631ba46cfd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113e59190613abe565b6001600160a01b03160361146c576001600160a01b03888116600081815260076020908152604080832080546001810182559084529282902090920180546001600160a01b031916948716948517905581519384528301919091527f838f99fd11911b251a4c0eba4c8419d6d1e2a68f211a4850cc3aaae3bdbd4dc7910160405180910390a15b600101611355565b50600195505050505061148a565b600094505050505b5061149460018055565b919050565b60006114a4816125bf565b6001600160a01b0382166114cb57604051635863f78960e01b815260040160405180910390fd5b6003546040516001600160a01b038085169216907fc3e7368ef0a668941a4115b651a9c4f3fa3a13cc6ea105bb91e12bcaa932293c90600090a350600380546001600160a01b0319166001600160a01b0392909216919091179055565b6000611533816125bf565b6001600160a01b03821661155a57604051635863f78960e01b815260040160405180910390fd5b600954604080516001600160a01b03928316815291841660208301527f1c87e2bbc4e5fa5d7f6f8c44d66cb241dff224b8602eb5435ca2076d2a5c6fc2910160405180910390a150600980546001600160a01b0319166001600160a01b0392909216919091179055565b600760205281600052604060002081815481106115e057600080fd5b6000918252602090912001546001600160a01b03169150829050565b6000611606612566565b600080516020613f4c83398151915261161e816125bf565b6000836001600160a01b031663b2016bd46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561165e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116829190613abe565b60048054604051633079e40760e21b81526001600160a01b038085169382019390935292935060009291169063c1e7901c906024016020604051808303816000875af11580156116d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fa9190613abe565b90506001600160a01b038116156117f957816001600160a01b0316816001600160a01b0316635bf8633a6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611755573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117799190613abe565b6001600160a01b0316036117f9576001600160a01b0385811660008181526005602090815260409182902080546001600160a01b031916948616948517905581519384528301919091527f2cd07978056e37a037494f3289be78d899d4416986ab5f09ac1e95c5a452095e910160405180910390a160019350505061148a565b6000935050505061149460018055565b6000611813612566565b600080516020613f4c83398151915261182b816125bf565b6004546001600160a01b031615611854576040516282b42960e81b815260040160405180910390fd5b600480546001600160a01b0319166001600160a01b0385169081179091556040519081527f1e7a8116a17a4568e6fa092bb819488be0fd6749291e1a6bb41ed9b8d3359f9c906020015b60405180910390a150506001808055919050565b60006118bc612566565b7f724f6a44d576143e18c60911798b2b15551ca96bd8f7cb7524b8fa36253a26d86118e6816125bf565b60006118f28a886129f1565b6001600160a01b038c1660009081526007602052604081208054929450909250908b90811061192357611923613adb565b600091825260208220015488516001600160a01b0390911692508291899161194d5761194d613adb565b60200260200101516001600160a01b03161461197c57604051635863f78960e01b815260040160405180910390fd5b816001600160a01b031687600189516119959190613b80565b815181106119a5576119a5613adb565b60200260200101516001600160a01b0316146119d457604051635863f78960e01b815260040160405180910390fd5b600254604051636eb1769f60e11b81523060048201526101009091046001600160a01b03908116602483018190529160009184169063dd62ed3e90604401602060405180830381865afa158015611a2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a539190613b51565b9050878114611a8457611a706001600160a01b0384168383612b10565b611a846001600160a01b038416838a612c23565b611a8f88888b612cdb565b50604051633208a6db60e21b81526001600160a01b038c81166004830152602482018c90528e169063c8229b6c90604401600060405180830381600087803b158015611ada57600080fd5b505af1158015611aee573d6000803e3d6000fd5b50505050600360009054906101000a90046001600160a01b03166001600160a01b031663ac8575c88e8c60006040518463ffffffff1660e01b8152600401611b56939291906001600160a01b0393909316835260208301919091521515604082015260600190565b600060405180830381600087803b158015611b7057600080fd5b505af1158015611b84573d6000803e3d6000fd5b50505050826001600160a01b03167f8339bcaa4cc3ca092b10ce5d9ce7116b7095b7a877aada50822b321a0f861dfd8a8a8a604051611bc593929190613bd0565b60405180910390a28a6001600160a01b03168d6001600160a01b03167f17c3ae1f3b8f172f8151ed702c7f85988479e1bc2a3a831c142ce751aea4a9e38c604051611c1291815260200190565b60405180910390a3600195505050505050611c2c60018055565b979650505050505050565b611c3f612566565b6000611c4a816125bf565b611c55600c8361284f565b15611c72576040516282b42960e81b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611cb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cdd9190613b51565b90508015611cff57600854611cff906001600160a01b03858116911683612871565b826001600160a01b03167fe501bb37b6bef3426625fab8c3a24d8b13c03875431d7da0ac09609ce80d1f4c82604051610cd291815260200190565b611d42612566565b6000611d4d816125bf565b611d58600e8561284f565b611d755760405163071cbeb560e21b815260040160405180910390fd5b836001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611db057600080fd5b505af1158015611dc4573d6000803e3d6000fd5b50506040516305b71aed60e51b815260048101859052600092506001600160a01b038716915063b6e35da090602401602060405180830381865afa158015611e10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e349190613b51565b905080600003611e5a57601f60405163023ba7b360e51b8152600401610d7e9190613c84565b6001600160a01b038086166000908152600a602052604090205416611e80813384612cf0565b50604051633208a6db60e21b81526001600160a01b0386811660048301526024820186905287169063c8229b6c90604401600060405180830381600087803b158015611ecb57600080fd5b505af1158015611edf573d6000803e3d6000fd5b50505050846001600160a01b0316866001600160a01b03167f17c3ae1f3b8f172f8151ed702c7f85988479e1bc2a3a831c142ce751aea4a9e386604051611f2891815260200190565b60405180910390a3505050610d0d60018055565b6000611f46612566565b600080516020613f4c833981519152611f5e816125bf565b6006546001600160a01b031615611f87576040516282b42960e81b815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0385169081179091556040519081527ff57d3f8d5fc63f6cbc601c75bcad9afbcc2b159adaaef7e689ceabdc9e9645699060200161189e565b6000611fe0816125bf565b600254604080516001600160a01b036101009093048316815291841660208301527feb8d61f6daf24f1091115a70adc941fd8537cfa96f53bfaf282c6bb3cfc5e6e6910160405180910390a150600280546001600160a01b039092166101000274ffffffffffffffffffffffffffffffffffffffff0019909216919091179055565b600061206c612566565b7f5e17fc5225d4a099df75359ce1f405503ca79498a8dc46a7d583235a0ee45c16612096816125bf565b6000806120a38686612e3b565b604051632142170760e11b81523060048201526001600160a01b03808316602483015260448201899052929450909250908316906342842e0e90606401600060405180830381600087803b1580156120fa57600080fd5b505af115801561210e573d6000803e3d6000fd5b50505050856001600160a01b0316816001600160a01b03167f365714f8aed6e4f8aea8dcd4f1b535feafd8c041bd667530e383f6320c69743d8760405161215791815260200190565b60405180910390a36001935050505061072160018055565b60008281526020819052604090206001015461218a816125bf565b610d0d8383612972565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6121be816125bf565b81156121d1576121cc613083565b6121d9565b6121d96130dd565b60405182151581527f54de9eeec491883bde91ed33c16b6f76252d136f2b3a13fcd0b6f5fed37962839060200160405180910390a15050565b61221a61251e565b612222612566565b600080516020613f4c83398151915261223a816125bf565b600954604051630e25940360e31b81526001600160a01b0384811660048301529091169063712ca01890602401602060405180830381865afa158015612284573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a89190613cac565b6122c55760405163071cbeb560e21b815260040160405180910390fd5b6122d0600e8361284f565b156122ee576040516302ed543d60e51b815260040160405180910390fd5b6040516301ffc9a760e01b81526341277d7560e01b60048201526001600160a01b038316906301ffc9a790602401602060405180830381865afa158015612339573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061235d9190613cac565b61237a57604051635863f78960e01b815260040160405180910390fd5b6000826001600160a01b0316632495a5996040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123de9190613abe565b90506000836001600160a01b031663b2016bd46040518163ffffffff1660e01b8152600401602060405180830381865afa158015612420573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124449190613abe565b6001600160a01b038581166000908152600a6020908152604080832080546001600160a01b031990811689871617909155600b90925290912080549091169183169190911790559050612498600c83613116565b506124a4600e85613116565b506124bb6001600160a01b03831685600019612c23565b806001600160a01b0316826001600160a01b0316856001600160a01b03167f9ffb8812dac6f2e537547c3aca35cf12dc9e30f0ee737f0a2954d3acfdcb71dc60405160405180910390a4505050610ce560018055565b6000610721600e8361284f565b60025460ff16156125645760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610d7e565b565b6002600154036125b85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d7e565b6002600155565b610ce5813361312b565b6000806125d461251e565b6001600160a01b038086166000818152600a6020908152604080832054600b9092529091205490831692169061262057601d60405163023ba7b360e51b8152600401610d7e9190613c84565b6001600160a01b0382166126475760405163071cbeb560e21b815260040160405180910390fd5b6001600160a01b0387166000908152600760205260408120805490910361268457602260405163023ba7b360e51b8152600401610d7e9190613c84565b600081878154811061269857612698613adb565b6000918252602082200154604051634f38d23560e01b8152600481018b90526001600160a01b039182169350908b1690634f38d2359060240160c060405180830381865afa1580156126ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127129190613cc9565b602081015190915060ff1660031461273d5760405163c73e16c160e01b815260040160405180910390fd5b6040516331a9108f60e11b8152600481018a905230906001600160a01b03861690636352211e90602401602060405180830381865afa158015612784573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a89190613abe565b6001600160a01b0316146127d257602360405163023ba7b360e51b8152600401610d7e9190613c84565b600354604051631590aeb960e31b81526001600160a01b038c81166004830152602482018c9052600060448301529091169063ac8575c890606401600060405180830381600087803b15801561282757600080fd5b505af115801561283b573d6000803e3d6000fd5b50959c939b50929950505050505050505050565b6001600160a01b03811660009081526001830160205260408120541515610b2d565b6040516001600160a01b038316602482015260448101829052610d0d90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261319e565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610d91576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561292e3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615610d91576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6001600160a01b038083166000908152600a6020908152604080832054600b909252822054919283929181169116612a2a600e8761284f565b612a475760405163071cbeb560e21b815260040160405180910390fd5b6001600160a01b038216612a6e5760405163071cbeb560e21b815260040160405180910390fd5b604051634f38d23560e01b8152600481018690526000906001600160a01b03881690634f38d2359060240160c060405180830381865afa158015612ab6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ada9190613cc9565b602081015190915060ff16600314612b055760405163c73e16c160e01b815260040160405180910390fd5b509590945092505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015612b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b849190613b51565b905081811015612be85760405162461bcd60e51b815260206004820152602960248201527f5361666545524332303a2064656372656173656420616c6c6f77616e63652062604482015268656c6f77207a65726f60b81b6064820152608401610d7e565b6040516001600160a01b03841660248201528282036044820181905290612c1c90869063095ea7b360e01b9060640161289d565b5050505050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015612c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c989190613b51565b612ca29190613d4b565b6040516001600160a01b038516602482015260448101829052909150612cd590859063095ea7b360e01b9060640161289d565b50505050565b6060612ce8848484613270565b949350505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa158015612d39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d5d9190613b51565b9050612d746001600160a01b0386168530866132f9565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa158015612dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ddf9190613b51565b905081811015612e325760405163c83ad1cd60e01b815260206004820152601860248201527f5472616e7366657220696e76617269616e74206572726f7200000000000000006044820152606401610d7e565b03949350505050565b600080612e4661251e565b6001600160a01b038416612e7057601d60405163023ba7b360e51b8152600401610d7e9190613c84565b612e7b600e8561284f565b612e985760405163071cbeb560e21b815260040160405180910390fd5b6001600160a01b038085166000908152600b6020908152604080832054600590925290912054908216911680612ee457602160405163023ba7b360e51b8152600401610d7e9190613c84565b604051634f38d23560e01b8152600481018690526003906001600160a01b03881690634f38d2359060240160c060405180830381865afa158015612f2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f509190613cc9565b6020015160ff1614612f755760405163c73e16c160e01b815260040160405180910390fd5b6040516331a9108f60e11b81526004810186905230906001600160a01b03841690636352211e90602401602060405180830381865afa158015612fbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fe09190613abe565b6001600160a01b03161461300a57602360405163023ba7b360e51b8152600401610d7e9190613c84565b600354604051631590aeb960e31b81526001600160a01b03888116600483015260248201889052600060448301529091169063ac8575c890606401600060405180830381600087803b15801561305f57600080fd5b505af1158015613073573d6000803e3d6000fd5b5093989297509195505050505050565b61308b61251e565b6002805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130c03390565b6040516001600160a01b03909116815260200160405180910390a1565b6130e5613331565b6002805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa336130c0565b6000610b2d836001600160a01b038416613383565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610d915761315c816133d2565b6131678360206133e4565b604051602001613178929190613d8f565b60408051601f198184030181529082905262461bcd60e51b8252610d7e91600401613e04565b60006131f3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135809092919063ffffffff16565b805190915015610d0d57808060200190518101906132119190613cac565b610d0d5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610d7e565b6002546040516338ed173960e01b81526060916000916101009091046001600160a01b0316906338ed1739906132b29088908890889030904290600401613e37565b6000604051808303816000875af11580156132d1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109239190810190613e73565b6040516001600160a01b0380851660248301528316604482015260648101829052612cd59085906323b872dd60e01b9060840161289d565b60025460ff166125645760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610d7e565b60008181526001830160205260408120546133ca57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610721565b506000610721565b60606107216001600160a01b03831660145b606060006133f3836002613ef9565b6133fe906002613d4b565b67ffffffffffffffff8111156134165761341661380c565b6040519080825280601f01601f191660200182016040528015613440576020820181803683370190505b509050600360fc1b8160008151811061345b5761345b613adb565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061348a5761348a613adb565b60200101906001600160f81b031916908160001a90535060006134ae846002613ef9565b6134b9906001613d4b565b90505b6001811115613531576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106134ed576134ed613adb565b1a60f81b82828151811061350357613503613adb565b60200101906001600160f81b031916908160001a90535060049490941c9361352a81613f18565b90506134bc565b508315610b2d5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d7e565b6060612ce8848460008585600080866001600160a01b031685876040516135a79190613f2f565b60006040518083038185875af1925050503d80600081146135e4576040519150601f19603f3d011682016040523d82523d6000602084013e6135e9565b606091505b5091509150611c2c878383876060831561366457825160000361365d576001600160a01b0385163b61365d5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d7e565b5081612ce8565b612ce883838151156136795781518083602001fd5b8060405162461bcd60e51b8152600401610d7e9190613e04565b6000602082840312156136a557600080fd5b81356001600160e01b031981168114610b2d57600080fd5b6001600160a01b0381168114610ce557600080fd5b6000806000806000608086880312156136ea57600080fd5b85356136f5816136bd565b94506020860135613705816136bd565b935060408601359250606086013567ffffffffffffffff8082111561372957600080fd5b818801915088601f83011261373d57600080fd5b81358181111561374c57600080fd5b89602082850101111561375e57600080fd5b9699959850939650602001949392505050565b60006020828403121561378357600080fd5b8135610b2d816136bd565b6000602082840312156137a057600080fd5b5035919050565b6000806000606084860312156137bc57600080fd5b83356137c7816136bd565b95602085013595506040909401359392505050565b600080604083850312156137ef57600080fd5b823591506020830135613801816136bd565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff811182821017156138455761384561380c565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156138745761387461380c565b604052919050565b600067ffffffffffffffff8211156138965761389661380c565b5060051b60200190565b600082601f8301126138b157600080fd5b813560206138c66138c18361387c565b61384b565b82815260059290921b840181019181810190868411156138e557600080fd5b8286015b848110156139095780356138fc816136bd565b83529183019183016138e9565b509695505050505050565b60008060008060008060c0878903121561392d57600080fd5b8635613938816136bd565b95506020870135613948816136bd565b945060408701359350606087013567ffffffffffffffff81111561396b57600080fd5b61397789828a016138a0565b9350506080870135915060a087013590509295509295509295565b600080604083850312156139a557600080fd5b82356139b0816136bd565b946020939093013593505050565b600080600080600080600060e0888a0312156139d957600080fd5b87356139e4816136bd565b96506020880135955060408801356139fb816136bd565b945060608801359350608088013567ffffffffffffffff811115613a1e57600080fd5b613a2a8a828b016138a0565b93505060a0880135915060c0880135905092959891949750929550565b600080600060608486031215613a5c57600080fd5b8335613a67816136bd565b92506020840135613a77816136bd565b929592945050506040919091013590565b8015158114610ce557600080fd5b600060208284031215613aa857600080fd5b8135610b2d81613a88565b8051611494816136bd565b600060208284031215613ad057600080fd5b8151610b2d816136bd565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b83811015613b2157815187529582019590820190600101613b05565b509495945050505050565b604081526000613b3f6040830185613af1565b82810360208401526109238185613af1565b600060208284031215613b6357600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600082821015613b9257613b92613b6a565b500390565b600081518084526020808501945080840160005b83811015613b215781516001600160a01b031687529582019590820190600101613bab565b606081526000613be36060830186613b97565b60208301949094525060400152919050565b60006020808385031215613c0857600080fd5b825167ffffffffffffffff811115613c1f57600080fd5b8301601f81018513613c3057600080fd5b8051613c3e6138c18261387c565b81815260059190911b82018301908381019087831115613c5d57600080fd5b928401925b82841015611c2c578351613c75816136bd565b82529284019290840190613c62565b6020810160358310613ca657634e487b7160e01b600052602160045260246000fd5b91905290565b600060208284031215613cbe57600080fd5b8151610b2d81613a88565b600060c08284031215613cdb57600080fd5b613ce3613822565b825163ffffffff81168114613cf757600080fd5b8152602083015160ff81168114613d0d57600080fd5b6020820152613d1e60408401613ab3565b6040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b60008219821115613d5e57613d5e613b6a565b500190565b60005b83811015613d7e578181015183820152602001613d66565b83811115612cd55750506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351613dc7816017850160208801613d63565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613df8816028840160208801613d63565b01602801949350505050565b6020815260008251806020840152613e23816040850160208701613d63565b601f01601f19169190910160400192915050565b85815284602082015260a060408201526000613e5660a0830186613b97565b6001600160a01b0394909416606083015250608001529392505050565b60006020808385031215613e8657600080fd5b825167ffffffffffffffff811115613e9d57600080fd5b8301601f81018513613eae57600080fd5b8051613ebc6138c18261387c565b81815260059190911b82018301908381019087831115613edb57600080fd5b928401925b82841015611c2c57835182529284019290840190613ee0565b6000816000190483118215151615613f1357613f13613b6a565b500290565b600081613f2757613f27613b6a565b506000190190565b60008251613f41818460208701613d63565b919091019291505056fe30d41a597cac127d8249d31298b50e481ee82c3f4a49ff93c76a22735aa9f3ada264697066735822122090b722b00104075efe994eb6023713b7942f513a1855172535b23b3b792129d864736f6c634300080f003300000000000000000000000007f8cefd165b9e4a84b60ce47f4c3784c2eb408a0000000000000000000000009a1edb903b058298dd0b06f52876d9d45358b7cb

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

00000000000000000000000007f8cefd165b9e4a84b60ce47f4c3784c2eb408a0000000000000000000000009a1edb903b058298dd0b06f52876d9d45358b7cb

-----Decoded View---------------
Arg [0] : _treasury (address): 0x07f8cefd165b9e4a84b60ce47f4c3784c2eb408a
Arg [1] : _controller (address): 0x9a1edb903b058298dd0b06f52876d9d45358b7cb

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000007f8cefd165b9e4a84b60ce47f4c3784c2eb408a
Arg [1] : 0000000000000000000000009a1edb903b058298dd0b06f52876d9d45358b7cb


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.