// File: contracts/Earn.sol



// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";

import "./interfaces/ISignatureValidator.sol";
import "./libraries/UniswapConstants.sol";
import "./libraries/TransferHelper.sol";
import "./libraries/UniswapCollectHelper.sol";

import "./interfaces/external/ISwapRouter02.sol";
import "./interfaces/IFactory.sol";
import "./interfaces/IFindNFT.sol";

contract Earn is Ownable, ERC721Holder {
  using ECDSA for bytes32;

  address public immutable find;
  address public immutable factory;
  address public immutable findnft;

  address public signatureAddress;
  bool public disableSetSignatureAddressFlag;

  event SetSignatureAddress(address signatureAddress);
  event DisableSetSignatureAddress();
  event ClaimOSPOwnerNFT(address osp, address nftOwner);
  event CollectForBuilder(address token, uint256 cAmount, uint256 oAmount);
  event CollectFindUniswapLPFee(uint256 findAmount, uint256 wethAmount);
  event CollectOspUniswapLPFee(address osp, uint256 cAmount, uint256 oAmount);

  constructor(
    address _find,
    address _factory,
    address _findnft,
    address _signatureAddress
  ) {
    find = _find;
    factory = _factory;
    findnft = _findnft;
    signatureAddress = _signatureAddress;
  }

  function setSignatureAddress(address _signatureAddress) external onlyOwner {
    require(!disableSetSignatureAddressFlag, "DE");

    signatureAddress = _signatureAddress;
    emit SetSignatureAddress(_signatureAddress);
  }

  function disableSetSignatureAddress() external onlyOwner {
    disableSetSignatureAddressFlag = true;
    emit DisableSetSignatureAddress();
  }

  function claimOSPOwnerNFT(
    address osp,
    address nftOwner,
    bytes memory signature
  ) external {
    _verifyclaimOSPOwnerNFTSignature(osp, nftOwner, signature);

    (, , address pool, uint256 cnftTokenId, uint256 onftTokenId, ) = IFactory(
      factory
    ).token2OspInfo(osp);

    require(pool != address(0), "NE");

    require(IFindNFT(findnft).isClaimed(cnftTokenId) == false, "AC1");
    require(IFindNFT(findnft).isClaimed(onftTokenId) == false, "AC2");

    require(IFindNFT(findnft).ownerOf(onftTokenId) == address(this), "E");

    IFindNFT(findnft).safeTransferFrom(address(this), nftOwner, onftTokenId);

    IFindNFT(findnft).claim(cnftTokenId);
    IFindNFT(findnft).claim(onftTokenId);

    emit ClaimOSPOwnerNFT(osp, nftOwner);
  }

  function findNFTInfo()
    public
    view
    returns (
      address cnftOwner,
      address onftOnwer,
      uint256 cpercent,
      uint256 opercent
    )
  {
    (, , uint256 cnftTokenId, uint256 onftTokenId, ) = IFactory(factory)
      .findInfo();
    cnftOwner = IFindNFT(findnft).ownerOf(cnftTokenId);
    onftOnwer = IFindNFT(findnft).ownerOf(onftTokenId);
    (, , , , , cpercent, , , ) = IFindNFT(findnft).tokenId2Info(cnftTokenId);
    (, , , , , opercent, , , ) = IFindNFT(findnft).tokenId2Info(onftTokenId);
  }

  function ospNFTInfo(address osp)
    public
    view
    returns (
      address cnftOwner,
      address onftOnwer,
      uint256 cpercent,
      uint256 opercent,
      bool isClaim
    )
  {
    (, , address pool, uint256 cnftTokenId, uint256 onftTokenId, ) = IFactory(
      factory
    ).token2OspInfo(osp);

    require(pool != address(0), "NE");

    cnftOwner = IFindNFT(findnft).ownerOf(cnftTokenId);
    onftOnwer = IFindNFT(findnft).ownerOf(onftTokenId);
    (, , , , , cpercent, , , ) = IFindNFT(findnft).tokenId2Info(cnftTokenId);
    (, , , , , opercent, , , ) = IFindNFT(findnft).tokenId2Info(onftTokenId);
    isClaim = IFindNFT(findnft).isClaimed(onftTokenId);
  }

  function collectForBuilder(address token)
    external
    returns (uint256 cAmount, uint256 oAmount)
  {
    (address cnftOwner, address onftOnwer, uint256 cpercent, ) = findNFTInfo();
    uint256 allAmount = IERC20(token).balanceOf(address(this));
    if (allAmount > 0) {
      cAmount = (allAmount * cpercent) / 10000;
      oAmount = allAmount - cAmount;
      if (cAmount > 0) {
        TransferHelper.safeTransfer(token, cnftOwner, cAmount);
      }
      if (oAmount > 0) {
        TransferHelper.safeTransfer(token, onftOnwer, oAmount);
      }
    }

    emit CollectForBuilder(token, cAmount, oAmount);
  }

  function collectFindUniswapLPFee()
    external
    returns (uint256 findAmount, uint256 wethAmount)
  {
    uint256[] memory lpTokenIdList = IFactory(factory).findLpTokenIdList();
    UniswapCollectHelper.FeeResult memory result = UniswapCollectHelper
      .collectFeeWithLpList(lpTokenIdList, address(this));
    if (result.token0 == find) {
      findAmount = result.token0Add;
      wethAmount = result.token1Add;
    } else {
      findAmount = result.token1Add;
      wethAmount = result.token0Add;
    }

    emit CollectFindUniswapLPFee(findAmount, wethAmount);
  }

  function collectOspUniswapLPFee(address osp)
    external
    returns (uint256 cAmount, uint256 oAmount)
  {
    (address cnftOwner, address onftOnwer, uint256 cpercent, , ) = ospNFTInfo(
      osp
    );

    UniswapCollectHelper.FeeResult memory result;
    {
      uint256[] memory lpTokenIdList = IFactory(factory).ospLpTokenIdList(osp);
      result = UniswapCollectHelper.collectFeeWithLpList(
        lpTokenIdList,
        address(this)
      );
    }

    (uint256 poolConfigIndex, , , , , ) = IFactory(factory).token2OspInfo(osp);
    (uint24 fee, , , , ) = IFactory(factory).getOspPoolConfigs(poolConfigIndex);

    uint256 cToken0Add = (result.token0Add * cpercent) / 10000;
    uint256 oToken0Add = result.token0Add - cToken0Add;

    uint256 cToken1Add = (result.token1Add * cpercent) / 10000;
    uint256 oToken1Add = result.token1Add - cToken1Add;

    cAmount = UniswapCollectHelper.swapPoolFeeToTokenOut(
      UniswapCollectHelper.SwapPoolFeeToTokenOutParams({
        token0: result.token0,
        token1: result.token1,
        token0Amount: cToken0Add,
        token1Amount: cToken1Add,
        fee: fee,
        tokenOut: find,
        recipient: address(this)
      })
    );

    oAmount = UniswapCollectHelper.swapPoolFeeToTokenOut(
      UniswapCollectHelper.SwapPoolFeeToTokenOutParams({
        token0: result.token0,
        token1: result.token1,
        token0Amount: oToken0Add,
        token1Amount: oToken1Add,
        fee: fee,
        tokenOut: osp,
        recipient: address(this)
      })
    );

    if (cAmount > 0) {
      TransferHelper.safeTransfer(find, cnftOwner, cAmount);
    }

    if (onftOnwer != address(this)) {
      if (oAmount > 0) {
        TransferHelper.safeTransfer(osp, onftOnwer, oAmount);
      }
    }

    emit CollectOspUniswapLPFee(osp, cAmount, oAmount);
  }

  function _verifyclaimOSPOwnerNFTSignature(
    address ospToken,
    address nftOwner,
    bytes memory signature
  ) private view {
    bytes32 raw = keccak256(abi.encode(ospToken, nftOwner));

    if (Address.isContract(signatureAddress)) {
      require(signature.length == 0, "SLE");
      require(
        ISignatureValidator(signatureAddress).isValidHash(
          raw.toEthSignedMessageHash()
        ),
        "SE1"
      );
    } else {
      require(
        raw.toEthSignedMessageHash().recover(signature) == signatureAddress,
        "SE2"
      );
    }
  }
}

// File: @openzeppelin/contracts/utils/Address.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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 functionCall(target, data, "Address: low-level call failed");
    }

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

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

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

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

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

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

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

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

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

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

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

// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

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

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/cryptography/ECDSA.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

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

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

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

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

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

// File: @openzeppelin/contracts/token/ERC20/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: @openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address,
        address,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

// File: contracts/interfaces/ISignatureValidator.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface ISignatureValidator {
  function isValidSignature(bytes32 _hash, bytes memory _signature)
    external
    view
    returns (bytes4 magicValue);

  function isValidHash(bytes32 _hash) external view returns (bool isValid);
}

// File: contracts/libraries/UniswapConstants.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;

library UniswapConstants {
  address public constant UNISWAP_V3_POSITIONS =
    0xC36442b4a4522E871399CD717aBDD847Ab11FE88;
  address public constant UNISWAP_ROUTER =
    0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45;
}

// File: contracts/libraries/TransferHelper.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

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

library TransferHelper {
  function safeTransfer(
    address token,
    address to,
    uint256 value
  ) internal {
    (bool success, bytes memory data) = token.call(
      abi.encodeWithSelector(IERC20.transfer.selector, to, value)
    );
    require(success && (data.length == 0 || abi.decode(data, (bool))), "ST");
  }

  function safeTransferFrom(
    address token,
    address from,
    address to,
    uint256 value
  ) internal {
    (bool success, bytes memory data) = token.call(
      abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)
    );
    require(success && (data.length == 0 || abi.decode(data, (bool))), "STF");
  }

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

// File: contracts/libraries/UniswapCollectHelper.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

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

import "../interfaces/external/ISwapRouter02.sol";
import "../interfaces/external/INonfungiblePositionManager.sol";
import "./UniswapConstants.sol";
import "./SwapRouterHelper.sol";

library UniswapCollectHelper {
  struct FeeResult {
    address token0;
    address token1;
    uint256 token0Add;
    uint256 token1Add;
  }

  struct SwapPoolFeeToTokenOutParams {
    address token0;
    address token1;
    uint256 token0Amount;
    uint256 token1Amount;
    uint24 fee;
    address tokenOut;
    address recipient;
  }

  function collectFeeWithLpList(
    uint256[] memory lpTokenIdList,
    address recipient
  ) internal returns (FeeResult memory result) {
    result.token0Add = 0;
    result.token1Add = 0;

    for (uint256 index = 0; index < lpTokenIdList.length; index++) {
      FeeResult memory sResult = collectFeeWithLp(
        lpTokenIdList[index],
        recipient
      );
      if (index == 0) {
        result.token0 = sResult.token0;
        result.token1 = sResult.token1;
      } else {
        require(result.token0 == sResult.token0, "CFE");
        require(result.token1 == sResult.token1, "CFE");
      }
      result.token0Add += sResult.token0Add;
      result.token1Add += sResult.token1Add;
    }
  }

  function collectFeeWithLp(uint256 lpTokenId, address recipient)
    internal
    returns (FeeResult memory result)
  {
    INonfungiblePositionManager inpm = INonfungiblePositionManager(
      UniswapConstants.UNISWAP_V3_POSITIONS
    );

    (, , result.token0, result.token1, , , , , , , , ) = inpm.positions(
      lpTokenId
    );

    (result.token0Add, result.token1Add) = inpm.collect(
      INonfungiblePositionManager.CollectParams({
        tokenId: lpTokenId,
        recipient: recipient,
        amount0Max: type(uint128).max,
        amount1Max: type(uint128).max
      })
    );
  }

  function swapPoolFeeToTokenOut(SwapPoolFeeToTokenOutParams memory params)
    internal
    returns (uint256 amountOut)
  {
    uint256 amountOutPart1;
    uint256 otherAmount;
    address other;
    if (params.token0 == params.tokenOut) {
      amountOutPart1 = params.token0Amount;
      otherAmount = params.token1Amount;
      other = params.token1;
    } else {
      require(params.token1 == params.tokenOut);
      amountOutPart1 = params.token1Amount;
      otherAmount = params.token0Amount;
      other = params.token0;
    }
    uint256 amountOutPart2 = 0;
    if (otherAmount > 0) {
      amountOutPart2 = swapInputSingle(
        other,
        params.tokenOut,
        params.fee,
        otherAmount,
        params.recipient
      );
    }
    amountOut = amountOutPart1 + amountOutPart2;
  }

  function swapInputSingle(
    address tokenIn,
    address tokenOut,
    uint24 fee,
    uint256 amountIn,
    address recipient
  ) internal returns (uint256 amountOut) {
    if (
      IERC20(tokenIn).allowance(
        address(this),
        UniswapConstants.UNISWAP_ROUTER
      ) < amountIn
    ) {
      IERC20(tokenIn).approve(
        UniswapConstants.UNISWAP_ROUTER,
        type(uint256).max
      );
    }

    amountOut = SwapRouterHelper.exactInputSingle(
      ISwapRouter02.ExactInputSingleParams({
        tokenIn: tokenIn,
        tokenOut: tokenOut,
        fee: fee,
        recipient: recipient,
        amountIn: amountIn,
        amountOutMinimum: 0,
        sqrtPriceLimitX96: 0
      })
    );
  }
}

// File: contracts/interfaces/external/ISwapRouter02.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface ISwapRouter02 {
  struct ExactInputSingleParams {
    address tokenIn;
    address tokenOut;
    uint24 fee;
    address recipient;
    uint256 amountIn;
    uint256 amountOutMinimum;
    uint160 sqrtPriceLimitX96;
  }

  function exactInputSingle(ExactInputSingleParams calldata params)
    external
    payable
    returns (uint256 amountOut);

  struct ExactInputParams {
    bytes path;
    address recipient;
    uint256 amountIn;
    uint256 amountOutMinimum;
  }

  function exactInput(ExactInputParams calldata params)
    external
    payable
    returns (uint256 amountOut);

  struct ExactOutputSingleParams {
    address tokenIn;
    address tokenOut;
    uint24 fee;
    address recipient;
    uint256 amountOut;
    uint256 amountInMaximum;
    uint160 sqrtPriceLimitX96;
  }

  function exactOutputSingle(ExactOutputSingleParams calldata params)
    external
    payable
    returns (uint256 amountIn);

  struct ExactOutputParams {
    bytes path;
    address recipient;
    uint256 amountOut;
    uint256 amountInMaximum;
  }

  function exactOutput(ExactOutputParams calldata params)
    external
    payable
    returns (uint256 amountIn);

  function refundETH() external payable;
}

// File: contracts/interfaces/IFactory.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
pragma abicoder v2;

import "../libraries/UniswapStruct.sol";

interface IFactory {
  function getOspPoolConfigs(uint256 index)
    external
    view
    returns (
      uint24 fee,
      uint160 findOspPoolInitSqrtPriceX96,
      UniswapStruct.Position[] memory findOspPoolPositions,
      uint160 ospFindPoolInitSqrtPriceX96,
      UniswapStruct.Position[] memory ospFindPoolPositions
    );

  function findInfo()
    external
    view
    returns (
      address token,
      address pool,
      uint256 cnftTokenId,
      uint256 onftTokenId,
      uint24 fee
    );

  function findLpTokenIdList()
    external
    view
    returns (uint256[] memory lpTokenIdList);

  function token2OspInfo(address _token)
    external
    view
    returns (
      uint256 poolConfigIndex,
      uint256 stars,
      address pool,
      uint256 cnftTokenId,
      uint256 onftTokenId,
      string memory projectId
    );

  function ospLpTokenIdList(address osp)
    external
    view
    returns (uint256[] memory lpTokenIdList);
}

// File: contracts/interfaces/IFindNFT.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IFindNFT {
  struct Info {
    string name;
    string symbol;
    string projectId;
    uint256 stars;
    address token;
    uint256 percent;
    bool isCnft;
    uint256 tokenId;
    uint256 timestamp;
  }

  struct MintParams {
    string name;
    string symbol;
    string projectId;
    uint256 stars;
    address token;
    uint256 percent;
    bool isCnft;
    address owner;
  }

  function ownerOf(uint256 tokenId) external view returns (address owner);

  function tokenId2Info(uint256 _tokenId)
    external
    view
    returns (
      string memory name,
      string memory symbol,
      string memory projectId,
      uint256 stars,
      address token,
      uint256 percent,
      bool isCnft,
      uint256 tokenId,
      uint256 timestamp
    );

  function mint(MintParams memory params) external returns (uint256 tokenId);

  function safeTransferFrom(
    address from,
    address to,
    uint256 tokenId
  ) external;

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

  function claim(uint256 tokenId) external;
}

// File: @openzeppelin/contracts/utils/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: @openzeppelin/contracts/utils/Strings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_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) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_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: @openzeppelin/contracts/token/ERC721/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: contracts/interfaces/external/INonfungiblePositionManager.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;
pragma abicoder v2;

interface INonfungiblePositionManager {
  function tokenOfOwnerByIndex(address owner, uint256 index)
    external
    view
    returns (uint256 tokenId);

  function refundETH() external payable;

  function factory() external view returns (address);

  function WETH9() external view returns (address);

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

  function ownerOf(uint256 tokenId) external view returns (address owner);

  function positions(uint256 tokenId)
    external
    view
    returns (
      uint96 nonce,
      address operator,
      address token0,
      address token1,
      uint24 fee,
      int24 tickLower,
      int24 tickUpper,
      uint128 liquidity,
      uint256 feeGrowthInside0LastX128,
      uint256 feeGrowthInside1LastX128,
      uint128 tokensOwed0,
      uint128 tokensOwed1
    );

  struct MintParams {
    address token0;
    address token1;
    uint24 fee;
    int24 tickLower;
    int24 tickUpper;
    uint256 amount0Desired;
    uint256 amount1Desired;
    uint256 amount0Min;
    uint256 amount1Min;
    address recipient;
    uint256 deadline;
  }

  function createAndInitializePoolIfNecessary(
    address token0,
    address token1,
    uint24 fee,
    uint160 sqrtPriceX96
  ) external payable returns (address pool);

  function mint(MintParams calldata params)
    external
    payable
    returns (
      uint256 tokenId,
      uint128 liquidity,
      uint256 amount0,
      uint256 amount1
    );

  struct CollectParams {
    uint256 tokenId;
    address recipient;
    uint128 amount0Max;
    uint128 amount1Max;
  }

  function collect(CollectParams calldata params)
    external
    payable
    returns (uint256 amount0, uint256 amount1);
}

// File: contracts/libraries/SwapRouterHelper.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

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

import "../interfaces/external/ISwapRouter02.sol";

import "./UniswapConstants.sol";
import "./Path.sol";

library SwapRouterHelper {
  function exactInputSingle(ISwapRouter02.ExactInputSingleParams memory params)
    internal
    returns (uint256 amountOut)
  {
    uint256 balanceOfBefore = IERC20(params.tokenIn).balanceOf(address(this));

    amountOut = ISwapRouter02(UniswapConstants.UNISWAP_ROUTER).exactInputSingle(
        params
      );

    uint256 balanceOfAfter = IERC20(params.tokenIn).balanceOf(address(this));
    require(balanceOfBefore - balanceOfAfter == params.amountIn, "IE");
  }

  function exactInput(ISwapRouter02.ExactInputParams memory params)
    internal
    returns (uint256 amountOut)
  {
    uint256[] memory balanceOfsBefore = _getPathTokenBalanceOf(params.path);

    amountOut = ISwapRouter02(UniswapConstants.UNISWAP_ROUTER).exactInput(
      params
    );
    uint256[] memory balanceOfsAfter = _getPathTokenBalanceOf(params.path);
    _assertBalanceOf(balanceOfsBefore, balanceOfsAfter, params.amountIn);
  }

  function _assertBalanceOf(
    uint256[] memory balanceOfsBefore,
    uint256[] memory balanceOfsAfter,
    uint256 amountIn
  ) private pure {
    require(balanceOfsBefore.length == balanceOfsAfter.length, "LE");

    for (uint256 i = 0; i < balanceOfsBefore.length; i++) {
      if (i == 0) {
        require(balanceOfsBefore[i] - balanceOfsAfter[i] == amountIn, "LEF0");
      } else {
        require(balanceOfsBefore[i] == balanceOfsAfter[i], "LEF1");
      }
    }
  }

  function _getPathTokenBalanceOf(bytes memory path)
    private
    view
    returns (uint256[] memory balanceOfs)
  {
    balanceOfs = new uint256[](Path.numPools(path));
    for (uint256 i = 0; i < balanceOfs.length; i++) {
      if (i == 0) {
        (address tokenIn, , ) = Path.decodeFirstPool(path);
        balanceOfs[i] = IERC20(tokenIn).balanceOf(address(this));
      } else {
        path = Path.skipToken(path);
        (address tokenIn, , ) = Path.decodeFirstPool(path);
        balanceOfs[i] = IERC20(tokenIn).balanceOf(
          UniswapConstants.UNISWAP_ROUTER
        );
      }
    }
  }
}

// File: contracts/libraries/Path.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "./BytesLib.sol";

library Path {
  using BytesLib for bytes;

  uint256 private constant ADDR_SIZE = 20;

  uint256 private constant FEE_SIZE = 3;

  uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;

  uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;

  uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET;

  function hasMultiplePools(bytes memory path) internal pure returns (bool) {
    return path.length >= MULTIPLE_POOLS_MIN_LENGTH;
  }

  function numPools(bytes memory path) internal pure returns (uint256) {
    return ((path.length - ADDR_SIZE) / NEXT_OFFSET);
  }

  function decodeFirstPool(bytes memory path)
    internal
    pure
    returns (
      address tokenA,
      address tokenB,
      uint24 fee
    )
  {
    tokenA = path.toAddress(0);
    fee = path.toUint24(ADDR_SIZE);
    tokenB = path.toAddress(NEXT_OFFSET);
  }

  function getFirstPool(bytes memory path)
    internal
    pure
    returns (bytes memory)
  {
    return path.slice(0, POP_OFFSET);
  }

  function skipToken(bytes memory path) internal pure returns (bytes memory) {
    return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);
  }
}

// File: contracts/libraries/BytesLib.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

library BytesLib {
  function slice(
    bytes memory _bytes,
    uint256 _start,
    uint256 _length
  ) internal pure returns (bytes memory) {
    require(_length + 31 >= _length, "slice_overflow");
    require(_start + _length >= _start, "slice_overflow");
    require(_bytes.length >= _start + _length, "slice_outOfBounds");

    bytes memory tempBytes;

    assembly {
      switch iszero(_length)
      case 0 {
        tempBytes := mload(0x40)

        let lengthmod := and(_length, 31)

        let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
        let end := add(mc, _length)

        for {
          let cc := add(
            add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))),
            _start
          )
        } lt(mc, end) {
          mc := add(mc, 0x20)
          cc := add(cc, 0x20)
        } {
          mstore(mc, mload(cc))
        }

        mstore(tempBytes, _length)

        mstore(0x40, and(add(mc, 31), not(31)))
      }
      default {
        tempBytes := mload(0x40)

        mstore(tempBytes, 0)

        mstore(0x40, add(tempBytes, 0x20))
      }
    }

    return tempBytes;
  }

  function toAddress(bytes memory _bytes, uint256 _start)
    internal
    pure
    returns (address)
  {
    require(_start + 20 >= _start, "toAddress_overflow");
    require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
    address tempAddress;

    assembly {
      tempAddress := div(
        mload(add(add(_bytes, 0x20), _start)),
        0x1000000000000000000000000
      )
    }

    return tempAddress;
  }

  function toUint24(bytes memory _bytes, uint256 _start)
    internal
    pure
    returns (uint24)
  {
    require(_start + 3 >= _start, "toUint24_overflow");
    require(_bytes.length >= _start + 3, "toUint24_outOfBounds");
    uint24 tempUint;

    assembly {
      tempUint := mload(add(add(_bytes, 0x3), _start))
    }

    return tempUint;
  }
}

// File: contracts/libraries/UniswapStruct.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6;

library UniswapStruct {
  struct Position {
    int24 tickLower;
    int24 tickUpper;
    uint256 amount;
  }
}