Contract Address Details

0x9d1Ab542B1581aCFd84B21900aD68EcAF02e4AE9

Contract Name
PostAuctionLauncher
Creator
0xb7d31d–e8f8e8 at 0x7e4355–d091f6
Balance
0 mADA
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
43009356
Contract name:
PostAuctionLauncher




Optimization enabled
true
Compiler version
v0.6.12+commit.27d51765




Optimization runs
200
Verified at
2023-12-15T01:46:07.663979Z

Constructor Arguments

000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f9

Arg [0] (address) : 0xae83571000af4499798d1e3b0fa0070eb3a3e3f9

              

contracts/Liquidity/PostAuctionLauncher.sol

pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

// Post Auction Launcher
//
// A post auction contract that takes the proceeds and creates a liquidity pool
//
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// ---------------------------------------------------------------------
// SPDX-License-Identifier: GPL-3.0
// ---------------------------------------------------------------------

import "../OpenZeppelin/utils/ReentrancyGuard.sol";
import "../Access/IHubAccessControls.sol";
import "../Utils/SafeTransfer.sol";
import "../Utils/ERC721Holder.sol";
import "../Utils/BoringMath.sol";
// interfaces of Tangleswap
import "../Tangleswap/core/interfaces/ITangleswapPool.sol";
import "../Tangleswap/core/interfaces/ITangleswapFactory.sol";
import "../Tangleswap/periphery/interfaces/INonfungiblePositionManager.sol";
import "../Utils/TangleswapCallingParams.sol";
import "../interfaces/IWETH9.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/IIHubAuction.sol";
import "../interfaces/IIHubAccessControls.sol";

// Uncomment if needed.
// import "hardhat/console.sol";

contract PostAuctionLauncher is IHubAccessControls, SafeTransfer, ReentrancyGuard, ERC721Holder {
    using BoringMath for uint256;
    using BoringMath128 for uint128;
    using BoringMath64 for uint64;
    using BoringMath32 for uint32;
    using BoringMath16 for uint16;

    address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    uint256 private constant LIQUIDITY_PRECISION = 10000;

    /// @notice IHubLiquidity template id.
    // solhint-disable-next-line const-name-snakecase
    uint256 public constant liquidityTemplate = 3;

    /// @notice First Token address.
    IERC20 public token1;
    /// @notice Second Token address.
    IERC20 public token2;
    // Tangleswap TangleswapPool->fee
    uint24 public fee;
    // nft id
    uint256 public tokenId;
    /// @dev Contract of the Tangleswap Nonfungible Position Manager.
    address public nonfungiblePositionManager;
    // notice Tangleswap factory address
    ITangleswapFactory public factory;
    /// @notice WETH contract address.
    address private immutable weth;

    /// @notice LP pair address.
    address public tokenPair;
    /// @notice Withdraw wallet address.
    address public wallet;
    /// @notice Token market contract address.
    IIHubAuction public market;

    struct LauncherInfo {
        uint32 locktime;
        uint64 unlock;
        uint16 liquidityPercent;
        bool launched;
        uint128 liquidityAdded;
    }
    LauncherInfo public launcherInfo;

    /// @notice Emitted when LP contract is initialised.
    event InitLiquidityLauncher(address indexed token1, address indexed token2, address factory, address sender);
    /// @notice Emitted when LP is launched.
    event LiquidityAdded(uint256 liquidity);
    /// @notice Emitted when wallet is updated.
    event WalletUpdated(address indexed wallet);
    /// @notice Emitted when launcher is cancelled.
    event LauncherCancelled(address indexed wallet);
    /// @notice Emitted when nft is withdrawn。
    event NftWithdrawn(address indexed to, uint256 tokenId);

    constructor(address _weth) public {
        weth = _weth;
    }

    /**
     * @notice Initializes main contract variables (requires launchwindow to be more than 2 days.)
     * @param _nonfungiblePositionManager Contract of the Tangleswap Nonfungible Position Manager.
     * @param _market Auction address for launcher.
     * @param _factory Tangleswap factory address.
     * @param _admin Contract owner address.
     * @param _wallet Withdraw wallet address.
     * @param _liquidityPercent Percentage of payment currency sent to liquidity pool.
     * @param _locktime How long the liquidity will be locked. Number of seconds.
     */
    function initAuctionLauncher(
        address _nonfungiblePositionManager,
        address _market,
        address _factory,
        address _admin,
        address _wallet,
        uint256 _liquidityPercent,
        uint256 _locktime
    ) public {
        // only market admin can call initAuctionLauncher
        require(IIHubAccessControls(_market).hasAdminRole(msg.sender), "Only market admin");
        require(_locktime > 0 && _locktime < 10000000000, "In seconds, not miliseconds");
        require(_liquidityPercent <= LIQUIDITY_PRECISION, "Greater than 100.00% (>10000)");
        require(_liquidityPercent > 0, "Liquidity percentage equals zero");
        require(_admin != address(0), "Admin is the zero address");
        require(_wallet != address(0), "Wallet is the zero address");

        initAccessControls(_admin);

        market = IIHubAuction(_market);
        token1 = IERC20(market.paymentCurrency());
        token2 = IERC20(market.auctionToken());
        // TangleswapPool->fee
        fee = market.fee();

        if (address(token1) == ETH_ADDRESS) {
            token1 = IERC20(weth);
        }

        uint256 d1 = uint256(token1.decimals());
        uint256 d2 = uint256(token2.decimals());
        require(d2 >= d1, "d2 must greater or equal to d1");

        // Tangleswap
        nonfungiblePositionManager = _nonfungiblePositionManager;
        factory = ITangleswapFactory(_factory);
        tokenPair = factory.getPool(address(token1), address(token2), fee);

        wallet = _wallet;
        launcherInfo.liquidityPercent = BoringMath.to16(_liquidityPercent);
        launcherInfo.locktime = BoringMath.to32(_locktime);

        uint256 initalTokenAmount = market.getTotalTokens().mul(_liquidityPercent).div(LIQUIDITY_PRECISION);
        _safeTransferFrom(address(token2), msg.sender, initalTokenAmount);

        emit InitLiquidityLauncher(address(token1), address(token2), address(_factory), _admin);
    }

    receive() external payable {
        if (msg.sender != weth) {
            depositETH();
        }
    }

    /// @notice Deposits ETH to the contract.
    function depositETH() public payable {
        require(address(token1) == weth || address(token2) == weth, "Launcher not accepting ETH");
        require(!launcherInfo.launched, "Must first launch liquidity");
        require(launcherInfo.liquidityAdded == 0, "Liquidity already added");
        if (msg.value > 0) {
            IWETH(weth).deposit{value: msg.value}();
        }
    }

    /**
     * @notice Deposits first Token to the contract.
     * @param _amount Number of tokens to deposit.
     */
    function depositToken1(uint256 _amount) external returns (bool success) {
        return _deposit(address(token1), msg.sender, _amount);
    }

    /**
     * @notice Deposits second Token to the contract.
     * @param _amount Number of tokens to deposit.
     */
    function depositToken2(uint256 _amount) external returns (bool success) {
        return _deposit(address(token2), msg.sender, _amount);
    }

    /**
     * @notice Deposits Tokens to the contract.
     * @param _amount Number of tokens to deposit.
     * @param _from Where the tokens to deposit will come from.
     * @param _token Token address.
     */
    function _deposit(address _token, address _from, uint _amount) internal returns (bool success) {
        require(!launcherInfo.launched, "Must first launch liquidity");
        require(launcherInfo.liquidityAdded == 0, "Liquidity already added");

        require(_amount > 0, "Amount must be greater than 0");
        _safeTransferFrom(_token, _from, _amount);
        return true;
    }

    /**
     * @notice Checks if market wallet is set to this launcher
     */
    function marketConnected() public view returns (bool) {
        return market.wallet() == address(this);
    }

    /**
     * @notice Finalizes Token sale and launches LP.
     * @return liquidity Number of LPs.
     */
    function finalize(
        uint160 sqrtPriceX96,
        int24 tickLower,
        int24 tickUpper
    ) external nonReentrant returns (uint256 liquidity) {
        // GP: Can we remove admin, let anyone can finalise and launch?
        require(hasAdminRole(msg.sender) || hasOperatorRole(msg.sender), "Sender must be operator");
        // solhint-disable-next-line reason-string
        require(marketConnected(), "Auction must have this launcher address set as the destination wallet");
        require(!launcherInfo.launched, "Auction must be unlaunched");

        if (!market.finalized()) {
            market.finalize();
        }

        launcherInfo.launched = true;
        if (!market.auctionSuccessful()) {
            return 0;
        }

        /// @dev if the auction is settled in weth, wrap any contract balance
        uint256 launcherBalance = address(this).balance;
        if (launcherBalance > 0) {
            IWETH(weth).deposit{value: launcherBalance}();
        }

        (uint256 token1Amount, uint256 token2Amount) = getTokenAmounts();

        /// @dev cannot start a liquidity pool with no tokens on either side
        if (token1Amount == 0 || token2Amount == 0) {
            return 0;
        }

        // Tangleswap TangleswapFactory.getPool
        address pair = factory.getPool(address(token1), address(token2), fee);
        require(pair == address(0) || ITangleswapPool(pair).liquidity() == 0, "PostLiquidity: Pair not new");
        if (pair == address(0)) {
            createPool(sqrtPriceX96);
        }

        // @dev add liquidity to pool via the nonfungiblePositionManager --- work with Tangleswap
        _safeApprove(address(token1), nonfungiblePositionManager, token1Amount);
        _safeApprove(address(token2), nonfungiblePositionManager, token2Amount);
        INonfungiblePositionManager.MintParams memory mintParam = TangleswapCallingParams.mintParams(
            address(token1) < address(token2) ? address(token1) : address(token2),
            address(token1) < address(token2) ? address(token2) : address(token1),
            fee,
            address(token1) < address(token2) ? token1Amount : token2Amount,
            address(token1) < address(token2) ? token2Amount : token1Amount,
            tickLower,
            tickUpper,
            type(uint256).max
        );
        (tokenId, liquidity, , ) = INonfungiblePositionManager(nonfungiblePositionManager).mint(mintParam);
        require(liquidity > 1e7, "liquidity too small!");

        launcherInfo.liquidityAdded = BoringMath.to128(uint256(launcherInfo.liquidityAdded).add(liquidity));

        /// @dev if unlock time not yet set, add it.
        if (launcherInfo.unlock == 0) {
            // solhint-disable-next-line not-rely-on-time
            launcherInfo.unlock = BoringMath.to64(block.timestamp + uint256(launcherInfo.locktime));
        }
        emit LiquidityAdded(liquidity);
    }

    function getTokenAmounts() public view returns (uint256 token1Amount, uint256 token2Amount) {
        token1Amount = getToken1Balance().mul(uint256(launcherInfo.liquidityPercent)).div(LIQUIDITY_PRECISION);
        token2Amount = getToken2Balance();

        uint256 tokenPrice = market.tokenPrice();
        uint256 d2 = uint256(token2.decimals());
        uint256 maxToken1Amount = token2Amount.mul(tokenPrice).div(10 ** (d2));
        uint256 maxToken2Amount = token1Amount.mul(10 ** (d2)).div(tokenPrice);

        /// @dev if more than the max.
        if (token2Amount > maxToken2Amount) {
            token2Amount = maxToken2Amount;
        }
        /// @dev if more than the max.
        if (token1Amount > maxToken1Amount) {
            token1Amount = maxToken1Amount;
        }
    }

    /**
     * @notice Tangleswap Withdraws NFT from the contract.
     */
    function withdrawNft() external {
        require(hasAdminRole(msg.sender) || hasOperatorRole(msg.sender), "Sender must be operator");
        require(
            tokenId > 0 && INonfungiblePositionManager(nonfungiblePositionManager).ownerOf(tokenId) == address(this),
            "Invalid nft"
        );
        require(launcherInfo.launched, "Must first launch liquidity");
        // solhint-disable-next-line not-rely-on-time
        require(block.timestamp >= uint256(launcherInfo.unlock), "Liquidity is locked");
        INonfungiblePositionManager(nonfungiblePositionManager).safeTransferFrom(address(this), wallet, tokenId);

        emit NftWithdrawn(wallet, tokenId);
    }

    /// @notice Withraws deposited tokens and ETH from the contract to wallet.
    function withdrawDeposits() external {
        require(hasAdminRole(msg.sender) || hasOperatorRole(msg.sender), "Sender must be operator");
        require(launcherInfo.launched, "Must first launch liquidity");
        // solhint-disable-next-line not-rely-on-time
        require(block.timestamp >= uint256(launcherInfo.unlock), "PostAuction: Liquidity is locked");

        uint256 token1Amount = getToken1Balance();
        if (token1Amount > 0) {
            _safeTransfer(address(token1), wallet, token1Amount);
        }
        uint256 token2Amount = getToken2Balance();
        if (token2Amount > 0) {
            _safeTransfer(address(token2), wallet, token2Amount);
        }
    }

    // TODO
    // GP: Sweep non relevant ERC20s / ETH

    //--------------------------------------------------------
    // Setter functions
    //--------------------------------------------------------

    /**
     * @notice Admin can set the wallet through this function.
     * @param _wallet Wallet is where funds will be sent.
     */
    function setWallet(address payable _wallet) external {
        require(hasAdminRole(msg.sender), "sender must be an admin");
        require(_wallet != address(0), "Wallet is the zero address");

        wallet = _wallet;

        emit WalletUpdated(_wallet);
    }

    function cancelLauncher() external {
        require(hasAdminRole(msg.sender), "sender must be an admin");
        require(!launcherInfo.launched, "already launched");

        launcherInfo.launched = true;
        emit LauncherCancelled(msg.sender);
    }

    //--------------------------------------------------------
    // Helper functions
    //--------------------------------------------------------

    /**
     * @notice Creates new SLP pair through TangleSwap.
     */
    function createPool(uint160 sqrtPriceX96) internal {
        // Tangleswap TangleswapFactory.createPool
        tokenPair = factory.createPool(address(token1), address(token2), fee);
        // Sets the initial price for the pool
        ITangleswapPool(tokenPair).initialize(sqrtPriceX96);
    }

    //--------------------------------------------------------
    // Getter functions
    //--------------------------------------------------------

    /**
     * @notice Gets the number of first token deposited into this contract.
     * @return uint256 Number of WETH.
     */
    function getToken1Balance() public view returns (uint256) {
        return token1.balanceOf(address(this));
    }

    /**
     * @notice Gets the number of second token deposited into this contract.
     * @return uint256 Number of WETH.
     */
    function getToken2Balance() public view returns (uint256) {
        return token2.balanceOf(address(this));
    }

    /**
     * @notice Returns LP token address.
     * @return address LP address.
     */
    function getLPTokenAddress() public view returns (address) {
        return tokenPair;
    }

    //--------------------------------------------------------
    // Init functions
    //--------------------------------------------------------

    /**
     * @notice Decodes and hands auction data to the initAuction function.
     * @param _data Encoded data for initialization.
     */
    // solhint-disable-next-line no-empty-blocks
    function init(bytes calldata _data) external payable {}

    function initLauncher(bytes calldata _data) public {
        (
            address _nonfungiblePositionManager,
            address _market,
            address _factory,
            address _admin,
            address _wallet,
            uint256 _liquidityPercent,
            uint256 _locktime
        ) = abi.decode(_data, (address, address, address, address, address, uint256, uint256));
        initAuctionLauncher(
            _nonfungiblePositionManager,
            _market,
            _factory,
            _admin,
            _wallet,
            _liquidityPercent,
            _locktime
        );
    }

    /**
     * @notice Collects data to initialize the auction and encodes them.
     * @param _nonfungiblePositionManager Contract of the Tangleswap Nonfungible Position Manager.
     * @param _market Auction address for launcher.
     * @param _factory Tangleswap factory address.
     * @param _admin Contract owner address.
     * @param _wallet Withdraw wallet address.
     * @param _liquidityPercent Percentage of payment currency sent to liquidity pool.
     * @param _locktime How long the liquidity will be locked. Number of seconds.
     * @return _data All the data in bytes format.
     */
    function getLauncherInitData(
        address _nonfungiblePositionManager,
        address _market,
        address _factory,
        address _admin,
        address _wallet,
        uint256 _liquidityPercent,
        uint256 _locktime
    ) external pure returns (bytes memory _data) {
        return
            abi.encode(_nonfungiblePositionManager, _market, _factory, _admin, _wallet, _liquidityPercent, _locktime);
    }
}
        

contracts/Tangleswap/periphery/interfaces/INonfungiblePositionManager.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

/// @title Non-fungible token for positions
/// @notice Wraps Tangleswap positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager {
    /// @notice Creates a new pool if it does not exist, then initializes if not initialized
    /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
    /// @param token0 The contract address of token0 of the pool
    /// @param token1 The contract address of token1 of the pool
    /// @param fee The fee amount of the pool for the specified token pair
    /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
    /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
    function createAndInitializePoolIfNecessary(
        address token0,
        address token1,
        uint24 fee,
        uint160 sqrtPriceX96
    ) external payable returns (address pool);

    /// @return Returns the address of the Tangleswap factory
    function factory() external view returns (address);

    /// @return Returns the address of WETH9
    // solhint-disable-next-line func-name-mixedcase
    function WETH9() external view returns (address);

    // details about the Tangleswap position
    struct Position {
        // the nonce for permits
        uint96 nonce;
        // the address that is approved for spending this token
        address operator;
        // the ID of the pool with which this token is connected
        uint80 poolId;
        // the tick range of the position
        int24 tickLower;
        int24 tickUpper;
        // the liquidity of the position
        uint128 liquidity;
        // the fee growth of the aggregate position as of the last action on the individual position
        uint256 feeGrowthInside0LastX128;
        uint256 feeGrowthInside1LastX128;
        // how many uncollected tokens are owed to the position, as of the last computation
        uint128 tokensOwed0;
        uint128 tokensOwed1;
    }

    /// @notice Returns the position information associated with a given token ID.
    /// @dev Throws if the token ID is not valid.
    /// @param tokenId The ID of the token that represents the position
    /// @return nonce The nonce for permits
    /// @return operator The address that is approved for spending
    /// @return token0 The address of the token0 for a specific pool
    /// @return token1 The address of the token1 for a specific pool
    /// @return fee The fee associated with the pool
    /// @return tickLower The lower end of the tick range for the position
    /// @return tickUpper The higher end of the tick range for the position
    /// @return liquidity The liquidity of the position
    /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
    /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
    /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
    /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
    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;
    }

    /// @notice Refunds any ETH balance held by this contract to the `msg.sender`
    /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
    /// that use ether for the input amount
    function refundETH() external payable;

    /// @notice Creates a new position wrapped in a NFT
    /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
    /// a method does not exist, i.e. the pool is assumed to be initialized.
    /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
    /// @return tokenId The ID of the token that represents the minted position
    /// @return liquidity The amount of liquidity for this position
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function mint(
        MintParams calldata params
    ) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Decreases the amount of liquidity in a position and accounts it to the position
    /// @param params tokenId The ID of the token for which liquidity is being decreased,
    /// amount The amount by which liquidity will be decreased,
    /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
    /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return amount0 The amount of token0 accounted to the position's tokens owed
    /// @return amount1 The amount of token1 accounted to the position's tokens owed
    function decreaseLiquidity(
        DecreaseLiquidityParams calldata params
    ) external payable returns (uint256 amount0, uint256 amount1);

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

    /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
    /// @param params tokenId The ID of the NFT for which tokens are being collected,
    /// recipient The account that should receive the tokens,
    /// amount0Max The maximum amount of token0 to collect,
    /// amount1Max The maximum amount of token1 to collect
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

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

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

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

    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address owner) external view returns (uint256);

    // @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `index` >= `balanceOf(owner)` or if
    ///  `owner` is the zero address, representing invalid NFTs.
    /// @param owner An address where we are interested in NFTs owned by them
    /// @param index A counter less than `balanceOf(owner)`
    /// @return The token identifier for the `_index`th NFT assigned to `owner`,
    ///   (sort order not specified)
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    function totalSupply() external view returns (uint256);

    function approve(address to, uint256 tokenId) external;
}
          

contracts/Access/IHubAccessControls.sol

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.6.12;

import "./IHubAdminAccess.sol";

/**
 * @notice Access Controls
 * @author Attr: BlockRocket.tech
 */
contract IHubAccessControls is IHubAdminAccess {
    /// @notice Role definitions
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant SMART_CONTRACT_ROLE = keccak256("SMART_CONTRACT_ROLE");
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    /**
     * @notice The deployer is automatically given the admin role which will allow them to then grant roles to other addresses
     */
    // solhint-disable-next-line no-empty-blocks
    constructor() public {}

    /////////////
    // Lookups //
    /////////////

    /**
     * @notice Used to check whether an address has the minter role
     * @param _address EOA or contract being checked
     * @return bool True if the account has the role or false if it does not
     */
    function hasMinterRole(address _address) public view returns (bool) {
        return hasRole(MINTER_ROLE, _address);
    }

    /**
     * @notice Used to check whether an address has the smart contract role
     * @param _address EOA or contract being checked
     * @return bool True if the account has the role or false if it does not
     */
    function hasSmartContractRole(address _address) public view returns (bool) {
        return hasRole(SMART_CONTRACT_ROLE, _address);
    }

    /**
     * @notice Used to check whether an address has the operator role
     * @param _address EOA or contract being checked
     * @return bool True if the account has the role or false if it does not
     */
    function hasOperatorRole(address _address) public view returns (bool) {
        return hasRole(OPERATOR_ROLE, _address);
    }

    ///////////////
    // Modifiers //
    ///////////////

    /**
     * @notice Grants the minter role to an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract receiving the new role
     */
    function addMinterRole(address _address) external {
        grantRole(MINTER_ROLE, _address);
    }

    /**
     * @notice Removes the minter role from an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract affected
     */
    function removeMinterRole(address _address) external {
        revokeRole(MINTER_ROLE, _address);
    }

    /**
     * @notice Grants the smart contract role to an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract receiving the new role
     */
    function addSmartContractRole(address _address) external {
        grantRole(SMART_CONTRACT_ROLE, _address);
    }

    /**
     * @notice Removes the smart contract role from an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract affected
     */
    function removeSmartContractRole(address _address) external {
        revokeRole(SMART_CONTRACT_ROLE, _address);
    }

    /**
     * @notice Grants the operator role to an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract receiving the new role
     */
    function addOperatorRole(address _address) external {
        grantRole(OPERATOR_ROLE, _address);
    }

    /**
     * @notice Removes the operator role from an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract affected
     */
    function removeOperatorRole(address _address) external {
        revokeRole(OPERATOR_ROLE, _address);
    }
}
          

contracts/Access/IHubAdminAccess.sol

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.6.12;

import "../OpenZeppelin/access/AccessControl.sol";

contract IHubAdminAccess is AccessControl {
    /// @dev Whether access is initialised.
    bool private initAccess;

    /// @notice The deployer is automatically given the admin role which will allow them to then grant roles to other addresses.
    // solhint-disable-next-line no-empty-blocks
    constructor() public {}

    /**
     * @notice Initializes access controls.
     * @param _admin Admins address.
     */
    function initAccessControls(address _admin) public {
        require(!initAccess, "Already initialised");
        require(_admin != address(0), "Incorrect input");
        _setupRole(DEFAULT_ADMIN_ROLE, _admin);
        initAccess = true;
    }

    /////////////
    // Lookups //
    /////////////

    /**
     * @notice Used to check whether an address has the admin role.
     * @param _address EOA or contract being checked.
     * @return bool True if the account has the role or false if it does not.
     */
    function hasAdminRole(address _address) public view returns (bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, _address);
    }

    ///////////////
    // Modifiers //
    ///////////////

    /**
     * @notice Grants the admin role to an address.
     * @dev The sender must have the admin role.
     * @param _address EOA or contract receiving the new role.
     */
    function addAdminRole(address _address) external {
        grantRole(DEFAULT_ADMIN_ROLE, _address);
    }

    /**
     * @notice Removes the admin role from an address.
     * @dev The sender must have the admin role.
     * @param _address EOA or contract affected.
     */
    function removeAdminRole(address _address) external {
        revokeRole(DEFAULT_ADMIN_ROLE, _address);
    }
}
          

contracts/OpenZeppelin/access/AccessControl.sol

pragma solidity 0.6.12;

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

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms.
 *
 * 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 {
    using EnumerableSet for EnumerableSet.AddressSet;

    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

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

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

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

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

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view returns (uint256) {
        return _roles[role].members.length();
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
        return _roles[role].members.at(index);
    }

    /**
     * @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 returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");

        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");

        _revokeRole(role, account);
    }

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

        _revokeRole(role, account);
    }

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

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

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

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}
          

contracts/OpenZeppelin/utils/Context.sol


pragma solidity 0.6.12;

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

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

contracts/OpenZeppelin/utils/EnumerableSet.sol

pragma solidity 0.6.12;

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

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            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] = toDeleteIndex + 1; // All indexes are 1-based

            // 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) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

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

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


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

contracts/OpenZeppelin/utils/ReentrancyGuard.sol


pragma solidity 0.6.12;

/**
 * @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 () internal {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

contracts/Tangleswap/core/interfaces/ITangleswapFactory.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title The interface for the Tangleswap Factory
/// @notice The Tangleswap Factory facilitates creation of Tangleswap pools and control over the protocol fees
interface ITangleswapFactory {
    /// @notice Emitted when the owner of the factory is changed
    /// @param oldOwner The owner before the owner was changed
    /// @param newOwner The owner after the owner was changed
    event OwnerChanged(address indexed oldOwner, address indexed newOwner);

    /// @notice Emitted when a pool is created
    /// @param token0 The first token of the pool by address sort order
    /// @param token1 The second token of the pool by address sort order
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks
    /// @param pool The address of the created pool
    event PoolCreated(
        address indexed token0,
        address indexed token1,
        uint24 indexed fee,
        int24 tickSpacing,
        address pool
    );

    /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
    /// @param fee The enabled fee, denominated in hundredths of a bip
    /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
    event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);

    /// @notice Returns the current owner of the factory
    /// @dev Can be changed by the current owner via setOwner
    /// @return The address of the factory owner
    function owner() external view returns (address);

    /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
    /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
    /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
    /// @return The tick spacing
    function feeAmountTickSpacing(uint24 fee) external view returns (int24);

    /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
    /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
    /// @param tokenA The contract address of either token0 or token1
    /// @param tokenB The contract address of the other token
    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @return pool The pool address
    function getPool(
        address tokenA,
        address tokenB,
        uint24 fee
    ) external view returns (address pool);

    /// @notice Creates a pool for the given two tokens and fee
    /// @param tokenA One of the two tokens in the desired pool
    /// @param tokenB The other of the two tokens in the desired pool
    /// @param fee The desired fee for the pool
    /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
    /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
    /// are invalid.
    /// @return pool The address of the newly created pool
    function createPool(
        address tokenA,
        address tokenB,
        uint24 fee
    ) external returns (address pool);

    /// @notice Updates the owner of the factory
    /// @dev Must be called by the current owner
    /// @param _owner The new owner of the factory
    function setOwner(address _owner) external;

    /// @notice Enables a fee amount with the given tickSpacing
    /// @dev Fee amounts may never be removed once enabled
    /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
    /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
    function enableFeeAmount(uint24 fee, int24 tickSpacing) external;

    /// @notice Burn Wallet (used for buyback-and-burn of VOID)
    /// @dev Can be changed by the current owner via modifyBurnWallet
    /// @return The address of burn wallet
    function burnWallet() external view returns (address);

    /// @notice Update burn wallet
    /// @dev Must be called by the current owner
    /// @param _burnWallet The new burn wallet
    function modifyBurnWallet(address _burnWallet) external;

    /// @notice Initialize NonfungiblePositionManager address
    /// @dev The nonfungiblePositionManager address will be initialized when NonfungiblePositionManager contract deployed, and it can only be initialized once
    function initNonfungiblePositionManager(address _nonfungiblePositionManager) external;

    /// @return Returns current nonfungiblePositionManager address
    function nonfungiblePositionManager() external returns (address);
}
          

contracts/Tangleswap/core/interfaces/ITangleswapPool.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

interface ITangleswapPool {
    /// @notice Sets the initial price for the pool
    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
    function initialize(uint160 sqrtPriceX96) external;

    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// observationIndex The index of the last oracle observation that was written,
    /// observationCardinality The current maximum number of observations stored in the pool,
    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
    /// feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice Collects tokens owed to a position
    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
    /// @param recipient The address which should receive the fees collected
    /// @param tickLower The lower tick of the position for which to collect fees
    /// @param tickUpper The upper tick of the position for which to collect fees
    /// @param amount0Requested How much token0 should be withdrawn from the fees owed
    /// @param amount1Requested How much token1 should be withdrawn from the fees owed
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
    /// @dev Fees must be collected separately via a call to #collect
    /// @param tickLower The lower tick of the position for which to burn liquidity
    /// @param tickUpper The upper tick of the position for which to burn liquidity
    /// @param amount How much liquidity to burn
    /// @return amount0 The amount of token0 sent to the recipient
    /// @return amount1 The amount of token1 sent to the recipient
    function burn(int24 tickLower, int24 tickUpper, uint128 amount) external returns (uint256 amount0, uint256 amount1);

    /// @notice Returns the information about a position by the position's key
    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
    /// @return _liquidity The amount of liquidity in the position,
    /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
    /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
    /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
    /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
    function positions(
        bytes32 key
    )
        external
        view
        returns (
            uint128 _liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
    /// @dev The caller of this method receives a callback in the form of ITangleswapMintCallback#tangleswapMintCallback
    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
    /// on tickLower, tickUpper, the amount of liquidity, and the current price.
    /// @param recipient The address for which the liquidity will be created
    /// @param tickLower The lower tick of the position in which to add liquidity
    /// @param tickUpper The upper tick of the position in which to add liquidity
    /// @param amount The amount of liquidity to mint
    /// @param data Any data that should be passed through to the callback
    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
    function mint(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount,
        bytes calldata data
    ) external returns (uint256 amount0, uint256 amount1);

    /// @notice Swap token0 for token1, or token1 for token0
    /// @dev The caller of this method receives a callback in the form of ITangleswapSwapCallback#tangleswapSwapCallback
    /// @param recipient The address to receive the output of the swap
    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
    /// @param data Any data to be passed through to the callback
    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
    function tickBitmap(int16 wordPosition) external view returns (uint256);

    /// @notice Look up information about a specific tick in the pool
    /// @param tick The tick to look up
    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
    /// tick upper,
    /// liquidityNet how much liquidity changes when the pool price crosses the tick,
    /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
    /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
    /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
    /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
    /// secondsOutside the seconds spent on the other side of the tick from the current tick,
    /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
    /// a specific position.
    function ticks(
        int24 tick
    )
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    /// @notice Returns data about a specific observation index
    /// @param index The element of the observations array to fetch
    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
    /// ago, rather than at a specific index in the array.
    /// @return blockTimestamp The timestamp of the observation,
    /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
    /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
    /// Returns initialized whether the observation has been initialized and the values are safe to use
    function observations(
        uint256 index
    )
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );

    /// @notice The currently in range liquidity available to the pool
    /// @dev This value has no relationship to the total liquidity across all ticks
    function liquidity() external view returns (uint128);

    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal0X128() external view returns (uint256);

    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal1X128() external view returns (uint256);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);
}
          

contracts/Utils/BoringMath.sol

pragma solidity 0.6.12;

/// @notice A library for performing overflow-/underflow-safe math,
/// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).
library BoringMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
        require((c = a + b) >= b, "BoringMath: Add Overflow");
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
        require((c = a - b) <= a, "BoringMath: Underflow");
    }

    function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
        require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
        require(b > 0, "BoringMath: Div zero");
        c = a / b;
    }

    function to128(uint256 a) internal pure returns (uint128 c) {
        require(a <= uint128(-1), "BoringMath: uint128 Overflow");
        c = uint128(a);
    }

    function to64(uint256 a) internal pure returns (uint64 c) {
        require(a <= uint64(-1), "BoringMath: uint64 Overflow");
        c = uint64(a);
    }

    function to32(uint256 a) internal pure returns (uint32 c) {
        require(a <= uint32(-1), "BoringMath: uint32 Overflow");
        c = uint32(a);
    }

    function to16(uint256 a) internal pure returns (uint16 c) {
        require(a <= uint16(-1), "BoringMath: uint16 Overflow");
        c = uint16(a);
    }
}

/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.
library BoringMath128 {
    function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
        require((c = a + b) >= b, "BoringMath: Add Overflow");
    }

    function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
        require((c = a - b) <= a, "BoringMath: Underflow");
    }
}

/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint64.
library BoringMath64 {
    function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
        require((c = a + b) >= b, "BoringMath: Add Overflow");
    }

    function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
        require((c = a - b) <= a, "BoringMath: Underflow");
    }
}

/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
library BoringMath32 {
    function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
        require((c = a + b) >= b, "BoringMath: Add Overflow");
    }

    function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
        require((c = a - b) <= a, "BoringMath: Underflow");
    }
}

/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
library BoringMath16 {
    function add(uint16 a, uint16 b) internal pure returns (uint16 c) {
        require((c = a + b) >= b, "BoringMath: Add Overflow");
    }

    function sub(uint16 a, uint16 b) internal pure returns (uint16 c) {
        require((c = a - b) <= a, "BoringMath: Underflow");
    }
}
          

contracts/Utils/ERC721Holder.sol

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

pragma solidity 0.6.12;

import "../interfaces/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;
    }
}
          

contracts/Utils/SafeTransfer.sol

pragma solidity 0.6.12;

contract SafeTransfer {
    address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @notice Event for token withdrawals.
    event TokensWithdrawn(address token, address to, uint256 amount);

    /// @dev Helper function to handle both ETH and ERC20 payments
    function _safeTokenPayment(address _token, address payable _to, uint256 _amount) internal {
        if (address(_token) == ETH_ADDRESS) {
            _safeTransferETH(_to, _amount);
        } else {
            _safeTransfer(_token, _to, _amount);
        }

        emit TokensWithdrawn(_token, _to, _amount);
    }

    /// @dev Helper function to handle both ETH and ERC20 payments
    function _tokenPayment(address _token, address payable _to, uint256 _amount) internal {
        if (address(_token) == ETH_ADDRESS) {
            _to.transfer(_amount);
        } else {
            _safeTransfer(_token, _to, _amount);
        }

        emit TokensWithdrawn(_token, _to, _amount);
    }

    /// @dev Transfer helper from UniswapV2 Router
    function _safeApprove(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED");
    }

    /**
     * There are many non-compliant ERC20 tokens... this can handle most, adapted from UniSwap V2
     * Im trying to make it a habit to put external calls last (reentrancy)
     * You can put this in an internal function if you like.
     */
    function _safeTransfer(address token, address to, uint256 amount) internal virtual {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = token.call(
            // 0xa9059cbb = bytes4(keccak256("transfer(address,uint256)"))
            abi.encodeWithSelector(0xa9059cbb, to, amount)
        );
        // solhint-disable-next-line reason-string
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED"); // ERC20 Transfer failed
    }

    function _safeTransferFrom(address token, address from, uint256 amount) internal virtual {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = token.call(
            // 0x23b872dd = bytes4(keccak256("transferFrom(address,address,uint256)"))
            abi.encodeWithSelector(0x23b872dd, from, address(this), amount)
        );
        // solhint-disable-next-line reason-string
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED"); // ERC20 TransferFrom failed
    }

    function _safeTransferFrom(address token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        // solhint-disable-next-line reason-string
        require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED");
    }

    function _safeTransferETH(address to, uint value) internal {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, ) = to.call{value: value}(new bytes(0));
        // solhint-disable-next-line reason-string
        require(success, "TransferHelper: ETH_TRANSFER_FAILED");
    }
}
          

contracts/Utils/TangleswapCallingParams.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "../Tangleswap/periphery/interfaces/INonfungiblePositionManager.sol";

library TangleswapCallingParams {
    function mintParams(
        address tokenX,
        address tokenY,
        uint24 fee,
        uint256 amountX,
        uint256 amountY,
        int24 leftPoint,
        int24 rightPoint,
        uint256 deadline
    ) internal view returns (INonfungiblePositionManager.MintParams memory params) {
        params.fee = fee;
        params.tickLower = leftPoint;
        params.tickUpper = rightPoint;
        params.deadline = deadline;
        params.recipient = address(this);
        params.amount0Min = 0;
        params.amount1Min = 0;
        if (tokenX < tokenY) {
            params.token0 = tokenX;
            params.token1 = tokenY;
            params.amount0Desired = amountX;
            params.amount1Desired = amountY;
        } else {
            params.token0 = tokenY;
            params.token1 = tokenX;
            params.amount0Desired = amountY;
            params.amount1Desired = amountX;
        }
    }
}
          

contracts/interfaces/IERC20.sol

pragma solidity 0.6.12;

interface IERC20 {
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    function decimals() external view returns (uint8);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function transferFrom(address from, address to, uint256 amount) external returns (bool);

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

contracts/interfaces/IERC721Receiver.sol

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

pragma solidity 0.6.12;

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

contracts/interfaces/IIHubAccessControls.sol

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.6.12;

interface IIHubAccessControls {
    /**
     * @notice Used to check whether an address has the admin role.
     * @param _address EOA or contract being checked.
     * @return bool True if the account has the role or false if it does not.
     */
    function hasAdminRole(address _address) external view returns (bool);

    /**
     * @notice Used to check whether an address has the minter role
     * @param _address EOA or contract being checked
     * @return bool True if the account has the role or false if it does not
     */
    function hasMinterRole(address _address) external view returns (bool);

    /**
     * @notice Used to check whether an address has the smart contract role
     * @param _address EOA or contract being checked
     * @return bool True if the account has the role or false if it does not
     */
    function hasSmartContractRole(address _address) external view returns (bool);

    /**
     * @notice Used to check whether an address has the operator role
     * @param _address EOA or contract being checked
     * @return bool True if the account has the role or false if it does not
     */
    function hasOperatorRole(address _address) external view returns (bool);

    /**
     * @notice Grants the minter role to an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract receiving the new role
     */
    function addMinterRole(address _address) external;

    /**
     * @notice Removes the minter role from an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract affected
     */
    function removeMinterRole(address _address) external;

    /**
     * @notice Grants the smart contract role to an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract receiving the new role
     */
    function addSmartContractRole(address _address) external;

    /**
     * @notice Removes the smart contract role from an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract affected
     */
    function removeSmartContractRole(address _address) external;

    /**
     * @notice Grants the operator role to an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract receiving the new role
     */
    function addOperatorRole(address _address) external;

    /**
     * @notice Removes the operator role from an address
     * @dev The sender must have the admin role
     * @param _address EOA or contract affected
     */
    function removeOperatorRole(address _address) external;
}
          

contracts/interfaces/IIHubAuction.sol

pragma solidity 0.6.12;

interface IIHubAuction {
    function initAuction(
        address _funder,
        address _token,
        uint256 _tokenSupply,
        uint256 _startDate,
        uint256 _endDate,
        address _paymentCurrency,
        uint24 _fee,
        uint256 _startPrice,
        uint256 _minimumPrice,
        address _operator,
        address _pointList,
        address payable _wallet
    ) external;

    function auctionSuccessful() external view returns (bool);

    function finalized() external view returns (bool);

    function wallet() external view returns (address);

    function paymentCurrency() external view returns (address);

    function auctionToken() external view returns (address);

    // TangleswapPool->fee
    function fee() external view returns (uint24);

    function finalize() external;

    function tokenPrice() external view returns (uint256);

    function getTotalTokens() external view returns (uint256);
}
          

contracts/interfaces/IWETH9.sol

pragma solidity 0.6.12;

import "./IERC20.sol";

interface IWETH is IERC20 {
    function deposit() external payable;

    function withdraw(uint) external;

    function transfer(address, uint) external returns (bool);
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_weth","internalType":"address"}]},{"type":"event","name":"InitLiquidityLauncher","inputs":[{"type":"address","name":"token1","internalType":"address","indexed":true},{"type":"address","name":"token2","internalType":"address","indexed":true},{"type":"address","name":"factory","internalType":"address","indexed":false},{"type":"address","name":"sender","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"LauncherCancelled","inputs":[{"type":"address","name":"wallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"LiquidityAdded","inputs":[{"type":"uint256","name":"liquidity","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NftWithdrawn","inputs":[{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"previousAdminRole","internalType":"bytes32","indexed":true},{"type":"bytes32","name":"newAdminRole","internalType":"bytes32","indexed":true}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TokensWithdrawn","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WalletUpdated","inputs":[{"type":"address","name":"wallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DEFAULT_ADMIN_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"MINTER_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"OPERATOR_ROLE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"SMART_CONTRACT_ROLE","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addAdminRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addMinterRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addOperatorRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addSmartContractRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"cancelLauncher","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"depositETH","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"success","internalType":"bool"}],"name":"depositToken1","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"success","internalType":"bool"}],"name":"depositToken2","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ITangleswapFactory"}],"name":"factory","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint24","name":"","internalType":"uint24"}],"name":"fee","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"finalize","inputs":[{"type":"uint160","name":"sqrtPriceX96","internalType":"uint160"},{"type":"int24","name":"tickLower","internalType":"int24"},{"type":"int24","name":"tickUpper","internalType":"int24"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getLPTokenAddress","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"bytes","name":"_data","internalType":"bytes"}],"name":"getLauncherInitData","inputs":[{"type":"address","name":"_nonfungiblePositionManager","internalType":"address"},{"type":"address","name":"_market","internalType":"address"},{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"_admin","internalType":"address"},{"type":"address","name":"_wallet","internalType":"address"},{"type":"uint256","name":"_liquidityPercent","internalType":"uint256"},{"type":"uint256","name":"_locktime","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"getRoleAdmin","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getRoleMember","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRoleMemberCount","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getToken1Balance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getToken2Balance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"token1Amount","internalType":"uint256"},{"type":"uint256","name":"token2Amount","internalType":"uint256"}],"name":"getTokenAmounts","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"grantRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasAdminRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasMinterRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasOperatorRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"hasSmartContractRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"init","inputs":[{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initAccessControls","inputs":[{"type":"address","name":"_admin","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initAuctionLauncher","inputs":[{"type":"address","name":"_nonfungiblePositionManager","internalType":"address"},{"type":"address","name":"_market","internalType":"address"},{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"_admin","internalType":"address"},{"type":"address","name":"_wallet","internalType":"address"},{"type":"uint256","name":"_liquidityPercent","internalType":"uint256"},{"type":"uint256","name":"_locktime","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initLauncher","inputs":[{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"locktime","internalType":"uint32"},{"type":"uint64","name":"unlock","internalType":"uint64"},{"type":"uint16","name":"liquidityPercent","internalType":"uint16"},{"type":"bool","name":"launched","internalType":"bool"},{"type":"uint128","name":"liquidityAdded","internalType":"uint128"}],"name":"launcherInfo","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"liquidityTemplate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IIHubAuction"}],"name":"market","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"marketConnected","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"nonfungiblePositionManager","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeAdminRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeMinterRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeOperatorRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeSmartContractRole","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"revokeRole","inputs":[{"type":"bytes32","name":"role","internalType":"bytes32"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setWallet","inputs":[{"type":"address","name":"_wallet","internalType":"address payable"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token2","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenId","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tokenPair","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"wallet","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawDeposits","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawNft","inputs":[]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x60806040526004361061031e5760003560e01c80639010d07c116101ab578063d5391393116100f7578063ee16c16011610095578063f6326fb31161006f578063f6326fb3146108f9578063f82fc8ce14610901578063fc4e3e0a14610921578063fc62485a146109415761035d565b8063ee16c160146108af578063f2ea5249146108c4578063f5b541a6146108e45761035d565b8063dccfe310116100d1578063dccfe3101461082d578063ddca3f431461084d578063deaa59df1461086f578063e6594abd1461088f5761035d565b8063d5391393146107d5578063d547741f146107ea578063d73cc4df1461080a5761035d565b8063b7928b1d11610164578063c9d41a401161013e578063c9d41a4014610776578063c9e57aa61461078b578063ca15c873146107a0578063d21220a7146107c05761035d565b8063b7928b1d14610721578063c395fcb314610741578063c45a0155146107615761035d565b80639010d07c1461067757806391d14854146106975780639478941c146106b7578063a217fddf146106d7578063adbf3776146106ec578063b44a27221461070c5761035d565b8063521eb2731161026a5780637f4f577d116102235780638566de4b116101fd5780638566de4b14610618578063857d26081461062d5780638a845fc0146106425780638ed013a6146106625761035d565b80637f4f577d146105c8578063801ea6ec146105ee57806380f55605146106035761035d565b8063521eb2731461051e57806354f1e126146105335780636595171c1461055357806368b84b721461057357806371210a0d146105935780637cf2ed22146105b35761035d565b8063248a9ca3116102d757806336568abe116102b157806336568abe146104b65780633f16431a146104d65780634ddf47d4146104f65780635153786b146105095761035d565b8063248a9ca31461045457806325be124e146104745780632f2ff15d146104965761035d565b8063099db017146103625780630cae451014610398578063113b0ab2146103c5578063150b7a02146103e557806317d70f7c146104125780631b1bb5cb146104345761035d565b3661035d57336001600160a01b037f000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f9161461035b5761035b610956565b005b600080fd5b34801561036e57600080fd5b5061038261037d366004612d71565b610abd565b60405161038f91906131f4565b60405180910390f35b3480156103a457600080fd5b506103b86103b3366004612da9565b610add565b60405161038f919061321d565b3480156103d157600080fd5b506103826103e0366004612d71565b610b18565b3480156103f157600080fd5b50610405610400366004612e2b565b610b32565b60405161038f9190613208565b34801561041e57600080fd5b50610427610b42565b60405161038f91906131ff565b34801561044057600080fd5b5061042761044f366004613003565b610b48565b34801561046057600080fd5b5061042761046f366004612f13565b611249565b34801561048057600080fd5b5061048961125e565b60405161038f919061311e565b3480156104a257600080fd5b5061035b6104b1366004612f2b565b61126d565b3480156104c257600080fd5b5061035b6104d1366004612f2b565b6112b5565b3480156104e257600080fd5b5061035b6104f1366004612d71565b6112f7565b61035b610504366004612f7b565b6112b1565b34801561051557600080fd5b50610427611312565b34801561052a57600080fd5b50610489611398565b34801561053f57600080fd5b5061035b61054e366004612d71565b6113a7565b34801561055f57600080fd5b5061035b61056e366004612d71565b6113bf565b34801561057f57600080fd5b5061038261058e366004612f13565b6113ca565b34801561059f57600080fd5b506103826105ae366004612f13565b6113e4565b3480156105bf57600080fd5b5061035b6113fe565b3480156105d457600080fd5b506105dd6114ed565b60405161038f959493929190613b78565b3480156105fa57600080fd5b50610427611535565b34801561060f57600080fd5b50610489611567565b34801561062457600080fd5b50610427611576565b34801561063957600080fd5b5061042761157b565b34801561064e57600080fd5b5061035b61065d366004612d71565b61158d565b34801561066e57600080fd5b5061035b6115a5565b34801561068357600080fd5b50610489610692366004612f5a565b6117a3565b3480156106a357600080fd5b506103826106b2366004612f2b565b6117c2565b3480156106c357600080fd5b5061035b6106d2366004612d71565b6117da565b3480156106e357600080fd5b506104276117f2565b3480156106f857600080fd5b5061035b610707366004612d71565b6117f7565b34801561071857600080fd5b5061048961180f565b34801561072d57600080fd5b5061035b61073c366004612d71565b61181e565b34801561074d57600080fd5b5061038261075c366004612d71565b611836565b34801561076d57600080fd5b50610489611842565b34801561078257600080fd5b5061035b611851565b34801561079757600080fd5b506104896118e0565b3480156107ac57600080fd5b506104276107bb366004612f13565b6118ef565b3480156107cc57600080fd5b50610489611906565b3480156107e157600080fd5b50610427611915565b3480156107f657600080fd5b5061035b610805366004612f2b565b611927565b34801561081657600080fd5b5061081f611961565b60405161038f929190613b6a565b34801561083957600080fd5b5061035b610848366004612d71565b611b03565b34801561085957600080fd5b50610862611b0e565b60405161038f9190613b5a565b34801561087b57600080fd5b5061035b61088a366004612d71565b611b20565b34801561089b57600080fd5b5061035b6108aa366004612d71565b611bb5565b3480156108bb57600080fd5b50610489611c18565b3480156108d057600080fd5b5061035b6108df366004612da9565b611c27565b3480156108f057600080fd5b5061042761233a565b61035b610956565b34801561090d57600080fd5b5061035b61091c366004612f7b565b61234c565b34801561092d57600080fd5b5061038261093c366004612d71565b612388565b34801561094d57600080fd5b506103826123a2565b6003547f000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f96001600160a01b03908116911614806109c157506004547f000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f96001600160a01b039081169116145b6109e65760405162461bcd60e51b81526004016109dd9061387c565b60405180910390fd5b600b54600160701b900460ff1615610a105760405162461bcd60e51b81526004016109dd906138b3565b600b54600160781b90046001600160801b031615610a405760405162461bcd60e51b81526004016109dd906134e8565b3415610abb577f000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f96001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610aa157600080fd5b505af1158015610ab5573d6000803e3d6000fd5b50505050505b565b6000610ad7600080516020613c51833981519152836117c2565b92915050565b606087878787878787604051602001610afc9796959493929190613170565b6040516020818303038152906040529050979650505050505050565b6000610ad7600080516020613c11833981519152836117c2565b630a85bd0160e11b949350505050565b60055481565b6000600280541415610b6c5760405162461bcd60e51b81526004016109dd9061392e565b60028055610b7933611836565b80610b885750610b8833612388565b610ba45760405162461bcd60e51b81526004016109dd906137a0565b610bac6123a2565b610bc85760405162461bcd60e51b81526004016109dd906136d5565b600b54600160701b900460ff1615610bf25760405162461bcd60e51b81526004016109dd9061340e565b600a60009054906101000a90046001600160a01b03166001600160a01b031663b3f05b976040518163ffffffff1660e01b815260040160206040518083038186803b158015610c4057600080fd5b505afa158015610c54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c789190612ef3565b610ce557600a60009054906101000a90046001600160a01b03166001600160a01b0316634bb278f36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ccc57600080fd5b505af1158015610ce0573d6000803e3d6000fd5b505050505b600b805460ff60701b1916600160701b179055600a54604080516336d0054b60e01b815290516001600160a01b03909216916336d0054b91600480820192602092909190829003018186803b158015610d3d57600080fd5b505afa158015610d51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d759190612ef3565b610d815750600061123d565b478015610dfd577f000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f96001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b158015610de357600080fd5b505af1158015610df7573d6000803e3d6000fd5b50505050505b600080610e08611961565b915091508160001480610e19575080155b15610e2a576000935050505061123d565b60075460035460048054604051630b4c774160e11b81526000946001600160a01b0390811694631698ee8294610e76949183169392821692600160a01b90920462ffffff1691016131b3565b60206040518083038186803b158015610e8e57600080fd5b505afa158015610ea2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec69190612d8d565b90506001600160a01b0381161580610f555750806001600160a01b0316631a6865026040518163ffffffff1660e01b815260040160206040518083038186803b158015610f1257600080fd5b505afa158015610f26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f4a9190612fe8565b6001600160801b0316155b610f715760405162461bcd60e51b81526004016109dd90613674565b6001600160a01b038116610f8857610f8888612443565b600354600654610fa5916001600160a01b0390811691168561253a565b600454600654610fc2916001600160a01b0390811691168461253a565b610fca612cfe565b600454600354611091916001600160a01b03908116911610610ff7576004546001600160a01b0316611004565b6003546001600160a01b03165b6004546003546001600160a01b0391821691161061102d576003546001600160a01b031661103a565b6004546001600160a01b03165b60045460035462ffffff600160a01b830416916001600160a01b039081169116106110655786611067565b875b6004546003546001600160a01b039182169116106110855788611087565b875b8d8d600019612621565b600654604051634418b22b60e11b81529192506001600160a01b0316906388316456906110c2908490600401613aac565b608060405180830381600087803b1580156110dc57600080fd5b505af11580156110f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111149190613088565b50506005919091556001600160801b031695506298968086116111495760405162461bcd60e51b81526004016109dd9061337c565b600b5461116f9061116a90600160781b90046001600160801b0316886126d2565b6126f5565b600b80546001600160801b0392909216600160781b026fffffffffffffffffffffffffffffffff60781b19909216919091179081905567ffffffffffffffff6401000000009091041661120057600b546111d09063ffffffff164201612722565b600b805467ffffffffffffffff92909216640100000000026bffffffffffffffff00000000199092169190911790555b7ffdb748c915e4e67b4bb23287bf4295a4595ce48b50343214369f72ccfb974cfa8660405161122f91906131ff565b60405180910390a150505050505b60016002559392505050565b60009081526020819052604090206002015490565b6004546001600160a01b031681565b60008281526020819052604090206002015461128b906106b261274c565b6112a75760405162461bcd60e51b81526004016109dd906132f6565b6112b18282612750565b5050565b6112bd61274c565b6001600160a01b0316816001600160a01b0316146112ed5760405162461bcd60e51b81526004016109dd906139d3565b6112b182826127b9565b61130f600080516020613c118339815191528261126d565b50565b6003546040516370a0823160e01b81526000916001600160a01b0316906370a082319061134390309060040161311e565b60206040518083038186803b15801561135b57600080fd5b505afa15801561136f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113939190613070565b905090565b6009546001600160a01b031681565b61130f600080516020613c5183398151915282611927565b61130f60008261126d565b600454600090610ad7906001600160a01b03163384612822565b600354600090610ad7906001600160a01b03163384612822565b61140733611836565b80611416575061141633612388565b6114325760405162461bcd60e51b81526004016109dd906137a0565b600b54600160701b900460ff1661145b5760405162461bcd60e51b81526004016109dd906138b3565b600b54640100000000900467ffffffffffffffff1642101561148f5760405162461bcd60e51b81526004016109dd90613740565b6000611499611312565b905080156114be576003546009546114be916001600160a01b039081169116836128b4565b60006114c8611535565b905080156112b1576004546009546112b1916001600160a01b039081169116836128b4565b600b5463ffffffff811690640100000000810467ffffffffffffffff1690600160601b810461ffff1690600160701b810460ff1690600160781b90046001600160801b031685565b600480546040516370a0823160e01b81526000926001600160a01b03909216916370a08231916113439130910161311e565b600a546001600160a01b031681565b600381565b600080516020613c1183398151915281565b61130f600080516020613c1183398151915282611927565b6115ae33611836565b806115bd57506115bd33612388565b6115d95760405162461bcd60e51b81526004016109dd906137a0565b600060055411801561167357506006546005546040516331a9108f60e11b815230926001600160a01b031691636352211e9161161891906004016131ff565b60206040518083038186803b15801561163057600080fd5b505afa158015611644573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116689190612d8d565b6001600160a01b0316145b61168f5760405162461bcd60e51b81526004016109dd90613a22565b600b54600160701b900460ff166116b85760405162461bcd60e51b81526004016109dd906138b3565b600b54640100000000900467ffffffffffffffff164210156116ec5760405162461bcd60e51b81526004016109dd906133aa565b600654600954600554604051632142170760e11b81526001600160a01b03938416936342842e0e936117279330939290911691600401613132565b600060405180830381600087803b15801561174157600080fd5b505af1158015611755573d6000803e3d6000fd5b50506009546005546040516001600160a01b0390921693507f44614d032e1206ebcd60b94fac608a2020b2899d5314b9e5c3a40c3592cbd1869250611799916131ff565b60405180910390a2565b60008281526020819052604081206117bb908361299b565b9392505050565b60008281526020819052604081206117bb90836129a7565b61130f600080516020613c3183398151915282611927565b600081565b61130f600080516020613c518339815191528261126d565b6006546001600160a01b031681565b61130f600080516020613c318339815191528261126d565b6000610ad781836117c2565b6007546001600160a01b031681565b61185a33611836565b6118765760405162461bcd60e51b81526004016109dd906135a6565b600b54600160701b900460ff16156118a05760405162461bcd60e51b81526004016109dd906136ab565b600b805460ff60701b1916600160701b17905560405133907f8c549b74aaa0c6619e0bbf388c66d59e84e25a829b1849376129856c778c6b4290600090a2565b6008546001600160a01b031690565b6000818152602081905260408120610ad7906129bc565b6003546001600160a01b031681565b600080516020613c5183398151915281565b600082815260208190526040902060020154611945906106b261274c565b6112ed5760405162461bcd60e51b81526004016109dd90613556565b600b546000908190611994906127109061198e90600160601b900461ffff16611988611312565b906129c7565b906129fe565b915061199e611535565b90506000600a60009054906101000a90046001600160a01b03166001600160a01b0316637ff9b5966040518163ffffffff1660e01b815260040160206040518083038186803b1580156119f057600080fd5b505afa158015611a04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a289190613070565b90506000600460009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611a7a57600080fd5b505afa158015611a8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab291906130c4565b60ff1690506000611acb600a83900a61198e86866129c7565b90506000611ae18461198e88600a87900a6129c7565b905080851115611aef578094505b81861115611afb578195505b505050509091565b61130f600082611927565b600454600160a01b900462ffffff1681565b611b2933611836565b611b455760405162461bcd60e51b81526004016109dd906135a6565b6001600160a01b038116611b6b5760405162461bcd60e51b81526004016109dd90613345565b600980546001600160a01b0319166001600160a01b0383169081179091556040517f4edbfac5b40fe46ac1af1fd222b224b38cfeeb9e21bd4fc6344526c245f7549b90600090a250565b60015460ff1615611bd85760405162461bcd60e51b81526004016109dd90613250565b6001600160a01b038116611bfe5760405162461bcd60e51b81526004016109dd9061364b565b611c096000826112a7565b506001805460ff191681179055565b6008546001600160a01b031681565b60405163c395fcb360e01b81526001600160a01b0387169063c395fcb390611c5390339060040161311e565b60206040518083038186803b158015611c6b57600080fd5b505afa158015611c7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ca39190612ef3565b611cbf5760405162461bcd60e51b81526004016109dd90613775565b600081118015611cd357506402540be40081105b611cef5760405162461bcd60e51b81526004016109dd906135dd565b612710821115611d115760405162461bcd60e51b81526004016109dd9061380e565b60008211611d315760405162461bcd60e51b81526004016109dd906134b3565b6001600160a01b038416611d575760405162461bcd60e51b81526004016109dd9061399c565b6001600160a01b038316611d7d5760405162461bcd60e51b81526004016109dd90613345565b611d8684611bb5565b600a80546001600160a01b0319166001600160a01b03888116919091179182905560408051633d15cc6d60e01b815290519290911691633d15cc6d91600480820192602092909190829003018186803b158015611de257600080fd5b505afa158015611df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1a9190612d8d565b600380546001600160a01b0319166001600160a01b03928316179055600a54604080516304cfed9960e51b8152905191909216916399fdb320916004808301926020929190829003018186803b158015611e7357600080fd5b505afa158015611e87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eab9190612d8d565b600480546001600160a01b0319166001600160a01b03928316178155600a546040805163ddca3f4360e01b81529051919093169263ddca3f439281810192602092909190829003018186803b158015611f0357600080fd5b505afa158015611f17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f3b919061304d565b6004805462ffffff92909216600160a01b0262ffffff60a01b199092169190911790556003546001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611fc157600380546001600160a01b0319167f000000000000000000000000ae83571000af4499798d1e3b0fa0070eb3a3e3f96001600160a01b03161790555b6003546040805163313ce56760e01b815290516000926001600160a01b03169163313ce567916004808301926020929190829003018186803b15801561200657600080fd5b505afa15801561201a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061203e91906130c4565b60ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561209357600080fd5b505afa1580156120a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120cb91906130c4565b60ff169050818110156120f05760405162461bcd60e51b81526004016109dd90613a47565b600680546001600160a01b03808c166001600160a01b031992831617909255600780548a84169216919091179081905560035460048054604051630b4c774160e11b815293851694631698ee829461215d948216939183169262ffffff600160a01b9091041691016131b3565b60206040518083038186803b15801561217557600080fd5b505afa158015612189573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ad9190612d8d565b600880546001600160a01b03199081166001600160a01b0393841617909155600980549091169187169190911790556121e584612a30565b600b805461ffff92909216600160601b0261ffff60601b1990921691909117905561220f83612a54565b600b60000160006101000a81548163ffffffff021916908363ffffffff16021790555060006122c461271061198e87600a60009054906101000a90046001600160a01b03166001600160a01b031663f08b82e66040518163ffffffff1660e01b815260040160206040518083038186803b15801561228c57600080fd5b505afa1580156122a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119889190613070565b6004549091506122de906001600160a01b03163383612a7a565b6004546003546040516001600160a01b0392831692909116907f347f9def0950a93749244d7a0cfde1e52dcb48c303a538fda3ceb25c2576d65690612326908c908c90613156565b60405180910390a350505050505050505050565b600080516020613c3183398151915281565b6000808080808080612360888a018a612da9565b965096509650965096509650965061237d87878787878787611c27565b505050505050505050565b6000610ad7600080516020613c31833981519152836117c2565b6000306001600160a01b0316600a60009054906101000a90046001600160a01b03166001600160a01b031663521eb2736040518163ffffffff1660e01b815260040160206040518083038186803b1580156123fc57600080fd5b505afa158015612410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124349190612d8d565b6001600160a01b031614905090565b6007546003546004805460405163a167129560e01b81526001600160a01b039485169463a16712959461248c949082169391821692600160a01b90920462ffffff1691016131b3565b602060405180830381600087803b1580156124a657600080fd5b505af11580156124ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124de9190612d8d565b600880546001600160a01b0319166001600160a01b03928316179081905560405163f637731d60e01b815291169063f637731d9061252090849060040161311e565b600060405180830381600087803b158015610aa157600080fd5b60006060846001600160a01b031663095ea7b385856040516024016125609291906131db565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516125999190613102565b6000604051808303816000865af19150503d80600081146125d6576040519150601f19603f3d011682016040523d82523d6000602084013e6125db565b606091505b50915091508180156126055750805115806126055750808060200190518101906126059190612ef3565b610ab55760405162461bcd60e51b81526004016109dd90613445565b612629612cfe565b62ffffff87166040820152600284810b810b606083015283810b900b6080820152610140810182905230610120820152600060e082018190526101008201526001600160a01b03888116908a1610156126a3576001600160a01b03808a1682528816602082015260a0810186905260c081018590526126c6565b6001600160a01b0380891682528916602082015260a0810185905260c081018690525b98975050505050505050565b81810181811015610ad75760405162461bcd60e51b81526004016109dd90613614565b60006001600160801b0382111561271e5760405162461bcd60e51b81526004016109dd9061351f565b5090565b600067ffffffffffffffff82111561271e5760405162461bcd60e51b81526004016109dd906137d7565b3390565b60008281526020819052604090206127689082612b63565b156112b15761277561274c565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526020819052604090206127d19082612b78565b156112b1576127de61274c565b6001600160a01b0316816001600160a01b0316837ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b60405160405180910390a45050565b600b54600090600160701b900460ff161561284f5760405162461bcd60e51b81526004016109dd906138b3565b600b54600160781b90046001600160801b03161561287f5760405162461bcd60e51b81526004016109dd906134e8565b6000821161289f5760405162461bcd60e51b81526004016109dd9061347c565b6128aa848484612a7a565b5060019392505050565b60006060846001600160a01b031663a9059cbb85856040516024016128da9291906131db565b6040516020818303038152906040529060e01b6020820180516001600160e01b0383818316178352505050506040516129139190613102565b6000604051808303816000865af19150503d8060008114612950576040519150601f19603f3d011682016040523d82523d6000602084013e612955565b606091505b509150915081801561297f57508051158061297f57508080602001905181019061297f9190612ef3565b610ab55760405162461bcd60e51b81526004016109dd906132bf565b60006117bb8383612b8d565b60006117bb836001600160a01b038416612bd2565b6000610ad782612bea565b60008115806129e2575050808202828282816129df57fe5b04145b610ad75760405162461bcd60e51b81526004016109dd90613965565b6000808211612a1f5760405162461bcd60e51b81526004016109dd90613a7e565b818381612a2857fe5b049392505050565b600061ffff82111561271e5760405162461bcd60e51b81526004016109dd906133d7565b600063ffffffff82111561271e5760405162461bcd60e51b81526004016109dd90613845565b60006060846001600160a01b03166323b872dd853086604051602401612aa293929190613132565b6040516020818303038152906040529060e01b6020820180516001600160e01b038381831617835250505050604051612adb9190613102565b6000604051808303816000865af19150503d8060008114612b18576040519150601f19603f3d011682016040523d82523d6000602084013e612b1d565b606091505b5091509150818015612b47575080511580612b47575080806020019051810190612b479190612ef3565b610ab55760405162461bcd60e51b81526004016109dd906138ea565b60006117bb836001600160a01b038416612bee565b60006117bb836001600160a01b038416612c38565b81546000908210612bb05760405162461bcd60e51b81526004016109dd9061327d565b826000018281548110612bbf57fe5b9060005260206000200154905092915050565b60009081526001919091016020526040902054151590565b5490565b6000612bfa8383612bd2565b612c3057508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610ad7565b506000610ad7565b60008181526001830160205260408120548015612cf45783546000198083019190810190600090879083908110612c6b57fe5b9060005260206000200154905080876000018481548110612c8857fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080612cb857fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610ad7565b6000915050610ad7565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905261014081019190915290565b80516001600160801b0381168114610ad757600080fd5b600060208284031215612d82578081fd5b81356117bb81613bec565b600060208284031215612d9e578081fd5b81516117bb81613bec565b600080600080600080600060e0888a031215612dc3578283fd5b8735612dce81613bec565b96506020880135612dde81613bec565b95506040880135612dee81613bec565b94506060880135612dfe81613bec565b93506080880135612e0e81613bec565b9699959850939692959460a0840135945060c09093013592915050565b60008060008060808587031215612e40578384fd5b8435612e4b81613bec565b9350602085810135612e5c81613bec565b935060408601359250606086013567ffffffffffffffff80821115612e7f578384fd5b818801915088601f830112612e92578384fd5b813581811115612ea0578485fd5b604051601f8201601f1916810185018381118282101715612ebf578687fd5b60405281815283820185018b1015612ed5578586fd5b81858501868301379081019093019390935250939692955090935050565b600060208284031215612f04578081fd5b815180151581146117bb578182fd5b600060208284031215612f24578081fd5b5035919050565b60008060408385031215612f3d578182fd5b823591506020830135612f4f81613bec565b809150509250929050565b60008060408385031215612f6c578182fd5b50508035926020909101359150565b60008060208385031215612f8d578182fd5b823567ffffffffffffffff80821115612fa4578384fd5b818501915085601f830112612fb7578384fd5b813581811115612fc5578485fd5b866020828501011115612fd6578485fd5b60209290920196919550909350505050565b600060208284031215612ff9578081fd5b6117bb8383612d5a565b600080600060608486031215613017578081fd5b833561302281613bec565b9250602084013561303281613c01565b9150604084013561304281613c01565b809150509250925092565b60006020828403121561305e578081fd5b815162ffffff811681146117bb578182fd5b600060208284031215613081578081fd5b5051919050565b6000806000806080858703121561309d578182fd5b845193506130ae8660208701612d5a565b6040860151606090960151949790965092505050565b6000602082840312156130d5578081fd5b815160ff811681146117bb578182fd5b6001600160a01b03169052565b60020b9052565b62ffffff169052565b60008251613114818460208701613bbc565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039788168152958716602087015293861660408601529185166060850152909316608083015260a082019290925260c081019190915260e00190565b6001600160a01b03938416815291909216602082015262ffffff909116604082015260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b6001600160e01b031991909116815260200190565b600060208252825180602084015261323c816040850160208701613bbc565b601f01601f19169190910160400192915050565b602080825260139082015272105b1c9958591e481a5b9a5d1a585b1a5cd959606a1b604082015260600190565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252601f908201527f5472616e7366657248656c7065723a205452414e534645525f4641494c454400604082015260600190565b6020808252602f908201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60408201526e0818591b5a5b881d1bc819dc985b9d608a1b606082015260800190565b6020808252601a908201527f57616c6c657420697320746865207a65726f2061646472657373000000000000604082015260600190565b6020808252601490820152736c697175696469747920746f6f20736d616c6c2160601b604082015260600190565b602080825260139082015272131a5c5d5a591a5d1e481a5cc81b1bd8dad959606a1b604082015260600190565b6020808252601b908201527f426f72696e674d6174683a2075696e743136204f766572666c6f770000000000604082015260600190565b6020808252601a908201527f41756374696f6e206d75737420626520756e6c61756e63686564000000000000604082015260600190565b6020808252601e908201527f5472616e7366657248656c7065723a20415050524f56455f4641494c45440000604082015260600190565b6020808252601d908201527f416d6f756e74206d7573742062652067726561746572207468616e2030000000604082015260600190565b6020808252818101527f4c69717569646974792070657263656e7461676520657175616c73207a65726f604082015260600190565b60208082526017908201527f4c697175696469747920616c7265616479206164646564000000000000000000604082015260600190565b6020808252601c908201527f426f72696e674d6174683a2075696e74313238204f766572666c6f7700000000604082015260600190565b60208082526030908201527f416363657373436f6e74726f6c3a2073656e646572206d75737420626520616e60408201526f2061646d696e20746f207265766f6b6560801b606082015260800190565b60208082526017908201527f73656e646572206d75737420626520616e2061646d696e000000000000000000604082015260600190565b6020808252601b908201527f496e207365636f6e64732c206e6f74206d696c697365636f6e64730000000000604082015260600190565b60208082526018908201527f426f72696e674d6174683a20416464204f766572666c6f770000000000000000604082015260600190565b6020808252600f908201526e125b98dbdc9c9958dd081a5b9c1d5d608a1b604082015260600190565b6020808252601b908201527f506f73744c69717569646974793a2050616972206e6f74206e65770000000000604082015260600190565b60208082526010908201526f185b1c9958591e481b185d5b98da195960821b604082015260600190565b60208082526045908201527f41756374696f6e206d75737420686176652074686973206c61756e636865722060408201527f6164647265737320736574206173207468652064657374696e6174696f6e2077606082015264185b1b195d60da1b608082015260a00190565b6020808252818101527f506f737441756374696f6e3a204c6971756964697479206973206c6f636b6564604082015260600190565b60208082526011908201527027b7363c9036b0b935b2ba1030b236b4b760791b604082015260600190565b60208082526017908201527f53656e646572206d757374206265206f70657261746f72000000000000000000604082015260600190565b6020808252601b908201527f426f72696e674d6174683a2075696e743634204f766572666c6f770000000000604082015260600190565b6020808252601d908201527f47726561746572207468616e203130302e30302520283e313030303029000000604082015260600190565b6020808252601b908201527f426f72696e674d6174683a2075696e743332204f766572666c6f770000000000604082015260600190565b6020808252601a908201527f4c61756e63686572206e6f7420616363657074696e6720455448000000000000604082015260600190565b6020808252601b908201527f4d757374206669727374206c61756e6368206c69717569646974790000000000604082015260600190565b60208082526024908201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416040820152631253115160e21b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526018908201527f426f72696e674d6174683a204d756c204f766572666c6f770000000000000000604082015260600190565b60208082526019908201527f41646d696e20697320746865207a65726f206164647265737300000000000000604082015260600190565b6020808252602f908201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560408201526e103937b632b9903337b91039b2b63360891b606082015260800190565b6020808252600b908201526a125b9d985b1a59081b999d60aa1b604082015260600190565b6020808252601e908201527f6432206d7573742067726561746572206f7220657175616c20746f2064310000604082015260600190565b602080825260149082015273426f72696e674d6174683a20446976207a65726f60601b604082015260600190565b600061016082019050613ac08284516130e5565b6020830151613ad260208401826130e5565b506040830151613ae560408401826130f9565b506060830151613af860608401826130f2565b506080830151613b0b60808401826130f2565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151613b49828501826130e5565b505061014092830151919092015290565b62ffffff91909116815260200190565b918252602082015260400190565b63ffffffff95909516855267ffffffffffffffff93909316602085015261ffff919091166040840152151560608301526001600160801b0316608082015260a00190565b60005b83811015613bd7578181015183820152602001613bbf565b83811115613be6576000848401525b50505050565b6001600160a01b038116811461130f57600080fd5b8060020b811461130f57600080fdfe9d49f397ae9ef1a834b569acb967799a367061e305932181a44f5773da873bfd97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b9299f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6a26469706673582212200d81c91f254cb4c478781f9fe2d5a00f71f41cd170e331a71921e9a5cd0f4d7564736f6c634300060c0033