ETH Price: $3,021.22 (+3.20%)

Contract

0x0337d36A3dF76d882369E3cBF984a2EA40f6636F
 

Overview

ETH Balance

0.0189 ETH

ETH Value

$57.10 (@ $3,021.22/ETH)

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Claim Order102746452024-10-19 17:31:45465 days ago1729359105IN
0x0337d36A...A40f6636F
0.009 ETH0.000000370.00397403
Claim Order102746422024-10-19 17:31:39465 days ago1729359099IN
0x0337d36A...A40f6636F
0.009 ETH0.000000370.00397686
Cancel Order102746342024-10-19 17:31:23465 days ago1729359083IN
0x0337d36A...A40f6636F
0 ETH0.000001150.00401313
Cancel Order102746312024-10-19 17:31:17465 days ago1729359077IN
0x0337d36A...A40f6636F
0 ETH0.000001180.00401891
Cancel Order74205252024-08-14 15:54:25531 days ago1723650865IN
0x0337d36A...A40f6636F
0 ETH0.000000110.00271592
Cancel Order74205242024-08-14 15:54:23531 days ago1723650863IN
0x0337d36A...A40f6636F
0 ETH0.000000830.00273232
Claim Order71290682024-08-07 21:59:11538 days ago1723067951IN
0x0337d36A...A40f6636F
0.0003 ETH0.000000110.00195327
Claim Order54708002024-06-30 12:43:35576 days ago1719751415IN
0x0337d36A...A40f6636F
0.0003 ETH0.000000860.01005539
Cancel Order53972822024-06-28 19:52:59578 days ago1719604379IN
0x0337d36A...A40f6636F
0 ETH0.000003170.010782
New Order53781422024-06-28 9:14:59578 days ago1719566099IN
0x0337d36A...A40f6636F
0 ETH0.00000740.01090892
New Order53780952024-06-28 9:13:25578 days ago1719566005IN
0x0337d36A...A40f6636F
0 ETH0.000006980.01068208
New Order52830872024-06-26 4:26:29580 days ago1719375989IN
0x0337d36A...A40f6636F
0 ETH0.000005480.00902739
New Order52830652024-06-26 4:25:45580 days ago1719375945IN
0x0337d36A...A40f6636F
0 ETH0.000005990.0091329
New Order52830552024-06-26 4:25:25580 days ago1719375925IN
0x0337d36A...A40f6636F
0 ETH0.000005590.00894053
Cancel Order52826402024-06-26 4:11:35580 days ago1719375095IN
0x0337d36A...A40f6636F
0 ETH0.000002520.00904684
Cancel Order52826372024-06-26 4:11:29580 days ago1719375089IN
0x0337d36A...A40f6636F
0 ETH0.000002580.00907741
Cancel Order52826352024-06-26 4:11:25580 days ago1719375085IN
0x0337d36A...A40f6636F
0 ETH0.000002550.0090935
New Order52817942024-06-26 3:43:23580 days ago1719373403IN
0x0337d36A...A40f6636F
0 ETH0.000006010.00897599
New Order52817762024-06-26 3:42:47580 days ago1719373367IN
0x0337d36A...A40f6636F
0 ETH0.000006190.00915057
New Order52817572024-06-26 3:42:09580 days ago1719373329IN
0x0337d36A...A40f6636F
0 ETH0.000005640.00901227
Setup Limit Orde...52816552024-06-26 3:38:45580 days ago1719373125IN
0x0337d36A...A40f6636F
0 ETH0.000000720.00913803
Setup Limit Orde...52816502024-06-26 3:38:35580 days ago1719373115IN
0x0337d36A...A40f6636F
0 ETH0.000000720.00915653
Setup Limit Orde...52816452024-06-26 3:38:25580 days ago1719373105IN
0x0337d36A...A40f6636F
0 ETH0.000000730.00920615
Setup Limit Orde...52816402024-06-26 3:38:15580 days ago1719373095IN
0x0337d36A...A40f6636F
0 ETH0.000000730.00924272
Setup Limit Orde...52816352024-06-26 3:38:05580 days ago1719373085IN
0x0337d36A...A40f6636F
0 ETH0.000000730.00929098
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LimitOrderRegistry

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
File 1 of 16 : LimitOrderRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

// Used to interact with ERC20 tokens
import { ERC20 } from "@solmate/tokens/ERC20.sol";
// Used to perform the safeTransfer
import { SafeTransferLib } from "@solmate/utils/SafeTransferLib.sol";
// Used for chainlink automation
import { AutomationCompatibleInterface } from "@chainlink/contracts/src/v0.8/interfaces/AutomationCompatibleInterface.sol";
// used to have an owner for our contract
import { Owned } from "@solmate/auth/Owned.sol";
// for interacting with uniswap pools,
import { UniswapV3Pool } from "src/interfaces/uniswapV3/UniswapV3Pool.sol";
// for creating uniswap NFT positions
import { NonFungiblePositionManager } from "src/interfaces/uniswapV3/NonFungiblePositionManager.sol";
// dealing with NFTs
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
// used to maintain upkeep
import { LinkTokenInterface } from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol";
// keeper for chainlink automation
import { IKeeperRegistrar, RegistrationParams } from "src/interfaces/chainlink/IKeeperRegistrar.sol";
// used to define _msgSender() for future metatransaction support
import { Context } from "@openzeppelin/contracts/utils/Context.sol";
// used to get the gas price from the chainlink gas oracle
import { IChainlinkAggregator } from "src/interfaces/chainlink/IChainlinkAggregator.sol";

/**
 * @title Limit Order Registry
 * @notice Allows users to create decentralized limit orders.
 * @dev DO NOT PLACE LIMIT ORDERS FOR STRONGLY CORRELATED ASSETS.
 *      - If a stable coin pair were to temporarily depeg, and a user places a limit order
 *        whose tick range encompasses the normal trading tick, there is NO way to cancel the order
 *        because the order is mixed. The user would have to wait for another depeg event to happen
 *        so that the order can be fulfilled, or the order can be cancelled.
 * @author crispymangoes
 */
contract LimitOrderRegistry is Owned, AutomationCompatibleInterface, ERC721Holder, Context {
    using SafeTransferLib for ERC20;
    using SafeTransferLib for address;

    /*//////////////////////////////////////////////////////////////
                             STRUCTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Stores linked list center values, and frequently used pool values.
     * @param centerHead Linked list center value closer to head of the list
     * @param centerTail Linked list center value closer to tail of the list
     * @param token0 ERC20 token0 of the pool
     * @param token1 ERC20 token1 of the pool
     * @param fee Uniswap V3 pool fee
     */
    struct PoolData {
        uint256 centerHead;
        uint256 centerTail;
        ERC20 token0;
        ERC20 token1;
        uint24 fee;
    }

    /**
     * @notice Stores information about batches of orders.
     * @dev User orders can be batched together if they share the same target price.
     * @param direction Determines what direction the tick must move in order for the order to be filled
     *        - true, pool tick must INCREASE to fill this order
     *        - false, pool tick must DECREASE to fill this order
     * @param tickUpper The upper tick of the underlying LP position
     * @param tickLower The lower tick of the underlying LP position
     * @param userCount The number of users in this batch order
     * @param batchId Unique id used to distinguish this batch order from another batch order in the past that used the same LP position
     * @param token0Amount The amount of token0 in this order
     * @param token1Amount The amount of token1 in this order
     * @param head The next node in the linked list when moving toward the head
     * @param tail The next node in the linked list when moving toward the tail
     */
    struct BatchOrder {
        bool direction;
        int24 tickUpper;
        int24 tickLower;
        uint64 userCount;
        uint128 batchId;
        uint128 token0Amount;
        uint128 token1Amount;
        uint256 head;
        uint256 tail;
    }

    /**
     * @notice Stores information needed for users to make claims.
     * @param pool The Uniswap V3 pool the batch order was in
     * @param token0Amount The amount of token0 in the order
     * @param token1Amount The amount of token1 in the order
     * @param feePerUser The native token fee that must be paid on order claiming
     * @param direction The underlying order direction, used to determine input/output token of the order
     * @param isReadyForClaim Explicit bool indicating whether or not this order is ready to be claimed
     */
    struct Claim {
        UniswapV3Pool pool;
        uint128 token0Amount; //Can either be the deposit amount or the amount got out of liquidity changing to the other token
        uint128 token1Amount;
        uint128 feePerUser; // Fee in terms of network native asset.
        bool direction; //Determines the token out
        bool isReadyForClaim;
    }

    /**
     * @notice Struct used to store variables needed during order creation.
     * @param tick The target tick of the order
     * @param upper The upper tick of the underlying LP position
     * @param lower The lower tick of the underlying LP position
     * @param userTotal The total amount of assets the user has in the order
     * @param positionId The underling LP position token id this order is adding liquidity to
     * @param amount0 Can be the amount of assets user added to the order, based off orders direction
     * @param amount1 Can be the amount of assets user added to the order, based off orders direction
     */
    struct OrderDetails {
        int24 tick;
        int24 upper;
        int24 lower;
        uint128 userTotal;
        uint256 positionId;
        uint128 amount0;
        uint128 amount1;
    }

    /*//////////////////////////////////////////////////////////////
                             GLOBAL STATE
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Stores swap fees earned from limit order where the input token earns swap fees.
     */
    mapping(address => uint256) public tokenToSwapFees;

    /**
     * @notice Used to store claim information needed when users are claiming their orders.
     */
    mapping(uint128 => Claim) public claim;

    /**
     * @notice Stores the pools center head/tail, as well as frequently read values.
     */
    mapping(UniswapV3Pool => PoolData) public poolToData;

    /**
     * @notice Maps tick ranges to LP positions owned by this contract.
     * @dev  maps pool -> direction -> lower -> upper -> positionId
     */
    mapping(UniswapV3Pool => mapping(bool => mapping(int24 => mapping(int24 => uint256)))) public getPositionFromTicks;

    /**
     * @notice The minimum amount of assets required to create a `newOrder`.
     * @dev Changeable by owner.
     */
    mapping(ERC20 => uint256) public minimumAssets;

    /**
     * @notice Approximated amount of gas needed to fulfill 1 BatchOrder.
     * @dev Changeable by owner.
     */
    uint32 public upkeepGasLimit = 300_000;

    /**
     * @notice Approximated gas price used to fulfill orders.
     * @dev Changeable by owner.
     */
    uint32 public upkeepGasPrice = 30;

    /**
     * @notice Max number of orders that can be filled in 1 upkeep call.
     * @dev Changeable by owner.
     */
    uint16 public maxFillsPerUpkeep = 10;

    /**
     * @notice Value is incremented whenever a new BatchOrder is added to the `orderBook`.
     * @dev Zero is reserved.
     */
    uint128 public batchCount = 1;

    /**
     * @notice Mapping is used to store user deposit amounts in each BatchOrder.
     */
    mapping(uint128 => mapping(address => uint128)) public batchIdToUserDepositAmount;

    /**
     * @notice The `orderBook` maps Uniswap V3 token ids to BatchOrder information.
     * @dev Each BatchOrder contains a head and tail value which effectively,
     *      which means BatchOrders are connected using a doubly linked list.
     */
    mapping(uint256 => BatchOrder) public orderBook;

    /**
     * @notice Chainlink Automation Registrar contract.
     */
    IKeeperRegistrar public registrar;

    /**
     * @notice Whether or not the contract is shutdown in case of an emergency.
     */
    bool public isShutdown;

    /**
     * @notice Chainlink Fast Gas Feed.
     * @dev Feed for ETH Mainnet 0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C.
     */
    address public fastGasFeed;

    /**
     * @notice The max possible gas the owner can set for the gas limit.
     */
    uint32 public constant MAX_GAS_LIMIT = 750_000;

    /**
     * @notice The max possible gas price the owner can set for the gas price.
     * @dev In units of gwei.
     */
    uint32 public constant MAX_GAS_PRICE = 1_000;

    /**
     * @notice The max number of orders that can be fulfilled in a single upkeep TX.
     */
    uint16 public constant MAX_FILLS_PER_UPKEEP = 20;

    /**
     * @notice The ETH Fast Gas Feed heartbeat.
     * @dev If answer is stale, owner set gas price is used.
     */
    uint256 public constant FAST_GAS_HEARTBEAT = 7200;

    /**
     * @notice Function signature used to create V1 Upkeep versions.
     */
    string private constant FUNC_SIGNATURE = "register(string,bytes,address,uint32,address,bytes,uint96,uint8,address)";

    /**
     * @notice Function selector used to create V1 Upkeep versions.
     */
    bytes4 private constant FUNC_SELECTOR =  bytes4(keccak256(bytes(FUNC_SIGNATURE)));

    /*//////////////////////////////////////////////////////////////
                                 MODIFIERS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Prevent a function from being called during a shutdown.
     */
    modifier whenNotShutdown() {
        if (isShutdown) revert LimitOrderRegistry__ContractShutdown();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    // On new limit order created
    event NewOrder(address user, address pool, uint128 amount, uint128 userTotal, BatchOrder affectedOrder);
    // On limit order claimed
    event ClaimOrder(address user, uint128 batchId, uint256 amount);
    // On limit order cancelled
    event CancelOrder(address user, uint128 amount0, uint128 amount1, BatchOrder affectedOrder);
    // On limit order filled
    event OrderFilled(uint256 batchId, address pool);
    // On change of shutdown condition
    event ShutdownChanged(bool isShutdown);
    // On setup of limit order for the pool
    event LimitOrderSetup(address pool);

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    // @notice When attempting to submit an order that is ITM
    error LimitOrderRegistry__OrderITM(int24 currentTick, int24 targetTick, bool direction);
    // @notice When attempting to setup a pool that is already setup
    error LimitOrderRegistry__PoolAlreadySetup(address pool);
    // @notice When pool is not setup and one tries to make an order
    error LimitOrderRegistry__PoolNotSetup(address pool);
    // @notice When an invalid target tick is provided for creating an order
    error LimitOrderRegistry__InvalidTargetTick(int24 targetTick, int24 tickSpacing);
    // @notice When the user for the batchid is not found
    error LimitOrderRegistry__UserNotFound(address user, uint256 batchId);
    // @notice When the position id is invalid
    error LimitOrderRegistry__InvalidPositionId();
    // @notice When there is no liquidity in the order
    error LimitOrderRegistry__NoLiquidityInOrder();
    // @notice When there are no orders to fulfill
    error LimitOrderRegistry__NoOrdersToFulfill();
    // @notice When the center is ITM
    error LimitOrderRegistry__CenterITM();
    // @notice When the order does not appear in the linked list
    error LimitOrderRegistry__OrderNotInList(uint256 tokenId);
    // @notice When the minimum for the asset is not set
    error LimitOrderRegistry__MinimumNotSet(address asset);
    // @notice When the minimum for the asset is not met
    error LimitOrderRegistry__MinimumNotMet(address asset, uint256 minimum, uint256 amount);
    // @notice When the tick range specified is incorrect
    error LimitOrderRegistry__InvalidTickRange(int24 upper, int24 lower);
    // @notice When there are no fees to withdraw
    error LimitOrderRegistry__ZeroFeesToWithdraw(address token);
    // @notice When there is no native balance to withdraw
    error LimitOrderRegistry__ZeroNativeBalance();
    // @notice When an invalid batch id is provided
    error LimitOrderRegistry__InvalidBatchId();
    // @notice When the order is not yet ready to claim
    error LimitOrderRegistry__OrderNotReadyToClaim(uint128 batchId);
    // @notice When the contract is shutdown and actions are performed
    error LimitOrderRegistry__ContractShutdown();
    // @notice When the contract is not shutdown and shutdown is attempted to be ended
    error LimitOrderRegistry__ContractNotShutdown();
    // @notice When an invalid gas limit, above the max, is provided
    error LimitOrderRegistry__InvalidGasLimit();
    // @notice When an invalid gas price, above the max, is provided
    error LimitOrderRegistry__InvalidGasPrice();
    // @notice When the fills for the upkeep are invalid
    error LimitOrderRegistry__InvalidFillsPerUpkeep();
    // @notice When amounts should be 0 but is not
    error LimitOrderRegistry__AmountShouldBeZero();
    // @notice When direction for the order doesn't match what state has.
    error LimitOrderRegistry__DirectionMisMatch();

    /*//////////////////////////////////////////////////////////////
                                 ENUMS
    //////////////////////////////////////////////////////////////*/

    // @notice  OrderStatus is used to show the status of an order
    enum OrderStatus {
        // When the order is through the book, it is considered ITM. this means that either the order was filled, or it should not exist.
        ITM,
        // When an order is not yet filled, it is considered OTM.
        OTM,
        // When the current tick is within the ticks of the limit order, it is considered MIXED.
        MIXED
    }

    /*//////////////////////////////////////////////////////////////
                              IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    // @notice the token addres of the wrapped native token
    ERC20 public immutable WRAPPED_NATIVE; // Mainnet 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

    // @notice the address of the position manager
    NonFungiblePositionManager public immutable POSITION_MANAGER; // Mainnet 0xC36442b4a4522E871399CD717aBDD847Ab11FE88

    // @notice the address of the link token
    LinkTokenInterface public immutable LINK; // Mainnet 0x514910771AF9Ca656af840dff83E8264EcF986CA

    constructor(
        address _owner,
        NonFungiblePositionManager _positionManager,
        ERC20 wrappedNative,
        LinkTokenInterface link,
        IKeeperRegistrar _registrar,
        address _fastGasFeed
    ) Owned(_owner) {
        POSITION_MANAGER = _positionManager;
        WRAPPED_NATIVE = wrappedNative;
        LINK = link;
        registrar = _registrar;
        fastGasFeed = _fastGasFeed;
    }

    /*//////////////////////////////////////////////////////////////
                              OWNER LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice No input validation is done because it is in the owners best interest to choose a valid registrar.
     */
    function setRegistrar(IKeeperRegistrar _registrar) external onlyOwner {
        registrar = _registrar;
    }

    /**
     * @notice Allows owner to set the fills per upkeep.
     */
    function setMaxFillsPerUpkeep(uint16 newVal) external onlyOwner {
        if (newVal == 0 || newVal > MAX_FILLS_PER_UPKEEP) revert LimitOrderRegistry__InvalidFillsPerUpkeep();
        maxFillsPerUpkeep = newVal;
    }

    /**
     * @notice Allows owner to setup a new limit order for a new pool.
     * @param pool The uniswap v3 pool to setup limit orders for
     * @param initialUpkeepFunds the amount of initial upkeep funds to provide for the pool
     * @dev New Limit orders, should have a keeper to fulfill orders.
     * @dev If `initialUpkeepFunds` is zero, upkeep creation is skipped.
     */
    function setupLimitOrder(UniswapV3Pool pool, uint256 initialUpkeepFunds) external onlyOwner {
        // Check if Limit Order is already setup for `pool`.
        if (address(poolToData[pool].token0) != address(0)) revert LimitOrderRegistry__PoolAlreadySetup(address(pool));

        // Create Upkeep, transfering funds only if initialUpkeepFunds is above 0.
        if (initialUpkeepFunds > 0) {
            // Owner wants to automatically create an upkeep for new pool.
            ERC20(address(LINK)).safeTransferFrom(owner, address(this), initialUpkeepFunds);
            // check the upkeep registration version
            if (bytes(registrar.typeAndVersion())[16] == bytes("1")[0]) {
                // Use V1 Upkeep Registration.
                bytes memory data = abi.encodeWithSelector(
                    FUNC_SELECTOR,
                    "Limit Order Registry",
                    abi.encode(0),
                    address(this),
                    uint32(maxFillsPerUpkeep * upkeepGasLimit),
                    owner,
                    abi.encode(pool),
                    uint96(initialUpkeepFunds),
                    77,
                    address(this)
                );
                LINK.transferAndCall(address(registrar), initialUpkeepFunds, data);
            } else {
                // Use V2 Upkeep Registration.
                ERC20(address(LINK)).safeApprove(address(registrar), initialUpkeepFunds);
                RegistrationParams memory params = RegistrationParams({
                    name: "Limit Order Registry",
                    encryptedEmail: abi.encode(0),
                    upkeepContract: address(this),
                    gasLimit: uint32(maxFillsPerUpkeep * upkeepGasLimit),
                    adminAddress: owner,
                    checkData: abi.encode(pool),
                    offchainConfig: abi.encode(0),
                    amount: uint96(initialUpkeepFunds)
                });
                registrar.registerUpkeep(params);
            }
        }

        // initialize the data for the pool. the center and tail of the linked list are 0, while the tokens and fees are set to the correct
        // ones for the pool
        poolToData[pool] = PoolData({
            centerHead: 0,
            centerTail: 0,
            token0: ERC20(pool.token0()),
            token1: ERC20(pool.token1()),
            fee: pool.fee()
        });

        emit LimitOrderSetup(address(pool));
    }

    /**
     * @notice Allows owner to set the minimum assets used to create `newOrder`s.
     * @param amount the amount to set minimum assets for the token to
     * @param asset the erc20 token address to set minimum assets for
     * @dev This value can be zero, but then this contract can be griefed by an attacker spamming low liquidity orders.
     */
    function setMinimumAssets(uint256 amount, ERC20 asset) external onlyOwner {
        minimumAssets[asset] = amount;
    }

    /**
     * @notice Allows owner to change the gas limit value used to determine the Native asset fee needed to claim orders.
     * @param gasLimit the gas limit that the upkeepGasLimit will be set to
     * @dev premium should be factored into this value.
     */
    function setUpkeepGasLimit(uint32 gasLimit) external onlyOwner {
        // should revert if the gas limit provided is greater than the provided max gas limit.
        if (gasLimit > MAX_GAS_LIMIT) revert LimitOrderRegistry__InvalidGasLimit();
        upkeepGasLimit = gasLimit;
    }

    /**
     * @notice Allows owner to change the gas price used to determine the Native asset fee needed to claim orders.
     * @param gasPrice the gas limit that the upkeepGasPrice will be set to
     * @dev `gasPrice` uses units of gwei.
     */
    function setUpkeepGasPrice(uint32 gasPrice) external onlyOwner {
        // should revert if the gas price provided is greater than the provided max gas price.
        if (gasPrice > MAX_GAS_PRICE) revert LimitOrderRegistry__InvalidGasPrice();
        upkeepGasPrice = gasPrice;
    }

    /**
     * @notice Allows owner to set the fast gas feed.
     * @param feed the address of the chainlink-compatible gas oracle
     * @dev The feed should be a chainlink-compatible gas oracle
     */
    function setFastGasFeed(address feed) external onlyOwner {
        fastGasFeed = feed;
    }

    /**
     * @notice Allows owner to withdraw swap fees earned from the input token of orders.
     * @param tokenFeeIsIn the address of the token fee.
     */
    function withdrawSwapFees(address tokenFeeIsIn) external onlyOwner {
        uint256 fee = tokenToSwapFees[tokenFeeIsIn];

        // Make sure there are actually fees to withdraw;
        if (fee == 0) revert LimitOrderRegistry__ZeroFeesToWithdraw(tokenFeeIsIn);

        // set fees to 0
        tokenToSwapFees[tokenFeeIsIn] = 0;
        // transfer fees to the user
        ERC20(tokenFeeIsIn).safeTransfer(owner, fee);
    }

    /**
     * @notice Allows owner to withdraw wrapped native and native assets from this contract.
     */
    function withdrawNative() external onlyOwner {
        uint256 wrappedNativeBalance = WRAPPED_NATIVE.balanceOf(address(this));
        uint256 nativeBalance = address(this).balance;
        // Make sure there is something to withdraw.
        if (wrappedNativeBalance == 0 && nativeBalance == 0) revert LimitOrderRegistry__ZeroNativeBalance();

        // transfer wrappedNativeBalance if it exists
        if (wrappedNativeBalance > 0) WRAPPED_NATIVE.safeTransfer(owner, wrappedNativeBalance);
        // transfer nativeBalance if it exists
        if (nativeBalance > 0) owner.safeTransferETH(nativeBalance);
    }

    /**
     * @notice Shutdown the registry. Used in an emergency or if the registry has been deprecated.
     */
    function initiateShutdown() external whenNotShutdown onlyOwner {
        isShutdown = true;

        emit ShutdownChanged(true);
    }

    /**
     * @notice Restart the registry.
     */
    function liftShutdown() external onlyOwner {
        if (!isShutdown) revert LimitOrderRegistry__ContractNotShutdown();
        isShutdown = false;

        emit ShutdownChanged(false);
    }

    /*//////////////////////////////////////////////////////////////
                        USER ORDER MANAGEMENT LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Creates a new limit order for a specific pool.
     * @dev Limit orders can be created to buy either token0, or token1 of the pool.
     * @param pool the Uniswap V3 pool to create a limit order on.
     * @param targetTick the tick, that when `pool`'s tick passes, the order will be completely fulfilled
     * @param amount the amount of the input token to sell for the desired token out
     * @param direction bool indicating what the desired token out is
     *                  - true  token in = token0 ; token out = token1
     *                  - false token in = token1 ; token out = token0
     * @param startingNode an NFT position id indicating where this contract should start searching for a spot in the list
     *                     - can be zero which defaults to starting the search at center of list
     * @dev reverts if
     *      - pool is not setup
     *      - targetTick is not divisible by the pools tick spacing
     *      - the new order would be ITM, or in a MIXED state
     *      - the new order does not meet minimum liquidity requirements
     *      - transferFrom fails

     * @dev Emits a `NewOrder` event which contains meta data about the order including the orders `batchId`(which is used for claiming/cancelling).
     */
    function newOrder(
        UniswapV3Pool pool,
        int24 targetTick,
        uint128 amount,
        bool direction,
        uint256 startingNode,
        uint256 deadline
    ) external whenNotShutdown returns (uint128) {
        if (address(poolToData[pool].token0) == address(0)) revert LimitOrderRegistry__PoolNotSetup(address(pool));

        address sender = _msgSender();

        // Transfer assets into contract before setting/checking any state.
        {
            // if direction is true, it means that assetIn is token0
            ERC20 assetIn = direction ? poolToData[pool].token0 : poolToData[pool].token1;
            // we make sure that there is enough amount of the assetIn
            _enforceMinimumLiquidity(amount, assetIn);
            // and transfer
            assetIn.safeTransferFrom(sender, address(this), amount);
        }

        OrderDetails memory details;

        // initialize the order =details tick with the pool slot0 value
        (, details.tick, , , , , ) = pool.slot0();

        // Determine upper and lower ticks.
        {
            // we need to grab the tickspace from the pool because we must send our order so that it aligns to ticks.
            int24 tickSpacing = pool.tickSpacing();
            // Make sure targetTick is divisible by spacing.
            if (targetTick % tickSpacing != 0) revert LimitOrderRegistry__InvalidTargetTick(targetTick, tickSpacing);
            if (direction) {
                // if assetIn is token0, then the limit goes from targetTick to targetTick-tickSpacing
                details.upper = targetTick;
                details.lower = targetTick - tickSpacing;
            } else {
                // if assetIn is token1, then the limit goes from targetTick to targetTick+tickSpacing
                details.upper = targetTick + tickSpacing;
                details.lower = targetTick;
            }
        }
        // Validate lower, upper,and direction.
        {
            OrderStatus status = _getOrderStatus(details.tick, details.lower, details.upper, direction);
            // forbid orders that are "ITM". basically, if your order is through the market, then instead of limit order, you should swap
            if (status != OrderStatus.OTM) revert LimitOrderRegistry__OrderITM(details.tick, targetTick, direction);
        }

        // Get the position id. this is the underlying position that we are managing
        details.positionId = getPositionFromTicks[pool][direction][details.lower][details.upper];

        // the amount should match with the tokenIn, so if tokenIn is token0, then set amount0, else set amount1
        if (direction) details.amount0 = amount;
        else details.amount1 = amount;

        // check if the position exists in the nft contract
        if (details.positionId == 0) {
            // Create new LP position(which adds liquidity)
            PoolData memory data = poolToData[pool];
            details.positionId = _mintPosition(
                data,
                details.upper,
                details.lower,
                details.amount0,
                details.amount1,
                direction,
                deadline
            );

            // Add it to the list.
            _addPositionToList(data, startingNode, targetTick, details.positionId, direction);

            // Set new orders upper and lower tick.
            orderBook[details.positionId].tickLower = details.lower;
            orderBook[details.positionId].tickUpper = details.upper;

            // Setup BatchOrder, setting batchId, direction.
            _setupOrder(direction, details.positionId);

            // Update token0Amount, token1Amount, batchIdToUserDepositAmount mapping.
            details.userTotal = _updateOrder(details.positionId, sender, amount);

            // Update the center values if need be.
            _updateCenter(pool, details.positionId, details.tick, details.upper, details.lower);

            // Update getPositionFromTicks since we have a new LP position.
            getPositionFromTicks[pool][direction][details.lower][details.upper] = details.positionId;
        } else {
            // Check if the position id is already being used in List.
            BatchOrder memory order = orderBook[details.positionId];
            if (order.token0Amount > 0 || order.token1Amount > 0) {
                // Check that supplied direction and order.direction are the same.
                if (direction != order.direction) revert LimitOrderRegistry__DirectionMisMatch();
                // Need to add liquidity.
                PoolData memory data = poolToData[pool];
                _addToPosition(data, details.positionId, details.amount0, details.amount1, direction, deadline);

                // Update token0Amount, token1Amount, batchIdToUserDepositAmount mapping.
                details.userTotal = _updateOrder(details.positionId, sender, amount);
            } else {
                // We already have an LP position with given tick ranges, but it is not in linked list.
                PoolData memory data = poolToData[pool];

                // Add it to the list.
                _addPositionToList(data, startingNode, targetTick, details.positionId, direction);

                // Setup BatchOrder, setting batchId, direction.
                _setupOrder(direction, details.positionId);

                // Need to add liquidity.
                _addToPosition(data, details.positionId, details.amount0, details.amount1, direction, deadline);

                // Update token0Amount, token1Amount, batchIdToUserDepositAmount mapping.
                details.userTotal = _updateOrder(details.positionId, sender, amount);

                // Update the center values if need be.
                _updateCenter(pool, details.positionId, details.tick, details.upper, details.lower);
            }
        }
        // emit the event
        emit NewOrder(sender, address(pool), amount, details.userTotal, orderBook[details.positionId]);
        // return the batch id of the position
        return orderBook[details.positionId].batchId;
    }

    /**
     * @notice Users can claim fulfilled orders by passing in the `batchId` corresponding to the order they want to claim.
     * @param batchId the batchId corresponding to a fulfilled order to claim
     * @param user the address of the user in the order to claim for
     * @return address erc20 address
     * @return uint256 amount claimed
     * @dev Caller must either approve this contract to spend their Wrapped Native token, and have at least `getFeePerUser` tokens in their wallet.
     *      Or caller must send `getFeePerUser` value with this call.
     */
    function claimOrder(uint128 batchId, address user) external payable returns (ERC20, uint256) {
        Claim storage userClaim = claim[batchId];
        if (!userClaim.isReadyForClaim) revert LimitOrderRegistry__OrderNotReadyToClaim(batchId);
        uint256 depositAmount = batchIdToUserDepositAmount[batchId][user];
        if (depositAmount == 0) revert LimitOrderRegistry__UserNotFound(user, batchId);

        // Zero out user balance.
        delete batchIdToUserDepositAmount[batchId][user];

        // Calculate owed amount.
        uint256 totalTokenDeposited;
        uint256 totalTokenOut;
        ERC20 tokenOut;

        // again, remembering that direction == true means that the input token is token0.
        if (userClaim.direction) {
            totalTokenDeposited = userClaim.token0Amount;
            totalTokenOut = userClaim.token1Amount;
            tokenOut = poolToData[userClaim.pool].token1;
        } else {
            totalTokenDeposited = userClaim.token1Amount;
            totalTokenOut = userClaim.token0Amount;
            tokenOut = poolToData[userClaim.pool].token0;
        }

        uint256 owed = (totalTokenOut * depositAmount) / totalTokenDeposited;

        // Transfer tokens owed to user.
        tokenOut.safeTransfer(user, owed);

        // Transfer fee in.
        address sender = _msgSender();
        if (msg.value >= userClaim.feePerUser) {
            // refund if necessary.
            uint256 refund = msg.value - userClaim.feePerUser;
            if (refund > 0) sender.safeTransferETH(refund);
        } else {
            WRAPPED_NATIVE.safeTransferFrom(sender, address(this), userClaim.feePerUser);
            // If value is non zero send it back to caller.
            if (msg.value > 0) sender.safeTransferETH(msg.value);
        }
        emit ClaimOrder(user, batchId, owed);
        return (tokenOut, owed);
    }

    /**
     * @notice Allows users to cancel orders as long as they are completely OTM.
     * @param pool the Uniswap V3 pool that contains the limit order to cancel
     * @param targetTick the targetTick of the order you want to cancel
     * @param direction bool indication the direction of the order
     * @return amount0 amount0 withdrawn
     * @return amount1 amount1 withdrawn
     * @return batchId batch id withdrawn
     */
    function cancelOrder(
        UniswapV3Pool pool,
        int24 targetTick,
        bool direction,
        uint256 deadline
    ) external returns (uint128 amount0, uint128 amount1, uint128 batchId) {
        // defined here since we want to grab it out of the closure
        uint256 positionId;
        // this is in a closure to avoid namespace pollusion
        {
            // Make sure order is OTM.
            (, int24 tick, , , , , ) = pool.slot0();

            // Determine upper and lower ticks.
            int24 upper;
            int24 lower;
            {
                int24 tickSpacing = pool.tickSpacing();
                // Make sure targetTick is divisible by spacing.
                if (targetTick % tickSpacing != 0) revert LimitOrderRegistry__InvalidTargetTick(targetTick, tickSpacing);

                if (direction) {
                    upper = targetTick;
                    lower = targetTick - tickSpacing;
                } else {
                    upper = targetTick + tickSpacing;
                    lower = targetTick;
                }
            }
            // Validate lower, upper,and direction. Make sure order is not ITM or MIXED
            {
                OrderStatus status = _getOrderStatus(tick, lower, upper, direction);
                if (status != OrderStatus.OTM) revert LimitOrderRegistry__OrderITM(tick, targetTick, direction);
            }

            // Get the position id.
            positionId = getPositionFromTicks[pool][direction][lower][upper];

            if (positionId == 0) revert LimitOrderRegistry__InvalidPositionId();
        }

        // Get the users deposit amount in the order.
        BatchOrder storage order = orderBook[positionId];
        // the batch id can't be zero
        if (order.batchId == 0) revert LimitOrderRegistry__InvalidBatchId();

        // this variable is set conditionally, so we declare it here first
        uint256 liquidityPercentToTake;

        // grab sender for shorthand.
        address sender = _msgSender();
        {
            // set the batchId return variable
            batchId = order.batchId;
            // grab the deposit amount from the mapping
            uint128 depositAmount = batchIdToUserDepositAmount[batchId][sender];
            // if there is no deposit, then there is nothing to do for this batch
            if (depositAmount == 0) revert LimitOrderRegistry__UserNotFound(sender, batchId);

            // Remove one from the userCount.
            order.userCount--;

            // Zero out user balance.
            delete batchIdToUserDepositAmount[batchId][sender];
            uint128 orderAmount;
            // remember, direction=true, tokenIn = token0
            if (order.direction) {
                orderAmount = order.token0Amount;
                if (orderAmount == depositAmount) {
                    liquidityPercentToTake = 1e18;
                    // Update order tokenAmount.
                    order.token0Amount = 0;
                } else {
                    liquidityPercentToTake = (1e18 * uint256(depositAmount)) / orderAmount;
                    // Update order tokenAmount.
                    order.token0Amount = orderAmount - depositAmount;
                }
            } else {
                orderAmount = order.token1Amount;
                if (orderAmount == depositAmount) {
                    liquidityPercentToTake = 1e18;
                    // Update order tokenAmount.
                    order.token1Amount = 0;
                } else {
                    liquidityPercentToTake = (1e18 * uint256(depositAmount)) / orderAmount;
                    // Update order tokenAmount.
                    order.token1Amount = orderAmount - depositAmount;
                }
            }

            // actual movement of money is here
            (amount0, amount1) = _takeFromPosition(positionId, pool, liquidityPercentToTake, deadline);
            // emit event
            emit CancelOrder(sender, amount0, amount1, order);

            // special case for percent is 100%
            if (liquidityPercentToTake == 1e18) {
                _removeOrderFromList(positionId, pool, order);
                // Zero out balances for cancelled order.
                order.token0Amount = 0;
                order.token1Amount = 0;
                order.batchId = 0;
            }
        }
        if (order.direction) {
            if (amount0 > 0) poolToData[pool].token0.safeTransfer(sender, amount0);
            else revert LimitOrderRegistry__NoLiquidityInOrder();
            if (amount1 > 0) revert LimitOrderRegistry__AmountShouldBeZero();
        } else {
            if (amount1 > 0) poolToData[pool].token1.safeTransfer(sender, amount1);
            else revert LimitOrderRegistry__NoLiquidityInOrder();
            if (amount0 > 0) revert LimitOrderRegistry__AmountShouldBeZero();
        }
    }

    /*//////////////////////////////////////////////////////////////
                     CHAINLINK AUTOMATION LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returned `performData` simply contains a bool indicating which direction in the `orderBook` has orders that need to be fulfilled.
     * @param checkData the data to decode when calling this from automation
     */
    function checkUpkeep(bytes calldata checkData) external view returns (bool upkeepNeeded, bytes memory performData) {
        UniswapV3Pool pool = abi.decode(checkData, (UniswapV3Pool));
        (, int24 currentTick, , , , , ) = pool.slot0();
        PoolData memory data = poolToData[pool];
        BatchOrder memory order;
        OrderStatus status;
        bool walkDirection;
        uint256 deadline = block.timestamp + 900;

        if (data.centerHead != 0) {
            // centerHead is set, check if it is ITM.
            order = orderBook[data.centerHead];
            status = _getOrderStatus(currentTick, order.tickLower, order.tickUpper, order.direction);
            if (status == OrderStatus.ITM) {
                walkDirection = true; // Walk towards head of list.
                upkeepNeeded = true;
                performData = abi.encode(pool, walkDirection, deadline);
                return (upkeepNeeded, performData);
            }
        }
        if (data.centerTail != 0) {
            // If walk direction has not been set, then we know, no head orders are ITM.
            // So check tail orders.
            order = orderBook[data.centerTail];
            status = _getOrderStatus(currentTick, order.tickLower, order.tickUpper, order.direction);
            if (status == OrderStatus.ITM) {
                walkDirection = false; // Walk towards tail of list.
                upkeepNeeded = true;
                performData = abi.encode(pool, walkDirection, deadline);
                return (upkeepNeeded, performData);
            }
        }
        return (false, abi.encode(0));
    }

    /**
     * @notice Callable by anyone, as long as there are orders ITM, that need to be fulfilled.
     * @param performData checkData the data to decode when calling this from automation
     * @dev Does not use _removeOrderFromList, so that the center head/tail
     *      value is not updated every single time and order is fulfilled, instead we just update it once at the end.
     */
    function performUpkeep(bytes calldata performData) external {
        (UniswapV3Pool pool, bool walkDirection, uint256 deadline) = abi.decode(
            performData,
            (UniswapV3Pool, bool, uint256)
        );

        if (address(poolToData[pool].token0) == address(0)) revert LimitOrderRegistry__PoolNotSetup(address(pool));

        PoolData storage data = poolToData[pool];

        // Estimate gas cost.
        uint256 estimatedFee = uint256(upkeepGasLimit * getGasPrice());

        (, int24 currentTick, , , , , ) = pool.slot0();
        bool orderFilled;

        // Fulfill orders.
        uint256 target = walkDirection ? data.centerHead : data.centerTail;
        for (uint256 i; i < maxFillsPerUpkeep; ++i) {
            if (target == 0) break;
            BatchOrder storage order = orderBook[target];
            OrderStatus status = _getOrderStatus(currentTick, order.tickLower, order.tickUpper, order.direction);
            if (status == OrderStatus.ITM) {
                _fulfillOrder(target, pool, order, estimatedFee, deadline);
                target = walkDirection ? order.head : order.tail;
                // Reconnect List and Zero out orders head and tail values removing order from the list.
                orderBook[order.tail].head = order.head;
                orderBook[order.head].tail = order.tail;
                order.head = 0;
                order.tail = 0;
                // Update bool to indicate batch order is ready to handle claims.
                claim[order.batchId].isReadyForClaim = true;
                // Zero out orders batch id.
                order.batchId = 0;
                // Reset user count.
                order.userCount = 0;
                orderFilled = true;
            } else break;
        }

        if (!orderFilled) revert LimitOrderRegistry__NoOrdersToFulfill();

        // Update appropriate center value.
        if (walkDirection) {
            data.centerHead = target;
        } else {
            data.centerTail = target;
        }
    }

    /*//////////////////////////////////////////////////////////////
                     INTERNAL ORDER LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Check if a given Uniswap V3 position is already in the `orderBook`.
     * @dev Looks at Nodes head and tail, and checks for edge case of node being the only node in the `orderBook`
     */
    function _checkThatNodeIsInList(uint256 node, BatchOrder memory order, PoolData memory data) internal pure {
        if (order.head == 0 && order.tail == 0) {
            // Possible but the order may be centerTail or centerHead.
            if (data.centerHead != node && data.centerTail != node) revert LimitOrderRegistry__OrderNotInList(node);
        }
    }

    /**
     * @notice Finds appropriate spot in `orderBook` for an order.
     * @param data the linked list in memory
     * @param startingNode the index to start searching from
     * @param targetTick the tick to stop searching at
     * @param direction the direction to traverse the list
     * @dev implemented as a linked list
     */
    function _findSpot(
        PoolData memory data,
        uint256 startingNode,
        int24 targetTick,
        bool direction
    ) internal view returns (uint256 proposedHead, uint256 proposedTail) {
        BatchOrder memory node;
        if (startingNode == 0) {
            if (direction && data.centerHead != 0) {
                startingNode = data.centerHead;
                node = orderBook[startingNode];
            } else if (!direction && data.centerTail != 0) {
                startingNode = data.centerTail;
                node = orderBook[startingNode];
            } else return (0, 0);
        } else {
            node = orderBook[startingNode];
            if (node.direction != direction) revert LimitOrderRegistry__OrderNotInList(startingNode);
            _checkThatNodeIsInList(startingNode, node, data);
        }
        uint256 nodeId = startingNode;

        while (true) {
            if (direction) {
                // Go until we find an order with a tick lower GREATER or equal to targetTick, then set proposedTail equal to the tail, and proposed head to the current node.
                if (node.tickLower >= targetTick) {
                    return (nodeId, node.tail);
                } else if (node.head == 0) {
                    // Made it to head of list.
                    return (0, nodeId);
                } else {
                    nodeId = node.head;
                    node = orderBook[nodeId];
                }
            } else {
                // Go until we find tick upper that is LESS than or equal to targetTick
                if (node.tickUpper <= targetTick) {
                    return (node.head, nodeId);
                } else if (node.tail == 0) {
                    // Made it to the tail of the list.
                    return (nodeId, 0);
                } else {
                    nodeId = node.tail;
                    node = orderBook[nodeId];
                }
            }
        }
    }

    /**
     * @notice Checks if newly added order should be made the new center head/tail.
     * @param pool the uniswap v3 pool
     * @param currentTick the current tick
     * @param upper the upper tick
     * @param lower the lower tick
     */
    function _updateCenter(
        UniswapV3Pool pool,
        uint256 positionId,
        int24 currentTick,
        int24 upper,
        int24 lower
    ) internal {
        PoolData memory data = poolToData[pool];
        if (currentTick > upper) {
            // Check if centerTail needs to be updated.
            if (data.centerTail == 0) {
                // Currently no centerTail, so this order must become it.
                poolToData[pool].centerTail = positionId;
            } else {
                BatchOrder memory centerTail = orderBook[data.centerTail];
                if (upper > centerTail.tickUpper) {
                    // New position is closer to the current pool tick, so it becomes new centerTail.
                    poolToData[pool].centerTail = positionId;
                }
                // else nothing to do.
            }
        } else if (currentTick < lower) {
            // Check if centerHead needs to be updated.
            if (data.centerHead == 0) {
                // Currently no centerHead, so this order must become it.
                poolToData[pool].centerHead = positionId;
            } else {
                BatchOrder memory centerHead = orderBook[data.centerHead];
                if (lower < centerHead.tickLower) {
                    // New position is closer to the current pool tick, so it becomes new centerHead.
                    poolToData[pool].centerHead = positionId;
                }
                // else nothing to do.
            }
        }
    }

    /**
     * @notice Add a Uniswap V3 LP position to the `orderBook`.
     * @param data the linked list in memory
     * @param startingNode the index to start searching from
     * @param targetTick the tick to stop searching at
     * @param position the position to add the item to the list at
     * @param direction the direction to traverse the list
     */
    function _addPositionToList(
        PoolData memory data,
        uint256 startingNode,
        int24 targetTick,
        uint256 position,
        bool direction
    ) internal {
        // seek to find a free spot
        (uint256 head, uint256 tail) = _findSpot(data, startingNode, targetTick, direction);
        // one of head or tail will be not zero. insert into the one that is not zero
        if (tail != 0) {
            orderBook[tail].head = position;
            orderBook[position].tail = tail;
        }
        if (head != 0) {
            orderBook[head].tail = position;
            orderBook[position].head = head;
        }
    }

    /**
     * @notice Setup a newly minted LP position, or one being reused.
     * @param position the position to add the item to the list at
     * @param direction the direction to traverse the list
     * @dev Sets batchId, and direction.
     */
    function _setupOrder(bool direction, uint256 position) internal {
        BatchOrder storage order = orderBook[position];
        order.batchId = batchCount++;
        order.direction = direction;
    }

    /**
     * @notice Updates a BatchOrder's token0/token1 amount, as well as associated
     *         `batchIdToUserDepositAmount` mapping value.
     * @param positionId the id of the position to update
     * @param user the user
     * @param amount the amount to update the order by
     * @return userTotal the new total
     * @dev If user is new to the order, increment userCount.
     */
    function _updateOrder(uint256 positionId, address user, uint128 amount) internal returns (uint128 userTotal) {
        BatchOrder storage order = orderBook[positionId];
        if (order.direction) {
            // token1
            order.token0Amount += amount;
        } else {
            // token0
            order.token1Amount += amount;
        }

        // Check if user is already in the order.
        uint128 batchId = order.batchId;
        uint128 originalDepositAmount = batchIdToUserDepositAmount[batchId][user];
        // If this is a new user in the order, add 1 to userCount.
        if (originalDepositAmount == 0) order.userCount++;
        batchIdToUserDepositAmount[batchId][user] = originalDepositAmount + amount;
        return (originalDepositAmount + amount);
    }

    /**
     * @notice Mints a new Uniswap V3 LP position.
     * @param data the linked list in memory
     * @param upper the upper tick for position
     * @param lower the lower tick for position
     * @param amount0 the amount of token0 for position
     * @param amount1 the amount of token1 for position
     * @param direction the direction of the order (bid/ask)
     * @return positionId the id of the position
     * @param deadline replay protection deadline
     */
    function _mintPosition(
        PoolData memory data,
        int24 upper,
        int24 lower,
        uint128 amount0,
        uint128 amount1,
        bool direction,
        uint256 deadline
    ) internal returns (uint256) {
        if (direction) data.token0.safeApprove(address(POSITION_MANAGER), amount0);
        else data.token1.safeApprove(address(POSITION_MANAGER), amount1);

        // 0.9999e4 accounts for rounding errors in the Uniswap V3 protocol.
        uint128 amount0Min = amount0 == 0 ? 0 : (amount0 * 0.9999e4) / 1e4;
        uint128 amount1Min = amount1 == 0 ? 0 : (amount1 * 0.9999e4) / 1e4;

        // Create mint params.
        NonFungiblePositionManager.MintParams memory params = NonFungiblePositionManager.MintParams({
            token0: address(data.token0),
            token1: address(data.token1),
            fee: data.fee,
            tickLower: lower,
            tickUpper: upper,
            amount0Desired: amount0,
            amount1Desired: amount1,
            amount0Min: amount0Min,
            amount1Min: amount1Min,
            recipient: address(this),
            deadline: deadline
        });

        // Supply liquidity to pool.
        (uint256 tokenId, , , ) = POSITION_MANAGER.mint(params);

        // Revert if tokenId received is 0 id.
        // Zero token id is reserved for NULL values in linked list.
        if (tokenId == 0) revert LimitOrderRegistry__InvalidPositionId();

        // If position manager still has allowance, zero it out.
        if (direction && data.token0.allowance(address(this), address(POSITION_MANAGER)) > 0)
            data.token0.safeApprove(address(POSITION_MANAGER), 0);
        if (!direction && data.token1.allowance(address(this), address(POSITION_MANAGER)) > 0)
            data.token1.safeApprove(address(POSITION_MANAGER), 0);

        return tokenId;
    }

    /**
     * @notice Adds liquidity to a given `positionId`.
     * @param data the linked list in memory
     * @param positionId the id of the position to update
     * @param amount0 the amount of token0 for position
     * @param amount1 the amount of token1 for position
     * @param direction the direction of the order (bid/ask)
     * @param deadline replay protection deadline
     */
    function _addToPosition(
        PoolData memory data,
        uint256 positionId,
        uint128 amount0,
        uint128 amount1,
        bool direction,
        uint256 deadline
    ) internal {
        if (direction) data.token0.safeApprove(address(POSITION_MANAGER), amount0);
        else data.token1.safeApprove(address(POSITION_MANAGER), amount1);

        uint128 amount0Min = amount0 == 0 ? 0 : (amount0 * 0.9999e4) / 1e4;
        uint128 amount1Min = amount1 == 0 ? 0 : (amount1 * 0.9999e4) / 1e4;

        // Create increase liquidity params.
        NonFungiblePositionManager.IncreaseLiquidityParams memory params = NonFungiblePositionManager
            .IncreaseLiquidityParams({
                tokenId: positionId,
                amount0Desired: amount0,
                amount1Desired: amount1,
                amount0Min: amount0Min,
                amount1Min: amount1Min,
                deadline: deadline
            });

        // Increase liquidity in pool.
        POSITION_MANAGER.increaseLiquidity(params);

        // If position manager still has allowance, zero it out.
        if (direction && data.token0.allowance(address(this), address(POSITION_MANAGER)) > 0)
            data.token0.safeApprove(address(POSITION_MANAGER), 0);
        if (!direction && data.token1.allowance(address(this), address(POSITION_MANAGER)) > 0)
            data.token1.safeApprove(address(POSITION_MANAGER), 0);
    }

    /**
     * @notice Enforces minimum liquidity requirements for orders.
     * @param amount the amount to check
     * @param asset the asset to check the amount against
     * @dev this will revert if we fail checks
     */
    function _enforceMinimumLiquidity(uint256 amount, ERC20 asset) internal view {
        uint256 minimum = minimumAssets[asset];
        // special case for 0, for increasing clarity
        if (minimum == 0) revert LimitOrderRegistry__MinimumNotSet(address(asset));
        if (amount < minimum) revert LimitOrderRegistry__MinimumNotMet(address(asset), minimum, amount);
    }

    /**
     * @notice Helper function to determine an orders status.
     * @param currentTick the current tick
     * @param lower the lower tick of order
     * @param upper the upper tick of order
     * @param direction the direction of the order
     * @return status the OrderStatus enum of an order created with the said current tick
     * @dev Returns
     *      - ITM if order is ready to be filled, and is composed of wanted asset
     *      - OTM if order is not ready to be filled, but order can still be cancelled, because order is composed of asset to sell
     *      - MIXED order is composed of both wanted asset, and asset to sell, can not be fulfilled or cancelled.
     */
    function _getOrderStatus(
        int24 currentTick,
        int24 lower,
        int24 upper,
        bool direction
    ) internal pure returns (OrderStatus status) {
        if (upper == lower) revert LimitOrderRegistry__InvalidTickRange(upper, lower);
        if (direction) {
            // Indicates we want to go lower -> upper.
            if (currentTick > upper) return OrderStatus.ITM;
            if (currentTick >= lower) return OrderStatus.MIXED;
            else return OrderStatus.OTM;
        } else {
            // Indicates we want to go upper -> lower.
            if (currentTick < lower) return OrderStatus.ITM;
            if (currentTick <= upper) return OrderStatus.MIXED;
            else return OrderStatus.OTM;
        }
    }

    /**
     * @notice Called during `performUpkeep` to fulfill an ITM order.
     * @dev Sets Claim info, removes all liquidity from position, and zeroes out BatchOrder amount0 and amount1 values.
     */
    function _fulfillOrder(
        uint256 target,
        UniswapV3Pool pool,
        BatchOrder storage order,
        uint256 estimatedFee,
        uint256 deadline
    ) internal {
        // Save fee per user in Claim Struct.
        uint256 totalUsers = order.userCount;
        Claim storage newClaim = claim[order.batchId];
        newClaim.feePerUser = uint128(estimatedFee / totalUsers);
        newClaim.pool = pool;

        // Take all liquidity from the order.
        uint128 amount0;
        uint128 amount1;
        (amount0, amount1) = _takeFromPosition(target, pool, 1e18, deadline);
        if (order.direction) {
            // Copy the tokenIn amount from the order, this is the total user deposit.
            newClaim.token0Amount = order.token0Amount;
            // Total token out is amount1.
            newClaim.token1Amount = amount1;
        } else {
            // Copy the tokenIn amount from the order, this is the total user deposit.
            newClaim.token1Amount = order.token1Amount;
            // Total token out is amount0.
            newClaim.token0Amount = amount0;
        }
        newClaim.direction = order.direction;

        // Zero out order balances.
        order.token0Amount = 0;
        order.token1Amount = 0;

        emit OrderFilled(order.batchId, address(pool));
    }

    /**
     * @notice Removes liquidity from `target` Uniswap V3 LP position.
     * @dev Collects fees from `target` position, and saves them in `tokenToSwapFees`.
     */
    function _takeFromPosition(
        uint256 target,
        UniswapV3Pool pool,
        uint256 liquidityPercent,
        uint256 deadline
    ) internal returns (uint128, uint128) {
        (, , , , , , , uint128 liquidity, , , , ) = POSITION_MANAGER.positions(target);
        liquidity = uint128(uint256(liquidity * liquidityPercent) / 1e18);

        // Create decrease liquidity params.
        // NB: because the amount0Min and amount1Min are 0 here, it is technically possible to front run this
        // that is probably okay, but it should be noted
        NonFungiblePositionManager.DecreaseLiquidityParams memory params = NonFungiblePositionManager
            .DecreaseLiquidityParams({
                tokenId: target,
                liquidity: liquidity,
                amount0Min: 0,
                amount1Min: 0,
                deadline: deadline
            });

        // Decrease liquidity in pool.
        uint128 amount0;
        uint128 amount1;
        {
            (uint256 a0, uint256 a1) = POSITION_MANAGER.decreaseLiquidity(params);
            // downcast to uint128 since those are the units we use
            amount0 = uint128(a0);
            amount1 = uint128(a1);
        }

        // If completely closing position, then collect fees as well.
        NonFungiblePositionManager.CollectParams memory collectParams;
        {
            uint128 amount0Max;
            uint128 amount1Max;
            if (liquidityPercent == 1e18) {
                amount0Max = type(uint128).max;
                amount1Max = type(uint128).max;
            } else {
                // Otherwise only collect principal.
                amount0Max = amount0;
                amount1Max = amount1;
            }
            // Create fee collection params.
            collectParams = NonFungiblePositionManager.CollectParams({
                tokenId: target,
                recipient: address(this),
                amount0Max: amount0Max,
                amount1Max: amount1Max
            });
        }

        // Save token balances.
        ERC20 token0 = poolToData[pool].token0;
        ERC20 token1 = poolToData[pool].token1;
        uint256 token0Balance = token0.balanceOf(address(this));
        uint256 token1Balance = token1.balanceOf(address(this));

        // Collect fees.
        POSITION_MANAGER.collect(collectParams);

        // Save fees earned, take the total token amount out - the amount removed from liquidity to get the fees earned.
        uint128 token0Fees = uint128(token0.balanceOf(address(this)) - token0Balance) - amount0;
        uint128 token1Fees = uint128(token1.balanceOf(address(this)) - token1Balance) - amount1;
        // Save any swap fees.
        if (token0Fees > 0) tokenToSwapFees[address(token0)] += token0Fees;
        if (token1Fees > 0) tokenToSwapFees[address(token1)] += token1Fees;

        return (amount0, amount1);
    }

    /**
     * @notice Removes an order from the `orderBook`.
     * @dev Checks if order is one of the center values, and updates the head if need be.
     */
    function _removeOrderFromList(uint256 target, UniswapV3Pool pool, BatchOrder storage order) internal {
        // grab the centerHead and centerTail from mapping
        uint256 centerHead = poolToData[pool].centerHead;
        uint256 centerTail = poolToData[pool].centerTail;

        if (target == centerHead) {
            // if the target is the current center, set it to the center orders head
            uint256 newHead = orderBook[centerHead].head;
            // it is okay to be zero
            poolToData[pool].centerHead = newHead;
        } else if (target == centerTail) {
            // do the same check with tail
            uint256 newTail = orderBook[centerTail].tail;
            // it is okay to be zero
            poolToData[pool].centerTail = newTail;
        }

        // Remove order from linked list.
        orderBook[order.tail].head = order.head;
        orderBook[order.head].tail = order.tail;
        order.head = 0;
        order.tail = 0;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Helper function to get the gas price used for fee calculation.
     */
    function getGasPrice() public view returns (uint256) {
        // If gas feed is set use it.
        if (fastGasFeed != address(0)) {
            (, int256 _answer, , uint256 _timestamp, ) = IChainlinkAggregator(fastGasFeed).latestRoundData();
            uint256 timeSinceLastUpdate = block.timestamp - _timestamp;
            // Check answer is not stale.
            if (timeSinceLastUpdate > FAST_GAS_HEARTBEAT) {
                // If answer is stale use owner set value.
                // Multiply by 1e9 to convert gas price to gwei
                return uint256(upkeepGasPrice) * 1e9;
            } else {
                // Else use the datafeed value.
                uint256 answer = uint256(_answer);
                return answer;
            }
        }
        // Else use owner set value.
        return uint256(upkeepGasPrice) * 1e9; // Multiply by 1e9 to convert gas price to gwei
    }

    /**
     * @notice Helper function that finds the appropriate spot in the linked list for a new order.
     * @param pool the Uniswap V3 pool you want to create an order in
     * @param startingNode the UniV3 position Id to start looking
     * @param targetTick the targetTick of the order you want to place
     * @param direction the direction of the order
     * @return proposedHead , proposedTail pr the correct head and tail for the new order
     * @dev if both head and tail are zero, just pass in zero for the `startingNode`
     *      otherwise pass in either the nonzero head or nonzero tail for the `startingNode`
     */
    function findSpot(
        UniswapV3Pool pool,
        uint256 startingNode,
        int24 targetTick,
        bool direction
    ) external view returns (uint256 proposedHead, uint256 proposedTail) {
        PoolData memory data = poolToData[pool];

        int24 tickSpacing = pool.tickSpacing();
        // Make sure targetTick is divisible by spacing.
        if (targetTick % tickSpacing != 0) revert LimitOrderRegistry__InvalidTargetTick(targetTick, tickSpacing);

        (proposedHead, proposedTail) = _findSpot(data, startingNode, targetTick, direction);
    }

    /**
     * @notice Helper function to get the fee per user for a specific order.
     * @param batchId the batch id to get info for
     */
    function getFeePerUser(uint128 batchId) external view returns (uint128) {
        return claim[batchId].feePerUser;
    }

    /**
     * @notice Helper function to view if a BatchOrder is ready to claim.
     * @param batchId the batch id to get info for
     * @return bool true if order is ready to claim, else false
     */
    function isOrderReadyForClaim(uint128 batchId) external view returns (bool) {
        return claim[batchId].isReadyForClaim;
    }

    /**
     * @notice Helper function to get OrderBook/BatchOrder
     * @param id the id to get info for
     * @return Batchorder the batch order at id
    */
    function getOrderBook(uint256 id) external view returns (BatchOrder memory) {
        return orderBook[id];
    }
/**
     * @notice Helper function to get claim
     * @param batchId the id to get info for
     * @return Claim the claim info at id
    */
    function getClaim(uint128 batchId) external view returns (Claim memory) {
        return claim[batchId];
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

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

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

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

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AutomationCompatibleInterface {
  /**
   * @notice method that is simulated by the keepers to see if any work actually
   * needs to be performed. This method does does not actually need to be
   * executable, and since it is only ever simulated it can consume lots of gas.
   * @dev To ensure that it is never called, you may want to add the
   * cannotExecute modifier from KeeperBase to your implementation of this
   * method.
   * @param checkData specified in the upkeep registration so it is always the
   * same for a registered upkeep. This can easily be broken down into specific
   * arguments using `abi.decode`, so multiple upkeeps can be registered on the
   * same contract and easily differentiated by the contract.
   * @return upkeepNeeded boolean to indicate whether the keeper should call
   * performUpkeep or not.
   * @return performData bytes that the keeper should call performUpkeep with, if
   * upkeep is needed. If you would like to encode data to decode later, try
   * `abi.encode`.
   */
  function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);

  /**
   * @notice method that is actually executed by the keepers, via the registry.
   * The data returned by the checkUpkeep simulation will be passed into
   * this method to actually be executed.
   * @dev The input to this method should not be trusted, and the caller of the
   * method should not even be restricted to any single registry. Anyone should
   * be able call it, and the input should be validated, there is no guarantee
   * that the data passed in is the performData returned from checkUpkeep. This
   * could happen due to malicious keepers, racing keepers, or simply a state
   * change while the performUpkeep transaction is waiting for confirmation.
   * Always validate the data passed in.
   * @param performData is the data which was passed back from the checkData
   * simulation. If it is encoded, it can easily be decoded into other types by
   * calling `abi.decode`. This data should not be trusted, and should be
   * validated against the contract's current state.
   */
  function performUpkeep(bytes calldata performData) external;
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

interface UniswapV3Pool {
    event Burn(
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );
    event Collect(
        address indexed owner,
        address recipient,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount0,
        uint128 amount1
    );
    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
    event Flash(
        address indexed sender,
        address indexed recipient,
        uint256 amount0,
        uint256 amount1,
        uint256 paid0,
        uint256 paid1
    );
    event IncreaseObservationCardinalityNext(
        uint16 observationCardinalityNextOld,
        uint16 observationCardinalityNextNew
    );
    event Initialize(uint160 sqrtPriceX96, int24 tick);
    event Mint(
        address sender,
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );
    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
    event Swap(
        address indexed sender,
        address indexed recipient,
        int256 amount0,
        int256 amount1,
        uint160 sqrtPriceX96,
        uint128 liquidity,
        int24 tick
    );

    function burn(int24 tickLower, int24 tickUpper, uint128 amount) external returns (uint256 amount0, uint256 amount1);

    function collect(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    function collectProtocol(
        address recipient,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    function factory() external view returns (address);

    function fee() external view returns (uint24);

    function feeGrowthGlobal0X128() external view returns (uint256);

    function feeGrowthGlobal1X128() external view returns (uint256);

    function flash(address recipient, uint256 amount0, uint256 amount1, bytes memory data) external;

    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;

    function initialize(uint160 sqrtPriceX96) external;

    function liquidity() external view returns (uint128);

    function maxLiquidityPerTick() external view returns (uint128);

    function mint(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount,
        bytes memory data
    ) external returns (uint256 amount0, uint256 amount1);

    function observations(
        uint256
    )
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );

    function observe(
        uint32[] memory secondsAgos
    ) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

    function positions(
        bytes32
    )
        external
        view
        returns (
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    function protocolFees() external view returns (uint128 token0, uint128 token1);

    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;

    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    function snapshotCumulativesInside(
        int24 tickLower,
        int24 tickUpper
    ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);

    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes memory data
    ) external returns (int256 amount0, int256 amount1);

    function tickBitmap(int16) external view returns (uint256);

    function tickSpacing() external view returns (int24);

    function ticks(
        int24
    )
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    function token0() external view returns (address);

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

File 7 of 16 : NonFungiblePositionManager.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

interface NonFungiblePositionManager {
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
    event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1);
    event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

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

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

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

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

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external view returns (bytes32);

    function WETH9() external view returns (address);

    function approve(address to, uint256 tokenId) external;

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

    function baseURI() external pure returns (string memory);

    function burn(uint256 tokenId) external payable;

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

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

    function decreaseLiquidity(
        DecreaseLiquidityParams memory params
    ) external payable returns (uint256 amount0, uint256 amount1);

    function factory() external view returns (address);

    function getApproved(uint256 tokenId) external view returns (address);

    function increaseLiquidity(
        IncreaseLiquidityParams memory params
    ) external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    function isApprovedForAll(address owner, address operator) external view returns (bool);

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

    function multicall(bytes[] memory data) external payable returns (bytes[] memory results);

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

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

    function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable;

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

    function refundETH() external payable;

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

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

    function selfPermit(address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable;

    function selfPermitAllowed(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;

    function selfPermitAllowedIfNecessary(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;

    function selfPermitIfNecessary(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;

    function setApprovalForAll(address operator, bool approved) external;

    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    function sweepToken(address token, uint256 amountMinimum, address recipient) external payable;

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

    function tokenByIndex(uint256 index) external view returns (uint256);

    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    function tokenURI(uint256 tokenId) external view returns (string memory);

    function totalSupply() external view returns (uint256);

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

    function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes memory data) external;

    function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;
}

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

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

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

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

  function decimals() external view returns (uint8 decimalPlaces);

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

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

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

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function transferFrom(
    address from,
    address to,
    uint256 value
  ) external returns (bool success);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

struct RegistrationParams {
    string name;
    bytes encryptedEmail;
    address upkeepContract;
    uint32 gasLimit;
    address adminAddress;
    bytes checkData;
    bytes offchainConfig;
    uint96 amount;
}

/**
 * @notice Contract to accept requests for upkeep registrations
 * @dev There are 2 registration workflows in this contract
 * Flow 1. auto approve OFF / manual registration - UI calls `register` function on this contract, this contract owner at a later time then manually
 *  calls `approve` to register upkeep and emit events to inform UI and others interested.
 * Flow 2. auto approve ON / real time registration - UI calls `register` function as before, which calls the `registerUpkeep` function directly on
 *  keeper registry and then emits approved event to finish the flow automatically without manual intervention.
 * The idea is to have same interface(functions,events) for UI or anyone using this contract irrespective of auto approve being enabled or not.
 * they can just listen to `RegistrationRequested` & `RegistrationApproved` events and know the status on registrations.
 */
interface IKeeperRegistrar {
    /**
     * @notice register can only be called through transferAndCall on LINK contract
     * @param name string of the upkeep to be registered
     * @param encryptedEmail email address of upkeep contact
     * @param upkeepContract address to perform upkeep on
     * @param gasLimit amount of gas to provide the target contract when performing upkeep
     * @param adminAddress address to cancel upkeep and withdraw remaining funds
     * @param checkData data passed to the contract when checking for upkeep
     * @param amount quantity of LINK upkeep is funded with (specified in Juels)
     * @param source application sending this request
     * @param sender address of the sender making the request
     */
    function register(
        string memory name,
        bytes calldata encryptedEmail,
        address upkeepContract,
        uint32 gasLimit,
        address adminAddress,
        bytes calldata checkData,
        uint96 amount,
        uint8 source,
        address sender
    ) external;

    function registerUpkeep(RegistrationParams calldata requestParams) external returns (uint256);

    function typeAndVersion() external view returns (string memory);
}

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

pragma solidity ^0.8.0;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

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

interface IChainlinkAggregator is AggregatorV2V3Interface {
    function maxAnswer() external view returns (int192);

    function minAnswer() external view returns (int192);

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

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

pragma solidity ^0.8.0;

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

File 14 of 16 : AggregatorV2V3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";

interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorInterface {
  function latestAnswer() external view returns (int256);

  function latestTimestamp() external view returns (uint256);

  function latestRound() external view returns (uint256);

  function getAnswer(uint256 roundId) external view returns (int256);

  function getTimestamp(uint256 roundId) external view returns (uint256);

  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);

  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

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

  function version() external view returns (uint256);

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

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

Settings
{
  "remappings": [
    "@forge-std/=lib/forge-std/src/",
    "@ds-test/=lib/forge-std/lib/ds-test/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@chainlink/=lib/chainlink/",
    "@solmate/=lib/solmate/src/",
    "chainlink/=lib/chainlink/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"contract NonFungiblePositionManager","name":"_positionManager","type":"address"},{"internalType":"contract ERC20","name":"wrappedNative","type":"address"},{"internalType":"contract LinkTokenInterface","name":"link","type":"address"},{"internalType":"contract IKeeperRegistrar","name":"_registrar","type":"address"},{"internalType":"address","name":"_fastGasFeed","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"LimitOrderRegistry__AmountShouldBeZero","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__CenterITM","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__ContractNotShutdown","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__ContractShutdown","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__DirectionMisMatch","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__InvalidBatchId","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__InvalidFillsPerUpkeep","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__InvalidGasLimit","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__InvalidGasPrice","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__InvalidPositionId","type":"error"},{"inputs":[{"internalType":"int24","name":"targetTick","type":"int24"},{"internalType":"int24","name":"tickSpacing","type":"int24"}],"name":"LimitOrderRegistry__InvalidTargetTick","type":"error"},{"inputs":[{"internalType":"int24","name":"upper","type":"int24"},{"internalType":"int24","name":"lower","type":"int24"}],"name":"LimitOrderRegistry__InvalidTickRange","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"minimum","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LimitOrderRegistry__MinimumNotMet","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"LimitOrderRegistry__MinimumNotSet","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__NoLiquidityInOrder","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__NoOrdersToFulfill","type":"error"},{"inputs":[{"internalType":"int24","name":"currentTick","type":"int24"},{"internalType":"int24","name":"targetTick","type":"int24"},{"internalType":"bool","name":"direction","type":"bool"}],"name":"LimitOrderRegistry__OrderITM","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"LimitOrderRegistry__OrderNotInList","type":"error"},{"inputs":[{"internalType":"uint128","name":"batchId","type":"uint128"}],"name":"LimitOrderRegistry__OrderNotReadyToClaim","type":"error"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"LimitOrderRegistry__PoolAlreadySetup","type":"error"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"LimitOrderRegistry__PoolNotSetup","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"batchId","type":"uint256"}],"name":"LimitOrderRegistry__UserNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"LimitOrderRegistry__ZeroFeesToWithdraw","type":"error"},{"inputs":[],"name":"LimitOrderRegistry__ZeroNativeBalance","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"},{"components":[{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"uint64","name":"userCount","type":"uint64"},{"internalType":"uint128","name":"batchId","type":"uint128"},{"internalType":"uint128","name":"token0Amount","type":"uint128"},{"internalType":"uint128","name":"token1Amount","type":"uint128"},{"internalType":"uint256","name":"head","type":"uint256"},{"internalType":"uint256","name":"tail","type":"uint256"}],"indexed":false,"internalType":"struct LimitOrderRegistry.BatchOrder","name":"affectedOrder","type":"tuple"}],"name":"CancelOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint128","name":"batchId","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pool","type":"address"}],"name":"LimitOrderSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"userTotal","type":"uint128"},{"components":[{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"uint64","name":"userCount","type":"uint64"},{"internalType":"uint128","name":"batchId","type":"uint128"},{"internalType":"uint128","name":"token0Amount","type":"uint128"},{"internalType":"uint128","name":"token1Amount","type":"uint128"},{"internalType":"uint256","name":"head","type":"uint256"},{"internalType":"uint256","name":"tail","type":"uint256"}],"indexed":false,"internalType":"struct LimitOrderRegistry.BatchOrder","name":"affectedOrder","type":"tuple"}],"name":"NewOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"batchId","type":"uint256"},{"indexed":false,"internalType":"address","name":"pool","type":"address"}],"name":"OrderFilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isShutdown","type":"bool"}],"name":"ShutdownChanged","type":"event"},{"inputs":[],"name":"FAST_GAS_HEARTBEAT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LINK","outputs":[{"internalType":"contract LinkTokenInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FILLS_PER_UPKEEP","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_GAS_LIMIT","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_GAS_PRICE","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POSITION_MANAGER","outputs":[{"internalType":"contract NonFungiblePositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WRAPPED_NATIVE","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"batchCount","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"address","name":"","type":"address"}],"name":"batchIdToUserDepositAmount","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract UniswapV3Pool","name":"pool","type":"address"},{"internalType":"int24","name":"targetTick","type":"int24"},{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"cancelOrder","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"},{"internalType":"uint128","name":"batchId","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"checkData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"","type":"uint128"}],"name":"claim","outputs":[{"internalType":"contract UniswapV3Pool","name":"pool","type":"address"},{"internalType":"uint128","name":"token0Amount","type":"uint128"},{"internalType":"uint128","name":"token1Amount","type":"uint128"},{"internalType":"uint128","name":"feePerUser","type":"uint128"},{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"bool","name":"isReadyForClaim","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"batchId","type":"uint128"},{"internalType":"address","name":"user","type":"address"}],"name":"claimOrder","outputs":[{"internalType":"contract ERC20","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"fastGasFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract UniswapV3Pool","name":"pool","type":"address"},{"internalType":"uint256","name":"startingNode","type":"uint256"},{"internalType":"int24","name":"targetTick","type":"int24"},{"internalType":"bool","name":"direction","type":"bool"}],"name":"findSpot","outputs":[{"internalType":"uint256","name":"proposedHead","type":"uint256"},{"internalType":"uint256","name":"proposedTail","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"batchId","type":"uint128"}],"name":"getClaim","outputs":[{"components":[{"internalType":"contract UniswapV3Pool","name":"pool","type":"address"},{"internalType":"uint128","name":"token0Amount","type":"uint128"},{"internalType":"uint128","name":"token1Amount","type":"uint128"},{"internalType":"uint128","name":"feePerUser","type":"uint128"},{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"bool","name":"isReadyForClaim","type":"bool"}],"internalType":"struct LimitOrderRegistry.Claim","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"batchId","type":"uint128"}],"name":"getFeePerUser","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getOrderBook","outputs":[{"components":[{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"uint64","name":"userCount","type":"uint64"},{"internalType":"uint128","name":"batchId","type":"uint128"},{"internalType":"uint128","name":"token0Amount","type":"uint128"},{"internalType":"uint128","name":"token1Amount","type":"uint128"},{"internalType":"uint256","name":"head","type":"uint256"},{"internalType":"uint256","name":"tail","type":"uint256"}],"internalType":"struct LimitOrderRegistry.BatchOrder","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract UniswapV3Pool","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"int24","name":"","type":"int24"},{"internalType":"int24","name":"","type":"int24"}],"name":"getPositionFromTicks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initiateShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"batchId","type":"uint128"}],"name":"isOrderReadyForClaim","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liftShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxFillsPerUpkeep","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"name":"minimumAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract UniswapV3Pool","name":"pool","type":"address"},{"internalType":"int24","name":"targetTick","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"uint256","name":"startingNode","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"newOrder","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"orderBook","outputs":[{"internalType":"bool","name":"direction","type":"bool"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"uint64","name":"userCount","type":"uint64"},{"internalType":"uint128","name":"batchId","type":"uint128"},{"internalType":"uint128","name":"token0Amount","type":"uint128"},{"internalType":"uint128","name":"token1Amount","type":"uint128"},{"internalType":"uint256","name":"head","type":"uint256"},{"internalType":"uint256","name":"tail","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"performUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract UniswapV3Pool","name":"","type":"address"}],"name":"poolToData","outputs":[{"internalType":"uint256","name":"centerHead","type":"uint256"},{"internalType":"uint256","name":"centerTail","type":"uint256"},{"internalType":"contract ERC20","name":"token0","type":"address"},{"internalType":"contract ERC20","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registrar","outputs":[{"internalType":"contract IKeeperRegistrar","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"feed","type":"address"}],"name":"setFastGasFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"newVal","type":"uint16"}],"name":"setMaxFillsPerUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"contract ERC20","name":"asset","type":"address"}],"name":"setMinimumAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKeeperRegistrar","name":"_registrar","type":"address"}],"name":"setRegistrar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"gasPrice","type":"uint32"}],"name":"setUpkeepGasPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract UniswapV3Pool","name":"pool","type":"address"},{"internalType":"uint256","name":"initialUpkeepFunds","type":"uint256"}],"name":"setupLimitOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenToSwapFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepGasLimit","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upkeepGasPrice","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenFeeIsIn","type":"address"}],"name":"withdrawSwapFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e0604052600680546001600160d01b0319166a01000a0000001e000493e01790553480156200002e57600080fd5b50604051620061fa380380620061fa8339810160408190526200005191620000f7565b600080546001600160a01b0319166001600160a01b03881690811782556040518892907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b0394851660a05292841660805290831660c052600980549184166001600160a01b0319928316179055600a8054929093169116179055506200018b565b6001600160a01b0381168114620000f457600080fd5b50565b60008060008060008060c087890312156200011157600080fd5b86516200011e81620000de565b60208801519096506200013181620000de565b60408801519095506200014481620000de565b60608801519094506200015781620000de565b60808801519093506200016a81620000de565b60a08801519092506200017d81620000de565b809150509295509295509295565b60805160a05160c051615f9e6200025c600039600081816103b301528181612b0101528181612cce0152612d660152600081816103ff01528181613b4101528181613b8801528181613cc801528181613da501528181613e3501528181613e8601528181613f1601528181614479015281816144c0015281816145d2015281816146740152818161470401528181614755015281816147e50152818161481d0152818161495b0152614b9a015260008181610bf5015281816110270152818161167001526117270152615f9e6000f3fe60806040526004361061027d5760003560e01c80638ac9972f1161014f578063bf86d690116100c1578063e3bbb4f11161007a578063e3bbb4f114610c17578063e3f5aa5114610c2d578063ec81205514610c44578063ed12b33514610c64578063f2fde38b14610c91578063faab9d3914610cb157600080fd5b8063bf86d69014610b28578063c9d6e63014610b49578063cd3547b414610b69578063cda5197614610b96578063d368f97614610bb6578063d999984d14610be357600080fd5b8063a20b6e3411610113578063a20b6e341461096a578063a31d1b7614610a55578063a9ea1f8014610a75578063b17a24ae14610a92578063b1e057a414610ab2578063b752ceb214610ad457600080fd5b80638ac9972f146106f05780638da5cb5b1461073a5780638e06ffd51461075a578063935f450e1461079b57806397bb2b83146108d257600080fd5b80632c1f969e116101f357806350431ce4116101ac57806350431ce41461059357806353410e7b146105a85780635e2c576e146105c857806360e72adb146105dd5780636e04ff0d1461069a578063713b9dc1146106c857600080fd5b80632c1f969e146104d357806330e7c992146104e95780633cccf2291461050957806343854af514610529578063455259cb1461055e5780634585e33b1461057357600080fd5b8063151da32e11610245578063151da32e1461036f5780631b6b6d23146103a15780631bea83fe146103ed5780631c4c4d56146104215780631dd969d21461045e5780632b20e397146104b357600080fd5b806306f13056146102825780630a680e18146102c65780630cc2106d146102dd5780631467feac146102fd578063150b7a0214610336575b600080fd5b34801561028e57600080fd5b506006546102a990600160501b90046001600160801b031681565b6040516001600160801b0390911681526020015b60405180910390f35b3480156102d257600080fd5b506102db610cd1565b005b3480156102e957600080fd5b506102db6102f8366004614f8d565b610d79565b34801561030957600080fd5b5060065461032190600160201b900463ffffffff1681565b60405163ffffffff90911681526020016102bd565b34801561034257600080fd5b5061035661035136600461503f565b610df3565b6040516001600160e01b031990911681526020016102bd565b61038261037d366004615102565b610e04565b604080516001600160a01b0390931683526020830191909152016102bd565b3480156103ad57600080fd5b506103d57f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102bd565b3480156103f957600080fd5b506103d57f000000000000000000000000000000000000000000000000000000000000000081565b34801561042d57600080fd5b506102a961043c36600461513b565b6001600160801b03908116600090815260026020819052604090912001541690565b34801561046a57600080fd5b506104a5610479366004615175565b600460209081526000948552604080862082529385528385208152918452828420909152825290205481565b6040519081526020016102bd565b3480156104bf57600080fd5b506009546103d5906001600160a01b031681565b3480156104df57600080fd5b506104a5611c2081565b3480156104f557600080fd5b506102db610504366004614f8d565b6110d7565b34801561051557600080fd5b506102db6105243660046151d1565b611147565b34801561053557600080fd5b506105496105443660046151ee565b611193565b604080519283526020830191909152016102bd565b34801561056a57600080fd5b506104a56112a3565b34801561057f57600080fd5b506102db61058e366004615236565b6113a3565b34801561059f57600080fd5b506102db61162e565b3480156105b457600080fd5b506102a96105c33660046152a7565b611770565b3480156105d457600080fd5b506102db611f6f565b3480156105e957600080fd5b506106516105f836600461513b565b60026020819052600091825260409091208054600182015491909201546001600160a01b03909216916001600160801b0380831692600160801b908190048216929182169160ff918104821691600160881b9091041686565b604080516001600160a01b0390971687526001600160801b03958616602088015293851693860193909352921660608401529015156080830152151560a082015260c0016102bd565b3480156106a657600080fd5b506106ba6106b5366004615236565b612001565b6040516102bd929190615365565b3480156106d457600080fd5b506106dd601481565b60405161ffff90911681526020016102bd565b3480156106fc57600080fd5b5061071061070b366004615380565b612352565b604080516001600160801b03948516815292841660208401529216918101919091526060016102bd565b34801561074657600080fd5b506000546103d5906001600160a01b031681565b34801561076657600080fd5b506102a9610775366004615102565b60076020908152600092835260408084209091529082529020546001600160801b031681565b3480156107a757600080fd5b5061086b6107b636600461513b565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160801b03908116600090815260026020818152604092839020835160c08101855281546001600160a01b03168152600182015480871693820193909352600160801b9283900486169481019490945290910154928316606083015260ff908304811615156080830152600160881b909204909116151560a082015290565b6040516102bd919081516001600160a01b031681526020808301516001600160801b03908116918301919091526040808401518216908301526060808401519091169082015260808083015115159082015260a09182015115159181019190915260c00190565b3480156108de57600080fd5b506109316108ed3660046151d1565b600360208190526000918252604090912080546001820154600283015492909301549092916001600160a01b039081169190811690600160a01b900462ffffff1685565b6040805195865260208601949094526001600160a01b039283169385019390935216606083015262ffffff16608082015260a0016102bd565b34801561097657600080fd5b506109f66109853660046153d1565b60086020526000908152604090208054600182015460028084015460039094015460ff8416946101008504830b94600160201b810490930b936001600160401b03600160381b850416936001600160801b03600160781b90910481169382821693600160801b909304909116919089565b604080519915158a52600298890b60208b01529690970b958801959095526001600160401b0390931660608701526001600160801b039182166080870152811660a08601521660c084015260e0830152610100820152610120016102bd565b348015610a6157600080fd5b506102db610a703660046151d1565b61295e565b348015610a8157600080fd5b506006546103219063ffffffff1681565b348015610a9e57600080fd5b506102db610aad3660046153fa565b6129f5565b348015610abe57600080fd5b506006546106dd90600160401b900461ffff1681565b348015610ae057600080fd5b50610b18610aef36600461513b565b6001600160801b031660009081526002602081905260409091200154600160881b900460ff1690565b60405190151581526020016102bd565b348015610b3457600080fd5b50600954610b1890600160a01b900460ff1681565b348015610b5557600080fd5b506102db610b64366004615417565b612a79565b348015610b7557600080fd5b506104a5610b843660046151d1565b60016020526000908152604090205481565b348015610ba257600080fd5b506102db610bb1366004615443565b61312e565b348015610bc257600080fd5b50610bd6610bd13660046153d1565b613172565b6040516102bd9190615468565b348015610bef57600080fd5b506103d57f000000000000000000000000000000000000000000000000000000000000000081565b348015610c2357600080fd5b506103216103e881565b348015610c3957600080fd5b50610321620b71b081565b348015610c5057600080fd5b50600a546103d5906001600160a01b031681565b348015610c7057600080fd5b506104a5610c7f3660046151d1565b60056020526000908152604090205481565b348015610c9d57600080fd5b506102db610cac3660046151d1565b61321f565b348015610cbd57600080fd5b506102db610ccc3660046151d1565b613294565b600954600160a01b900460ff1615610cfc57604051637856112960e11b815260040160405180910390fd5b6000546001600160a01b03163314610d2f5760405162461bcd60e51b8152600401610d2690615521565b60405180910390fd5b6009805460ff60a01b1916600160a01b179055604051600181527fb8527b93c36dabdfe078af41be789ba946a4adcfeafcf9d8de21d51629859e3c906020015b60405180910390a1565b6000546001600160a01b03163314610da35760405162461bcd60e51b8152600401610d2690615521565b6103e863ffffffff82161115610dcc5760405163eec915d760e01b815260040160405180910390fd5b6006805463ffffffff909216600160201b0267ffffffff0000000019909216919091179055565b630a85bd0160e11b5b949350505050565b6001600160801b0382166000908152600260208190526040822090810154829190600160881b900460ff16610e575760405163327c783360e01b81526001600160801b0386166004820152602401610d26565b6001600160801b0380861660009081526007602090815260408083206001600160a01b038916845290915281205490911690819003610ec35760405163775c73bf60e01b81526001600160a01b03861660048201526001600160801b0387166024820152604401610d26565b6001600160801b03861660009081526007602090815260408083206001600160a01b0389168452909152812080546001600160801b0319169055600283015481908190600160801b900460ff1615610f5857505050600182015482546001600160a01b03908116600090815260036020819052604090912001546001600160801b0380841693600160801b9004169116610f95565b505050600182015482546001600160a01b039081166000908152600360205260409020600201546001600160801b03600160801b84048116931691165b600083610fa2868561555d565b610fac9190615592565b9050610fc26001600160a01b0383168a836132e0565b600286015433906001600160801b03163410611014576002870154600090610ff3906001600160801b0316346155a6565b9050801561100e5761100e6001600160a01b0383168261335e565b50611073565b600287015461105a906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690839030906001600160801b03166133b4565b3415611073576110736001600160a01b0382163461335e565b604080516001600160a01b038c1681526001600160801b038d1660208201529081018390527f7694d48c5e21d47a725e1e62a0159727e60d57f1e42d9f83e30199fffaefc8ac9060600160405180910390a1509096509450505050505b9250929050565b6000546001600160a01b031633146111015760405162461bcd60e51b8152600401610d2690615521565b620b71b063ffffffff8216111561112b576040516301eadab160e31b815260040160405180910390fd5b6006805463ffffffff191663ffffffff92909216919091179055565b6000546001600160a01b031633146111715760405162461bcd60e51b8152600401610d2690615521565b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038481166000818152600360208181526040808420815160a0810183528154815260018201548185015260028201548816818401529301549586166060840152600160a01b90950462ffffff16608083015284516334324e9f60e21b8152945192948594929385939263d0c93a7c92600480820193918290030181865afa158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d91906155cf565b905061125981876155ec565b60020b15611288576040516327deee8960e21b8152600287810b600483015282900b6024820152604401610d26565b6112948288888861343e565b90999098509650505050505050565b600a546000906001600160a01b03161561138057600080600a60009054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561130d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113319190615628565b509350509250506000814261134691906155a6565b9050611c208111156113785760065461137090600160201b900463ffffffff16633b9aca0061555d565b935050505090565b509092915050565b60065461139e90600160201b900463ffffffff16633b9aca0061555d565b905090565b600080806113b384860186615678565b6001600160a01b038084166000908152600360205260409020600201549396509194509250166114015760405163d495675960e01b81526001600160a01b0384166004820152602401610d26565b6001600160a01b0383166000908152600360205260408120906114226112a3565b600654611435919063ffffffff1661555d565b90506000856001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611477573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149b91906156b9565b5050505050915050600080866114b55784600101546114b8565b84545b905060005b600654600160401b900461ffff168110156115ed5781156115ed576000828152600860205260408120805490919061150f908790600160201b8104600290810b91610100810490910b9060ff16613890565b905060008160028111156115255761152561575b565b036115d357611537848c848a8d61392d565b8961154657816003015461154c565b81600201545b6002808401805460038087018054600090815260086020908152604080832088018690558354958352808320909401949094559384905583905586546001600160801b03600160781b90910416835290839052902001805460ff60881b1916600160881b1790558254670100000000000000600160f81b03191683556001955093506115da565b50506115ed565b5050806115e690615771565b90506114bd565b508161160c57604051630f72fa0160e11b815260040160405180910390fd5b861561161a57808555611622565b600185018190555b50505050505050505050565b6000546001600160a01b031633146116585760405162461bcd60e51b8152600401610d2690615521565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156116bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e3919061578a565b905047811580156116f2575080155b15611710576040516336f53c8f60e21b815260040160405180910390fd5b811561175057600054611750906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169116846132e0565b801561176c5760005461176c906001600160a01b03168261335e565b5050565b600954600090600160a01b900460ff161561179e57604051637856112960e11b815260040160405180910390fd5b6001600160a01b03878116600090815260036020526040902060020154166117e45760405163d495675960e01b81526001600160a01b0388166004820152602401610d26565b33600085611810576001600160a01b03808a16600090815260036020819052604090912001541661182f565b6001600160a01b03808a16600090815260036020526040902060020154165b9050611844876001600160801b031682613aa6565b6118626001600160a01b03821683306001600160801b038b166133b4565b506040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152886001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156118da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fe91906156b9565b5050505060029190910b83525050604080516334324e9f60e21b815290516000916001600160a01b038c169163d0c93a7c916004808201926020929091908290030181865afa158015611955573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197991906155cf565b9050611985818a6155ec565b60020b156119b4576040516327deee8960e21b815260028a810b600483015282900b6024820152604401610d26565b86156119db57600289900b60208301526119ce818a6157a3565b60020b60408301526119f8565b6119e5818a6157c8565b600290810b602084015289900b60408301525b506000611a138260000151836040015184602001518a613890565b90506001816002811115611a2957611a2961575b565b14611a6057815160405163ea7469e760e01b8152600291820b6004820152908a900b60248201528715156044820152606401610d26565b506001600160a01b038916600090815260046020908152604080832089158015855290835281842085830151600290810b86529084528285208685015190910b8552909252909120546080830152611ac6576001600160801b03871660a0820152611ad6565b6001600160801b03871660c08201525b8060800151600003611c6d576001600160a01b03898116600090815260036020818152604092839020835160a0808201865282548252600183015482850152600283015487168287015291909301549485166060840152600160a01b90940462ffffff166080830152840151918401519284015160c08501519193611b619385939092908c8b613b25565b60808301819052611b7890829088908c908b613f4b565b604080830151608084018051600090815260086020908152848220805462ffffff958616600160201b0266ffffff0000000019909116179055860151825182529390208054939092166101000263ffffff001990931692909217905551611be0908890613fb7565b611bef8260800151848a614048565b6001600160801b031660608301526080820151825160208401516040850151611c1d938e93909290916141df565b5060808101516001600160a01b038a1660009081526004602090815260408083208a15158452825280832081860151600290810b85529083528184208387015190910b8452909152902055611ee5565b608080820151600090815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b909104811695820195909552600182015480861660a08301819052600160801b90910490951660c08201529181015460e0830152600301549181019190915290151580611d36575060008160c001516001600160801b0316115b15611e05578051151587151514611d605760405163e86e44c160e01b815260040160405180910390fd5b6001600160a01b038a8116600090815260036020818152604092839020835160a08082018652825482526001830154938201939093526002820154861694810194909452909101549283166060830152600160a01b90920462ffffff166080808301919091528401519184015160c08501519192611de2928492908c8b61445f565b611df18360800151858b614048565b6001600160801b0316606084015250611ee3565b6001600160a01b038a8116600090815260036020818152604092839020835160a0810185528154815260018201549281019290925260028101548516938201939093529101549182166060820152600160a01b90910462ffffff16608080830191909152830151611e7c90829089908d908c613f4b565b611e8a888460800151613fb7565b611ea48184608001518560a001518660c001518c8b61445f565b611eb38360800151858b614048565b6001600160801b031660608401526080830151835160208501516040860151611ee1938f93909290916141df565b505b505b7f559322b66708f28c8fe1386f8cd96634d0c2fdb250d925b71a6c15e11e26069c828a8984606001516008600087608001518152602001908152602001600020604051611f36959493929190615873565b60405180910390a160800151600090815260086020526040902054600160781b90046001600160801b03169150505b9695505050505050565b6000546001600160a01b03163314611f995760405162461bcd60e51b8152600401610d2690615521565b600954600160a01b900460ff16611fc357604051632960b58960e11b815260040160405180910390fd5b6009805460ff60a01b19169055604051600081527fb8527b93c36dabdfe078af41be789ba946a4adcfeafcf9d8de21d51629859e3c90602001610d6f565b6000606081612012848601866151d1565b90506000816001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015612054573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207891906156b9565b505050506001600160a01b03858116600090815260036020818152604092839020835160a0810185528154815260018201549281019290925260028101548516938201939093529101549182166060820152600160a01b90910462ffffff1660808201529193509091506120ec9050614f41565b600080806120fc426103846158b0565b85519091501561221b578451600090815260086020908152604091829020825161012081018452815460ff81161515808352610100808304600290810b968501879052600160201b8404810b978501889052600160381b84046001600160401b03166060860152600160781b9093046001600160801b039081166080860152600186015480821660a0870152600160801b90041660c08501529184015460e08401526003909301549082015296506121b79289929091613890565b925060008360028111156121cd576121cd61575b565b0361221b57604080516001600160a01b03891660208201526001918101829052606081018390529099508992506080015b6040516020818303038152906040529750505050505050506110d0565b60208501511561232257602085810151600090815260088252604090819020815161012081018352815460ff81161515808352610100808304600290810b978501889052600160201b8404810b968501879052600160381b84046001600160401b03166060860152600160781b9093046001600160801b039081166080860152600186015480821660a0870152600160801b90041660c08501529184015460e08401526003909301549082015296506122d692899291613890565b925060008360028111156122ec576122ec61575b565b0361232257604080516001600160a01b038916602082015260009181018290526060810183905260019a509092506080016121fe565b60408051600060208201819052910160405160208183030381529060405298509850505050505050509250929050565b6000806000806000886001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015612398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bc91906156b9565b505050505091505060008060008b6001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612407573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061242b91906155cf565b9050612437818c6155ec565b60020b15612466576040516327deee8960e21b815260028c810b600483015282900b6024820152604401610d26565b8915612480578a925061247981846157a3565b9150612490565b61248a818c6157c8565b92508a91505b50600061249f8483858d613890565b905060018160028111156124b5576124b561575b565b146124e95760405163ea7469e760e01b8152600285810b60048301528c900b60248201528a15156044820152606401610d26565b506001600160a01b038b1660009081526004602090815260408083208c151584528252808320600285810b85529083528184209086900b8452909152812054945084900361254a576040516327bd01f960e21b815260040160405180910390fd5b505050600081815260086020526040812080549091600160781b9091046001600160801b0316900361258f5760405163d17ecf4960e01b815260040160405180910390fd5b8054600160781b90046001600160801b03908116600081815260076020908152604080832033808552925282205492965090929091168083036125ff5760405163775c73bf60e01b81526001600160a01b03831660048201526001600160801b0387166024820152604401610d26565b8354600160381b90046001600160401b031684600761261d836158c3565b82546001600160401b039182166101009390930a9283029190920219909116179055506001600160801b03861660009081526007602090815260408083206001600160a01b0386168452909152812080546001600160801b0319169055845460ff1615612724575060018401546001600160801b0390811690821681036126c0576001850180546001600160801b0319169055670de0b6b3a764000093506127c3565b806001600160801b0316826001600160801b0316670de0b6b3a76400006126e7919061555d565b6126f19190615592565b93506126fd82826158e6565b6001860180546001600160801b0319166001600160801b03929092169190911790556127c3565b5060018401546001600160801b03600160801b90910481169082168103612766576001850180546001600160801b03169055670de0b6b3a764000093506127c3565b806001600160801b0316826001600160801b0316670de0b6b3a764000061278d919061555d565b6127979190615592565b93506127a382826158e6565b6001860180546001600160801b03928316600160801b0292169190911790555b6127cf868e868d614816565b604051919a5098507f61e6e7599d7fb0402072db6fa40efca86866f1c441dc2515a79575743ab59cf79061280a9085908c908c908a9061590d565b60405180910390a183670de0b6b3a76400000361284e5761282c868e87614db6565b6000600186015584546fffffffffffffffffffffffffffffffff60781b191685555b5050825460ff16156128e6576001600160801b038716156128a0576001600160a01b03808c1660009081526003602052604090206002015461289b9116826001600160801b038a166132e0565b6128b9565b604051634a68d63560e11b815260040160405180910390fd5b6001600160801b038616156128e1576040516395f95a0760e01b815260040160405180910390fd5b612950565b6001600160801b038616156128a0576001600160a01b03808c16600090815260036020819052604090912001546129289116826001600160801b0389166132e0565b6001600160801b03871615612950576040516395f95a0760e01b815260040160405180910390fd5b505050509450945094915050565b6000546001600160a01b031633146129885760405162461bcd60e51b8152600401610d2690615521565b6001600160a01b038116600090815260016020526040812054908190036129cd5760405163117373e160e01b81526001600160a01b0383166004820152602401610d26565b6001600160a01b0380831660008181526001602052604081208190555461176c9216836132e0565b6000546001600160a01b03163314612a1f5760405162461bcd60e51b8152600401610d2690615521565b61ffff81161580612a345750601461ffff8216115b15612a52576040516394aa3c9160e01b815260040160405180910390fd5b6006805461ffff909216600160401b0269ffff000000000000000019909216919091179055565b6000546001600160a01b03163314612aa35760405162461bcd60e51b8152600401610d2690615521565b6001600160a01b038281166000908152600360205260409020600201541615612aea57604051635edc3a1b60e11b81526001600160a01b0383166004820152602401610d26565b8015612f1c57600054612b2b906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911630846133b4565b60408051808201825260018152603160f81b6020909101819052600954825163181f5a7760e01b8152925191926001600160a01b039091169163181f5a77916004808201926000929091908290030181865afa158015612b8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612bb79190810190615961565b601081518110612bc957612bc961594b565b01602001516001600160f81b03191603612d55576000604051806080016040528060488152602001615f216048913980516020918201206040805160008185015281518082039094018452810190526006549091903090612c3c9063ffffffff811690600160401b900461ffff166159ce565b600054604080516001600160a01b038a81166020830152909216910160408051601f1981840301815290829052612c8095949392918990604d9030906024016159fa565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092526009549151630200057560e51b81529092506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811692634000aea092612d0b92919091169086908690600401615ab1565b6020604051808303816000875af1158015612d2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d4e9190615ad8565b5050612f1c565b600954612d8f906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683614e7a565b604080516101408101825260146101008201908152734c696d6974204f7264657220526567697374727960601b61012083015281528151600060208083018290529093908301910160408051808303601f19018152918152908252306020830152600654910190612e129063ffffffff811690600160401b900461ffff166159ce565b63ffffffff1681526000546001600160a01b031660208083019190915260408051920191612e51918791016001600160a01b0391909116815260200190565b60405160208183030381529060405281526020016000604051602001612e80919060ff91909116815260200190565b60408051808303601f190181529181529082526001600160601b038516602090920191909152600954905163022de76960e21b81529192506001600160a01b0316906308b79da490612ed6908490600401615af5565b6020604051808303816000875af1158015612ef5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f19919061578a565b50505b6040518060a001604052806000815260200160008152602001836001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f979190615be3565b6001600160a01b03168152602001836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fe3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130079190615be3565b6001600160a01b03168152602001836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015613053573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130779190615c13565b62ffffff9081169091526001600160a01b0384811660008181526003602081815260409283902087518155878201516001820155878401516002820180549188166001600160a01b0319909216919091179055606088015192018054608090980151909616600160a01b026001600160b81b031990971691909416179490941790925591519081527f250d91a5317f78e9f385385f5e6073f9a759b17cfa6f5d2374163436fbb44ae4910160405180910390a15050565b6000546001600160a01b031633146131585760405162461bcd60e51b8152600401610d2690615521565b6001600160a01b0316600090815260056020526040902055565b61317a614f41565b50600090815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529181015460e0830152600301549181019190915290565b6000546001600160a01b031633146132495760405162461bcd60e51b8152600401610d2690615521565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b6000546001600160a01b031633146132be5760405162461bcd60e51b8152600401610d2690615521565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806133585760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610d26565b50505050565b600080600080600085875af19050806133af5760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610d26565b505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806134375760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610d26565b5050505050565b600080613449614f41565b856000036135db5783801561345e5750865115155b1561350f57508551600081815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529181015460e083015260030154918101919091529095506136b0565b831580156135205750602087015115155b156135ce5750602080870151600081815260088352604090819020815161012081018352815460ff811615158252610100808204600290810b97840197909752600160201b8204870b948301949094526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529381015460e0850152600301549083015295506136b0565b6000809250925050613887565b50600085815260086020908152604091829020825161012081018452815460ff81161515808352610100808304600290810b96850196909652600160201b8304860b96840196909652600160381b82046001600160401b03166060840152600160781b9091046001600160801b039081166080840152600184015480821660a0850152600160801b90041660c08301529282015460e082015260039091015492810192909252841515146136a557604051630eb5001960e41b815260048101879052602401610d26565b6136b0868289614ef1565b855b84156137a1578560020b826040015160020b126136da57610100909101519092509050613887565b8160e001516000036136f3576000935091506138879050565b5060e090810151600081815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529181015494820194909452600390930154908301526136b2565b8560020b826020015160020b136137c15760e09091015192509050613887565b8161010001516000036137db579250600091506138879050565b5061010090810151600081815260086020908152604091829020825161012081018452815460ff811615158252868104600290810b94830194909452600160201b8104840b94820194909452600160381b84046001600160401b03166060820152600160781b9093046001600160801b039081166080850152600182015480821660a0860152600160801b90041660c08401529081015460e083015260030154928101929092526136b2565b94509492505050565b60008360020b8360020b036138c957604051600162993c7b60e01b03198152600284810b600483015285900b6024820152604401610d26565b8115613902578260020b8560020b13156138e557506000610dfc565b8360020b8560020b126138fa57506002610dfc565b506001610dfc565b8360020b8560020b121561391857506000610dfc565b8260020b8560020b136138fa57506002610dfc565b8254600160781b81046001600160801b03166000908152600260205260409020600160381b9091046001600160401b0316906139698285615592565b6002820180546001600160801b0319166001600160801b039290921691909117905580546001600160a01b0319166001600160a01b0387161781556000806139bb8989670de0b6b3a764000088614816565b8854919350915060ff16156139ed576001808801546001600160801b03838116600160801b0291161790840155613a1d565b6001878101546001600160801b0319600160801b918290046001600160801b039081169092021690841617908401555b865460028401805460ff9092161515600160801b0260ff60801b1990921691909117905560006001880155865460408051600160781b9092046001600160801b031682526001600160a01b038a1660208301527f74ebbab24ee8774729982ac100f931306b160f431f7f1483cc380186d56754bc910160405180910390a1505050505050505050565b6001600160a01b03811660009081526005602052604081205490819003613aeb57604051630b90e8fb60e21b81526001600160a01b0383166004820152602401610d26565b808310156133af576040516360d7e97160e01b81526001600160a01b03831660048201526024810182905260448101849052606401610d26565b60008215613b74576040880151613b6f906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b038816614e7a565b613bb6565b6060880151613bb6906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b038716614e7a565b60006001600160801b03861615613be557612710613bd68761270f615c2e565b613be09190615c54565b613be8565b60005b905060006001600160801b03861615613c1957612710613c0a8761270f615c2e565b613c149190615c54565b613c1c565b60005b905060006040518061016001604052808c604001516001600160a01b031681526020018c606001516001600160a01b031681526020018c6080015162ffffff1681526020018a60020b81526020018b60020b8152602001896001600160801b03168152602001886001600160801b03168152602001846001600160801b03168152602001836001600160801b03168152602001306001600160a01b0316815260200186815250905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166388316456836040518263ffffffff1660e01b8152600401613d129190615c7a565b6080604051808303816000875af1158015613d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d559190615d49565b505050905080600003613d7b576040516327bd01f960e21b815260040160405180910390fd5b868015613e1c575060408c8101519051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152600092169063dd62ed3e90604401602060405180830381865afa158015613df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e1a919061578a565b115b15613e5b5760408c0151613e5b906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000614e7a565b86158015613efd575060608c0151604051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152600092169063dd62ed3e90604401602060405180830381865afa158015613ed7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613efb919061578a565b115b15613f3c5760608c0151613f3c906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000614e7a565b9b9a5050505050505050505050565b600080613f5a8787878661343e565b9150915080600014613f875760008181526008602052604080822060020186905585825290206003018190555b8115613fae5760008281526008602052604080822060030186905585825290206002018290555b50505050505050565b600081815260086020526040902060068054600160501b90046001600160801b031690600a613fe583615d87565b82546101009290920a6001600160801b0381810219909316918316021790915582547fff00000000000000000000000000000000ffffffffffffffffffffffffffff0016600160781b929091169190910260ff1916179215159290921790915550565b6000838152600860205260408120805460ff16156140a9576001810180548491906000906140809084906001600160801b0316615dad565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506140f4565b828160010160108282829054906101000a90046001600160801b03166140cf9190615dad565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b80546001600160801b03600160781b909104811660008181526007602090815260408083206001600160a01b038a1684529091528120549192919091169081900361417d578254600160381b90046001600160401b031683600761415783615dcd565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505b6141878582615dad565b6001600160801b0383811660009081526007602090815260408083206001600160a01b038c168452909152902080546001600160801b031916929091169190911790556141d48582615dad565b979650505050505050565b6001600160a01b03858116600090815260036020818152604092839020835160a08101855281548152600182015492810192909252600280820154861694830194909452909101549283166060820152600160a01b90920462ffffff16608083015283810b9085900b131561435357806020015160000361427d576001600160a01b0386166000908152600360205260409020600101859055614457565b602081810151600090815260088252604090819020815161012081018352815460ff811615158252610100808204600290810b968401879052600160201b8304810b95840195909552600160381b82046001600160401b03166060840152600160781b9091046001600160801b039081166080840152600184015480821660a0850152600160801b90041660c08301528383015460e083015260039092015491810191909152919085900b131561434d576001600160a01b03871660009081526003602052604090206001018690555b50614457565b8160020b8460020b121561445757805160000361438a576001600160a01b0386166000908152600360205260409020859055614457565b8051600090815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301869052600160381b82046001600160401b03166060840152600160781b9091046001600160801b039081166080840152600184015480821660a0850152600160801b90041660c08301528383015460e083015260039092015491810191909152919084900b1215613fae576001600160a01b0387166000908152600360205260409020869055505b505050505050565b81156144ac5760408601516144a7906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b038716614e7a565b6144ee565b60608601516144ee906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b038616614e7a565b60006001600160801b0385161561451d5761271061450e8661270f615c2e565b6145189190615c54565b614520565b60005b905060006001600160801b03851615614551576127106145428661270f615c2e565b61454c9190615c54565b614554565b60005b6040805160c0810182528981526001600160801b0389811660208301908152898216838501908152878316606085019081529286166080850190815260a085018a8152955163219f5d1760e01b81528551600482015292516024840152905160448301529151606482015290516084820152915160a48301529192507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063219f5d179060c4016060604051808303816000875af1158015614623573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146479190615de9565b5050508480156146eb57506040898101519051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152600092169063dd62ed3e90604401602060405180830381865afa1580156146c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146e9919061578a565b115b1561472a57604089015161472a906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000614e7a565b841580156147cc57506060890151604051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152600092169063dd62ed3e90604401602060405180830381865afa1580156147a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147ca919061578a565b115b1561480b57606089015161480b906001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006000614e7a565b505050505050505050565b60008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166399fbab88886040518263ffffffff1660e01b815260040161486991815260200190565b61018060405180830381865afa158015614887573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148ab9190615e20565b50505050975050505050505050670de0b6b3a764000085826001600160801b03166148d6919061555d565b6148e09190615592565b6040805160a0810182528981526001600160801b0380841660208301908152600083850181815260608501828152608086018c81529651630624e65f60e11b81528651600482015293519094166024840152516044830152915160648201529251608484015292935091908190819081906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630c49ccbe9060a40160408051808303816000875af11580156149a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149c79190615efc565b604080516080810182526000808252602082018190529181018290526060810191909152919550935091506149f99050565b6000808a670de0b6b3a764000003614a1c57506001600160801b03905080614a22565b50839050825b604080516080810182528e81523060208083018290526001600160801b03958616838501529390941660608201526001600160a01b038e81166000908152600394859052838120600281015495015493516370a0823160e01b815260048101969096529195509283169391909216919083906370a0823190602401602060405180830381865afa158015614aba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ade919061578a565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa158015614b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b4c919061578a565b6040805163fc6f786560e01b81528751600482015260208801516001600160a01b039081166024830152918801516001600160801b03908116604483015260608901511660648201529192507f0000000000000000000000000000000000000000000000000000000000000000169063fc6f78659060840160408051808303816000875af1158015614be2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c069190615efc565b50506040516370a0823160e01b8152306004820152600090889084906001600160a01b038816906370a0823190602401602060405180830381865afa158015614c53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c77919061578a565b614c8191906155a6565b614c8b91906158e6565b6040516370a0823160e01b8152306004820152909150600090889084906001600160a01b038816906370a0823190602401602060405180830381865afa158015614cd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cfd919061578a565b614d0791906155a6565b614d1191906158e6565b90506001600160801b03821615614d59576001600160a01b038616600090815260016020526040812080546001600160801b0385169290614d539084906158b0565b90915550505b6001600160801b03811615614d9f576001600160a01b038516600090815260016020526040812080546001600160801b0384169290614d999084906158b0565b90915550505b50969f959e50949c50505050505050505050505050565b6001600160a01b03821660009081526003602052604090208054600190910154818503614e0c576000828152600860209081526040808320600201546001600160a01b0388168452600390925290912055614e40565b808503614e405760008181526008602090815260408083206003908101546001600160a01b03891685529252909120600101555b5050600280820180546003938401805460009081526008602052604080822090950183905581549281529384209094015581905590555050565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806133585760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610d26565b60e0820151158015614f065750610100820151155b156133af5780518314801590614f20575082816020015114155b156133af57604051630eb5001960e41b815260048101849052602401610d26565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b600060208284031215614f9f57600080fd5b813563ffffffff81168114614fb357600080fd5b9392505050565b6001600160a01b0381168114614fcf57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561501057615010614fd2565b604052919050565b60006001600160401b0382111561503157615031614fd2565b50601f01601f191660200190565b6000806000806080858703121561505557600080fd5b843561506081614fba565b9350602085013561507081614fba565b92506040850135915060608501356001600160401b0381111561509257600080fd5b8501601f810187136150a357600080fd5b80356150b66150b182615018565b614fe8565b8181528860208385010111156150cb57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6001600160801b0381168114614fcf57600080fd5b6000806040838503121561511557600080fd5b8235615120816150ed565b9150602083013561513081614fba565b809150509250929050565b60006020828403121561514d57600080fd5b8135614fb3816150ed565b8015158114614fcf57600080fd5b8060020b8114614fcf57600080fd5b6000806000806080858703121561518b57600080fd5b843561519681614fba565b935060208501356151a681615158565b925060408501356151b681615166565b915060608501356151c681615166565b939692955090935050565b6000602082840312156151e357600080fd5b8135614fb381614fba565b6000806000806080858703121561520457600080fd5b843561520f81614fba565b935060208501359250604085013561522681615166565b915060608501356151c681615158565b6000806020838503121561524957600080fd5b82356001600160401b038082111561526057600080fd5b818501915085601f83011261527457600080fd5b81358181111561528357600080fd5b86602082850101111561529557600080fd5b60209290920196919550909350505050565b60008060008060008060c087890312156152c057600080fd5b86356152cb81614fba565b955060208701356152db81615166565b945060408701356152eb816150ed565b935060608701356152fb81615158565b9598949750929560808101359460a0909101359350915050565b60005b83811015615330578181015183820152602001615318565b50506000910152565b60008151808452615351816020860160208601615315565b601f01601f19169290920160200192915050565b8215158152604060208201526000610dfc6040830184615339565b6000806000806080858703121561539657600080fd5b84356153a181614fba565b935060208501356153b181615166565b925060408501356153c181615158565b9396929550929360600135925050565b6000602082840312156153e357600080fd5b5035919050565b61ffff81168114614fcf57600080fd5b60006020828403121561540c57600080fd5b8135614fb3816153ea565b6000806040838503121561542a57600080fd5b823561543581614fba565b946020939093013593505050565b6000806040838503121561545657600080fd5b82359150602083013561513081614fba565b600061012082019050825115158252602083015160020b60208301526040830151615498604084018260020b9052565b5060608301516154b360608401826001600160401b03169052565b5060808301516154ce60808401826001600160801b03169052565b5060a08301516154e960a08401826001600160801b03169052565b5060c083015161550460c08401826001600160801b03169052565b5060e083015160e083015261010080840151818401525092915050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561557757615577615547565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826155a1576155a161557c565b500490565b818103818111156155b9576155b9615547565b92915050565b80516155ca81615166565b919050565b6000602082840312156155e157600080fd5b8151614fb381615166565b60008260020b806155ff576155ff61557c565b808360020b0791505092915050565b805169ffffffffffffffffffff811681146155ca57600080fd5b600080600080600060a0868803121561564057600080fd5b6156498661560e565b945060208601519350604086015192506060860151915061566c6080870161560e565b90509295509295909350565b60008060006060848603121561568d57600080fd5b833561569881614fba565b925060208401356156a881615158565b929592945050506040919091013590565b600080600080600080600060e0888a0312156156d457600080fd5b87516156df81614fba565b60208901519097506156f081615166565b6040890151909650615701816153ea565b6060890151909550615712816153ea565b6080890151909450615723816153ea565b60a089015190935060ff8116811461573a57600080fd5b60c089015190925061574b81615158565b8091505092959891949750929550565b634e487b7160e01b600052602160045260246000fd5b60006001820161578357615783615547565b5060010190565b60006020828403121561579c57600080fd5b5051919050565b600282810b9082900b03627fffff198112627fffff821317156155b9576155b9615547565b600281810b9083900b01627fffff8113627fffff19821217156155b9576155b9615547565b805460ff811615158352600881901c60020b6020840152602081901c60020b6040840152603881901c6001600160401b031660608401526001600160801b03607882901c8116608085015260018301546001600160801b038282161660a0860152915050608081901c60c084015250600281015460e08301526003015461010090910152565b6001600160a01b038681168252851660208201526001600160801b038481166040830152831660608201526101a08101611f6560808301846157ed565b808201808211156155b9576155b9615547565b60006001600160401b038216806158dc576158dc615547565b6000190192915050565b6001600160801b0382811682821603908082111561590657615906615547565b5092915050565b6001600160a01b03851681526001600160801b03848116602083015283166040820152610180810161594260608301846157ed565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561597357600080fd5b81516001600160401b0381111561598957600080fd5b8201601f8101841361599a57600080fd5b80516159a86150b182615018565b8181528560208385010111156159bd57600080fd5b615942826020830160208601615315565b600063ffffffff808316818516818304811182151516156159f1576159f1615547565b02949350505050565b600061012080835260148184015250734c696d6974204f7264657220526567697374727960601b610140830152610160806020840152615a3c8184018c615339565b6001600160a01b038b8116604086015263ffffffff8b1660608601528916608085015283810360a08501529050615a738188615339565b9150506001600160601b03851660c083015260ff841660e0830152615aa46101008301846001600160a01b03169052565b9998505050505050505050565b60018060a01b03841681528260208201526060604082015260006159426060830184615339565b600060208284031215615aea57600080fd5b8151614fb381615158565b6020815260008251610100806020850152615b14610120850183615339565b91506020850151601f1980868503016040870152615b328483615339565b935060408701519150615b5060608701836001600160a01b03169052565b606087015163ffffffff81166080880152915060808701516001600160a01b03811660a0880152915060a08701519150808685030160c0870152615b948483615339565b935060c08701519150808685030160e087015250615bb28382615339565b92505060e0850151615bce828601826001600160601b03169052565b5090949350505050565b80516155ca81614fba565b600060208284031215615bf557600080fd5b8151614fb381614fba565b805162ffffff811681146155ca57600080fd5b600060208284031215615c2557600080fd5b614fb382615c00565b60006001600160801b03808316818516818304811182151516156159f1576159f1615547565b60006001600160801b0380841680615c6e57615c6e61557c565b92169190910492915050565b81516001600160a01b0316815261016081016020830151615ca660208401826001600160a01b03169052565b506040830151615cbd604084018262ffffff169052565b506060830151615cd2606084018260020b9052565b506080830151615ce7608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151615d2d828501826001600160a01b03169052565b505061014092830151919092015290565b80516155ca816150ed565b60008060008060808587031215615d5f57600080fd5b845193506020850151615d71816150ed565b6040860151606090960151949790965092505050565b60006001600160801b03808316818103615da357615da3615547565b6001019392505050565b6001600160801b0381811683821601908082111561590657615906615547565b60006001600160401b03808316818103615da357615da3615547565b600080600060608486031215615dfe57600080fd5b8351615e09816150ed565b602085015160409095015190969495509392505050565b6000806000806000806000806000806000806101808d8f031215615e4357600080fd5b8c516001600160601b0381168114615e5a57600080fd5b9b50615e6860208e01615bd8565b9a50615e7660408e01615bd8565b9950615e8460608e01615bd8565b9850615e9260808e01615c00565b9750615ea060a08e016155bf565b9650615eae60c08e016155bf565b9550615ebc60e08e01615d3e565b94506101008d015193506101208d01519250615edb6101408e01615d3e565b9150615eea6101608e01615d3e565b90509295989b509295989b509295989b565b60008060408385031215615f0f57600080fd5b50508051602090910151909290915056fe726567697374657228737472696e672c62797465732c616464726573732c75696e7433322c616464726573732c62797465732c75696e7439362c75696e74382c6164647265737329a2646970667358221220050050e9eeef002bff0490b18116493419b20249972b8e2abbd9ef060d3e423f64736f6c63430008100033000000000000000000000000e75358526ef4441db03ccaeb9a87f180fae80eb9000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f280000000000000000000000004300000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x60806040526004361061027d5760003560e01c80638ac9972f1161014f578063bf86d690116100c1578063e3bbb4f11161007a578063e3bbb4f114610c17578063e3f5aa5114610c2d578063ec81205514610c44578063ed12b33514610c64578063f2fde38b14610c91578063faab9d3914610cb157600080fd5b8063bf86d69014610b28578063c9d6e63014610b49578063cd3547b414610b69578063cda5197614610b96578063d368f97614610bb6578063d999984d14610be357600080fd5b8063a20b6e3411610113578063a20b6e341461096a578063a31d1b7614610a55578063a9ea1f8014610a75578063b17a24ae14610a92578063b1e057a414610ab2578063b752ceb214610ad457600080fd5b80638ac9972f146106f05780638da5cb5b1461073a5780638e06ffd51461075a578063935f450e1461079b57806397bb2b83146108d257600080fd5b80632c1f969e116101f357806350431ce4116101ac57806350431ce41461059357806353410e7b146105a85780635e2c576e146105c857806360e72adb146105dd5780636e04ff0d1461069a578063713b9dc1146106c857600080fd5b80632c1f969e146104d357806330e7c992146104e95780633cccf2291461050957806343854af514610529578063455259cb1461055e5780634585e33b1461057357600080fd5b8063151da32e11610245578063151da32e1461036f5780631b6b6d23146103a15780631bea83fe146103ed5780631c4c4d56146104215780631dd969d21461045e5780632b20e397146104b357600080fd5b806306f13056146102825780630a680e18146102c65780630cc2106d146102dd5780631467feac146102fd578063150b7a0214610336575b600080fd5b34801561028e57600080fd5b506006546102a990600160501b90046001600160801b031681565b6040516001600160801b0390911681526020015b60405180910390f35b3480156102d257600080fd5b506102db610cd1565b005b3480156102e957600080fd5b506102db6102f8366004614f8d565b610d79565b34801561030957600080fd5b5060065461032190600160201b900463ffffffff1681565b60405163ffffffff90911681526020016102bd565b34801561034257600080fd5b5061035661035136600461503f565b610df3565b6040516001600160e01b031990911681526020016102bd565b61038261037d366004615102565b610e04565b604080516001600160a01b0390931683526020830191909152016102bd565b3480156103ad57600080fd5b506103d57f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102bd565b3480156103f957600080fd5b506103d57f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f2881565b34801561042d57600080fd5b506102a961043c36600461513b565b6001600160801b03908116600090815260026020819052604090912001541690565b34801561046a57600080fd5b506104a5610479366004615175565b600460209081526000948552604080862082529385528385208152918452828420909152825290205481565b6040519081526020016102bd565b3480156104bf57600080fd5b506009546103d5906001600160a01b031681565b3480156104df57600080fd5b506104a5611c2081565b3480156104f557600080fd5b506102db610504366004614f8d565b6110d7565b34801561051557600080fd5b506102db6105243660046151d1565b611147565b34801561053557600080fd5b506105496105443660046151ee565b611193565b604080519283526020830191909152016102bd565b34801561056a57600080fd5b506104a56112a3565b34801561057f57600080fd5b506102db61058e366004615236565b6113a3565b34801561059f57600080fd5b506102db61162e565b3480156105b457600080fd5b506102a96105c33660046152a7565b611770565b3480156105d457600080fd5b506102db611f6f565b3480156105e957600080fd5b506106516105f836600461513b565b60026020819052600091825260409091208054600182015491909201546001600160a01b03909216916001600160801b0380831692600160801b908190048216929182169160ff918104821691600160881b9091041686565b604080516001600160a01b0390971687526001600160801b03958616602088015293851693860193909352921660608401529015156080830152151560a082015260c0016102bd565b3480156106a657600080fd5b506106ba6106b5366004615236565b612001565b6040516102bd929190615365565b3480156106d457600080fd5b506106dd601481565b60405161ffff90911681526020016102bd565b3480156106fc57600080fd5b5061071061070b366004615380565b612352565b604080516001600160801b03948516815292841660208401529216918101919091526060016102bd565b34801561074657600080fd5b506000546103d5906001600160a01b031681565b34801561076657600080fd5b506102a9610775366004615102565b60076020908152600092835260408084209091529082529020546001600160801b031681565b3480156107a757600080fd5b5061086b6107b636600461513b565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160801b03908116600090815260026020818152604092839020835160c08101855281546001600160a01b03168152600182015480871693820193909352600160801b9283900486169481019490945290910154928316606083015260ff908304811615156080830152600160881b909204909116151560a082015290565b6040516102bd919081516001600160a01b031681526020808301516001600160801b03908116918301919091526040808401518216908301526060808401519091169082015260808083015115159082015260a09182015115159181019190915260c00190565b3480156108de57600080fd5b506109316108ed3660046151d1565b600360208190526000918252604090912080546001820154600283015492909301549092916001600160a01b039081169190811690600160a01b900462ffffff1685565b6040805195865260208601949094526001600160a01b039283169385019390935216606083015262ffffff16608082015260a0016102bd565b34801561097657600080fd5b506109f66109853660046153d1565b60086020526000908152604090208054600182015460028084015460039094015460ff8416946101008504830b94600160201b810490930b936001600160401b03600160381b850416936001600160801b03600160781b90910481169382821693600160801b909304909116919089565b604080519915158a52600298890b60208b01529690970b958801959095526001600160401b0390931660608701526001600160801b039182166080870152811660a08601521660c084015260e0830152610100820152610120016102bd565b348015610a6157600080fd5b506102db610a703660046151d1565b61295e565b348015610a8157600080fd5b506006546103219063ffffffff1681565b348015610a9e57600080fd5b506102db610aad3660046153fa565b6129f5565b348015610abe57600080fd5b506006546106dd90600160401b900461ffff1681565b348015610ae057600080fd5b50610b18610aef36600461513b565b6001600160801b031660009081526002602081905260409091200154600160881b900460ff1690565b60405190151581526020016102bd565b348015610b3457600080fd5b50600954610b1890600160a01b900460ff1681565b348015610b5557600080fd5b506102db610b64366004615417565b612a79565b348015610b7557600080fd5b506104a5610b843660046151d1565b60016020526000908152604090205481565b348015610ba257600080fd5b506102db610bb1366004615443565b61312e565b348015610bc257600080fd5b50610bd6610bd13660046153d1565b613172565b6040516102bd9190615468565b348015610bef57600080fd5b506103d57f000000000000000000000000430000000000000000000000000000000000000481565b348015610c2357600080fd5b506103216103e881565b348015610c3957600080fd5b50610321620b71b081565b348015610c5057600080fd5b50600a546103d5906001600160a01b031681565b348015610c7057600080fd5b506104a5610c7f3660046151d1565b60056020526000908152604090205481565b348015610c9d57600080fd5b506102db610cac3660046151d1565b61321f565b348015610cbd57600080fd5b506102db610ccc3660046151d1565b613294565b600954600160a01b900460ff1615610cfc57604051637856112960e11b815260040160405180910390fd5b6000546001600160a01b03163314610d2f5760405162461bcd60e51b8152600401610d2690615521565b60405180910390fd5b6009805460ff60a01b1916600160a01b179055604051600181527fb8527b93c36dabdfe078af41be789ba946a4adcfeafcf9d8de21d51629859e3c906020015b60405180910390a1565b6000546001600160a01b03163314610da35760405162461bcd60e51b8152600401610d2690615521565b6103e863ffffffff82161115610dcc5760405163eec915d760e01b815260040160405180910390fd5b6006805463ffffffff909216600160201b0267ffffffff0000000019909216919091179055565b630a85bd0160e11b5b949350505050565b6001600160801b0382166000908152600260208190526040822090810154829190600160881b900460ff16610e575760405163327c783360e01b81526001600160801b0386166004820152602401610d26565b6001600160801b0380861660009081526007602090815260408083206001600160a01b038916845290915281205490911690819003610ec35760405163775c73bf60e01b81526001600160a01b03861660048201526001600160801b0387166024820152604401610d26565b6001600160801b03861660009081526007602090815260408083206001600160a01b0389168452909152812080546001600160801b0319169055600283015481908190600160801b900460ff1615610f5857505050600182015482546001600160a01b03908116600090815260036020819052604090912001546001600160801b0380841693600160801b9004169116610f95565b505050600182015482546001600160a01b039081166000908152600360205260409020600201546001600160801b03600160801b84048116931691165b600083610fa2868561555d565b610fac9190615592565b9050610fc26001600160a01b0383168a836132e0565b600286015433906001600160801b03163410611014576002870154600090610ff3906001600160801b0316346155a6565b9050801561100e5761100e6001600160a01b0383168261335e565b50611073565b600287015461105a906001600160a01b037f00000000000000000000000043000000000000000000000000000000000000041690839030906001600160801b03166133b4565b3415611073576110736001600160a01b0382163461335e565b604080516001600160a01b038c1681526001600160801b038d1660208201529081018390527f7694d48c5e21d47a725e1e62a0159727e60d57f1e42d9f83e30199fffaefc8ac9060600160405180910390a1509096509450505050505b9250929050565b6000546001600160a01b031633146111015760405162461bcd60e51b8152600401610d2690615521565b620b71b063ffffffff8216111561112b576040516301eadab160e31b815260040160405180910390fd5b6006805463ffffffff191663ffffffff92909216919091179055565b6000546001600160a01b031633146111715760405162461bcd60e51b8152600401610d2690615521565b600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038481166000818152600360208181526040808420815160a0810183528154815260018201548185015260028201548816818401529301549586166060840152600160a01b90950462ffffff16608083015284516334324e9f60e21b8152945192948594929385939263d0c93a7c92600480820193918290030181865afa158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d91906155cf565b905061125981876155ec565b60020b15611288576040516327deee8960e21b8152600287810b600483015282900b6024820152604401610d26565b6112948288888861343e565b90999098509650505050505050565b600a546000906001600160a01b03161561138057600080600a60009054906101000a90046001600160a01b03166001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561130d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113319190615628565b509350509250506000814261134691906155a6565b9050611c208111156113785760065461137090600160201b900463ffffffff16633b9aca0061555d565b935050505090565b509092915050565b60065461139e90600160201b900463ffffffff16633b9aca0061555d565b905090565b600080806113b384860186615678565b6001600160a01b038084166000908152600360205260409020600201549396509194509250166114015760405163d495675960e01b81526001600160a01b0384166004820152602401610d26565b6001600160a01b0383166000908152600360205260408120906114226112a3565b600654611435919063ffffffff1661555d565b90506000856001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015611477573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061149b91906156b9565b5050505050915050600080866114b55784600101546114b8565b84545b905060005b600654600160401b900461ffff168110156115ed5781156115ed576000828152600860205260408120805490919061150f908790600160201b8104600290810b91610100810490910b9060ff16613890565b905060008160028111156115255761152561575b565b036115d357611537848c848a8d61392d565b8961154657816003015461154c565b81600201545b6002808401805460038087018054600090815260086020908152604080832088018690558354958352808320909401949094559384905583905586546001600160801b03600160781b90910416835290839052902001805460ff60881b1916600160881b1790558254670100000000000000600160f81b03191683556001955093506115da565b50506115ed565b5050806115e690615771565b90506114bd565b508161160c57604051630f72fa0160e11b815260040160405180910390fd5b861561161a57808555611622565b600185018190555b50505050505050505050565b6000546001600160a01b031633146116585760405162461bcd60e51b8152600401610d2690615521565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000043000000000000000000000000000000000000046001600160a01b0316906370a0823190602401602060405180830381865afa1580156116bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e3919061578a565b905047811580156116f2575080155b15611710576040516336f53c8f60e21b815260040160405180910390fd5b811561175057600054611750906001600160a01b037f000000000000000000000000430000000000000000000000000000000000000481169116846132e0565b801561176c5760005461176c906001600160a01b03168261335e565b5050565b600954600090600160a01b900460ff161561179e57604051637856112960e11b815260040160405180910390fd5b6001600160a01b03878116600090815260036020526040902060020154166117e45760405163d495675960e01b81526001600160a01b0388166004820152602401610d26565b33600085611810576001600160a01b03808a16600090815260036020819052604090912001541661182f565b6001600160a01b03808a16600090815260036020526040902060020154165b9050611844876001600160801b031682613aa6565b6118626001600160a01b03821683306001600160801b038b166133b4565b506040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810191909152886001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156118da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fe91906156b9565b5050505060029190910b83525050604080516334324e9f60e21b815290516000916001600160a01b038c169163d0c93a7c916004808201926020929091908290030181865afa158015611955573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197991906155cf565b9050611985818a6155ec565b60020b156119b4576040516327deee8960e21b815260028a810b600483015282900b6024820152604401610d26565b86156119db57600289900b60208301526119ce818a6157a3565b60020b60408301526119f8565b6119e5818a6157c8565b600290810b602084015289900b60408301525b506000611a138260000151836040015184602001518a613890565b90506001816002811115611a2957611a2961575b565b14611a6057815160405163ea7469e760e01b8152600291820b6004820152908a900b60248201528715156044820152606401610d26565b506001600160a01b038916600090815260046020908152604080832089158015855290835281842085830151600290810b86529084528285208685015190910b8552909252909120546080830152611ac6576001600160801b03871660a0820152611ad6565b6001600160801b03871660c08201525b8060800151600003611c6d576001600160a01b03898116600090815260036020818152604092839020835160a0808201865282548252600183015482850152600283015487168287015291909301549485166060840152600160a01b90940462ffffff166080830152840151918401519284015160c08501519193611b619385939092908c8b613b25565b60808301819052611b7890829088908c908b613f4b565b604080830151608084018051600090815260086020908152848220805462ffffff958616600160201b0266ffffff0000000019909116179055860151825182529390208054939092166101000263ffffff001990931692909217905551611be0908890613fb7565b611bef8260800151848a614048565b6001600160801b031660608301526080820151825160208401516040850151611c1d938e93909290916141df565b5060808101516001600160a01b038a1660009081526004602090815260408083208a15158452825280832081860151600290810b85529083528184208387015190910b8452909152902055611ee5565b608080820151600090815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b909104811695820195909552600182015480861660a08301819052600160801b90910490951660c08201529181015460e0830152600301549181019190915290151580611d36575060008160c001516001600160801b0316115b15611e05578051151587151514611d605760405163e86e44c160e01b815260040160405180910390fd5b6001600160a01b038a8116600090815260036020818152604092839020835160a08082018652825482526001830154938201939093526002820154861694810194909452909101549283166060830152600160a01b90920462ffffff166080808301919091528401519184015160c08501519192611de2928492908c8b61445f565b611df18360800151858b614048565b6001600160801b0316606084015250611ee3565b6001600160a01b038a8116600090815260036020818152604092839020835160a0810185528154815260018201549281019290925260028101548516938201939093529101549182166060820152600160a01b90910462ffffff16608080830191909152830151611e7c90829089908d908c613f4b565b611e8a888460800151613fb7565b611ea48184608001518560a001518660c001518c8b61445f565b611eb38360800151858b614048565b6001600160801b031660608401526080830151835160208501516040860151611ee1938f93909290916141df565b505b505b7f559322b66708f28c8fe1386f8cd96634d0c2fdb250d925b71a6c15e11e26069c828a8984606001516008600087608001518152602001908152602001600020604051611f36959493929190615873565b60405180910390a160800151600090815260086020526040902054600160781b90046001600160801b03169150505b9695505050505050565b6000546001600160a01b03163314611f995760405162461bcd60e51b8152600401610d2690615521565b600954600160a01b900460ff16611fc357604051632960b58960e11b815260040160405180910390fd5b6009805460ff60a01b19169055604051600081527fb8527b93c36dabdfe078af41be789ba946a4adcfeafcf9d8de21d51629859e3c90602001610d6f565b6000606081612012848601866151d1565b90506000816001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015612054573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207891906156b9565b505050506001600160a01b03858116600090815260036020818152604092839020835160a0810185528154815260018201549281019290925260028101548516938201939093529101549182166060820152600160a01b90910462ffffff1660808201529193509091506120ec9050614f41565b600080806120fc426103846158b0565b85519091501561221b578451600090815260086020908152604091829020825161012081018452815460ff81161515808352610100808304600290810b968501879052600160201b8404810b978501889052600160381b84046001600160401b03166060860152600160781b9093046001600160801b039081166080860152600186015480821660a0870152600160801b90041660c08501529184015460e08401526003909301549082015296506121b79289929091613890565b925060008360028111156121cd576121cd61575b565b0361221b57604080516001600160a01b03891660208201526001918101829052606081018390529099508992506080015b6040516020818303038152906040529750505050505050506110d0565b60208501511561232257602085810151600090815260088252604090819020815161012081018352815460ff81161515808352610100808304600290810b978501889052600160201b8404810b968501879052600160381b84046001600160401b03166060860152600160781b9093046001600160801b039081166080860152600186015480821660a0870152600160801b90041660c08501529184015460e08401526003909301549082015296506122d692899291613890565b925060008360028111156122ec576122ec61575b565b0361232257604080516001600160a01b038916602082015260009181018290526060810183905260019a509092506080016121fe565b60408051600060208201819052910160405160208183030381529060405298509850505050505050509250929050565b6000806000806000886001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015612398573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bc91906156b9565b505050505091505060008060008b6001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612407573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061242b91906155cf565b9050612437818c6155ec565b60020b15612466576040516327deee8960e21b815260028c810b600483015282900b6024820152604401610d26565b8915612480578a925061247981846157a3565b9150612490565b61248a818c6157c8565b92508a91505b50600061249f8483858d613890565b905060018160028111156124b5576124b561575b565b146124e95760405163ea7469e760e01b8152600285810b60048301528c900b60248201528a15156044820152606401610d26565b506001600160a01b038b1660009081526004602090815260408083208c151584528252808320600285810b85529083528184209086900b8452909152812054945084900361254a576040516327bd01f960e21b815260040160405180910390fd5b505050600081815260086020526040812080549091600160781b9091046001600160801b0316900361258f5760405163d17ecf4960e01b815260040160405180910390fd5b8054600160781b90046001600160801b03908116600081815260076020908152604080832033808552925282205492965090929091168083036125ff5760405163775c73bf60e01b81526001600160a01b03831660048201526001600160801b0387166024820152604401610d26565b8354600160381b90046001600160401b031684600761261d836158c3565b82546001600160401b039182166101009390930a9283029190920219909116179055506001600160801b03861660009081526007602090815260408083206001600160a01b0386168452909152812080546001600160801b0319169055845460ff1615612724575060018401546001600160801b0390811690821681036126c0576001850180546001600160801b0319169055670de0b6b3a764000093506127c3565b806001600160801b0316826001600160801b0316670de0b6b3a76400006126e7919061555d565b6126f19190615592565b93506126fd82826158e6565b6001860180546001600160801b0319166001600160801b03929092169190911790556127c3565b5060018401546001600160801b03600160801b90910481169082168103612766576001850180546001600160801b03169055670de0b6b3a764000093506127c3565b806001600160801b0316826001600160801b0316670de0b6b3a764000061278d919061555d565b6127979190615592565b93506127a382826158e6565b6001860180546001600160801b03928316600160801b0292169190911790555b6127cf868e868d614816565b604051919a5098507f61e6e7599d7fb0402072db6fa40efca86866f1c441dc2515a79575743ab59cf79061280a9085908c908c908a9061590d565b60405180910390a183670de0b6b3a76400000361284e5761282c868e87614db6565b6000600186015584546fffffffffffffffffffffffffffffffff60781b191685555b5050825460ff16156128e6576001600160801b038716156128a0576001600160a01b03808c1660009081526003602052604090206002015461289b9116826001600160801b038a166132e0565b6128b9565b604051634a68d63560e11b815260040160405180910390fd5b6001600160801b038616156128e1576040516395f95a0760e01b815260040160405180910390fd5b612950565b6001600160801b038616156128a0576001600160a01b03808c16600090815260036020819052604090912001546129289116826001600160801b0389166132e0565b6001600160801b03871615612950576040516395f95a0760e01b815260040160405180910390fd5b505050509450945094915050565b6000546001600160a01b031633146129885760405162461bcd60e51b8152600401610d2690615521565b6001600160a01b038116600090815260016020526040812054908190036129cd5760405163117373e160e01b81526001600160a01b0383166004820152602401610d26565b6001600160a01b0380831660008181526001602052604081208190555461176c9216836132e0565b6000546001600160a01b03163314612a1f5760405162461bcd60e51b8152600401610d2690615521565b61ffff81161580612a345750601461ffff8216115b15612a52576040516394aa3c9160e01b815260040160405180910390fd5b6006805461ffff909216600160401b0269ffff000000000000000019909216919091179055565b6000546001600160a01b03163314612aa35760405162461bcd60e51b8152600401610d2690615521565b6001600160a01b038281166000908152600360205260409020600201541615612aea57604051635edc3a1b60e11b81526001600160a01b0383166004820152602401610d26565b8015612f1c57600054612b2b906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911630846133b4565b60408051808201825260018152603160f81b6020909101819052600954825163181f5a7760e01b8152925191926001600160a01b039091169163181f5a77916004808201926000929091908290030181865afa158015612b8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612bb79190810190615961565b601081518110612bc957612bc961594b565b01602001516001600160f81b03191603612d55576000604051806080016040528060488152602001615f216048913980516020918201206040805160008185015281518082039094018452810190526006549091903090612c3c9063ffffffff811690600160401b900461ffff166159ce565b600054604080516001600160a01b038a81166020830152909216910160408051601f1981840301815290829052612c8095949392918990604d9030906024016159fa565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092526009549151630200057560e51b81529092506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811692634000aea092612d0b92919091169086908690600401615ab1565b6020604051808303816000875af1158015612d2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d4e9190615ad8565b5050612f1c565b600954612d8f906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911683614e7a565b604080516101408101825260146101008201908152734c696d6974204f7264657220526567697374727960601b61012083015281528151600060208083018290529093908301910160408051808303601f19018152918152908252306020830152600654910190612e129063ffffffff811690600160401b900461ffff166159ce565b63ffffffff1681526000546001600160a01b031660208083019190915260408051920191612e51918791016001600160a01b0391909116815260200190565b60405160208183030381529060405281526020016000604051602001612e80919060ff91909116815260200190565b60408051808303601f190181529181529082526001600160601b038516602090920191909152600954905163022de76960e21b81529192506001600160a01b0316906308b79da490612ed6908490600401615af5565b6020604051808303816000875af1158015612ef5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f19919061578a565b50505b6040518060a001604052806000815260200160008152602001836001600160a01b0316630dfe16816040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f979190615be3565b6001600160a01b03168152602001836001600160a01b031663d21220a76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fe3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130079190615be3565b6001600160a01b03168152602001836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015613053573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130779190615c13565b62ffffff9081169091526001600160a01b0384811660008181526003602081815260409283902087518155878201516001820155878401516002820180549188166001600160a01b0319909216919091179055606088015192018054608090980151909616600160a01b026001600160b81b031990971691909416179490941790925591519081527f250d91a5317f78e9f385385f5e6073f9a759b17cfa6f5d2374163436fbb44ae4910160405180910390a15050565b6000546001600160a01b031633146131585760405162461bcd60e51b8152600401610d2690615521565b6001600160a01b0316600090815260056020526040902055565b61317a614f41565b50600090815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529181015460e0830152600301549181019190915290565b6000546001600160a01b031633146132495760405162461bcd60e51b8152600401610d2690615521565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b6000546001600160a01b031633146132be5760405162461bcd60e51b8152600401610d2690615521565b600980546001600160a01b0319166001600160a01b0392909216919091179055565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806133585760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610d26565b50505050565b600080600080600085875af19050806133af5760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610d26565b505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806134375760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610d26565b5050505050565b600080613449614f41565b856000036135db5783801561345e5750865115155b1561350f57508551600081815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529181015460e083015260030154918101919091529095506136b0565b831580156135205750602087015115155b156135ce5750602080870151600081815260088352604090819020815161012081018352815460ff811615158252610100808204600290810b97840197909752600160201b8204870b948301949094526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529381015460e0850152600301549083015295506136b0565b6000809250925050613887565b50600085815260086020908152604091829020825161012081018452815460ff81161515808352610100808304600290810b96850196909652600160201b8304860b96840196909652600160381b82046001600160401b03166060840152600160781b9091046001600160801b039081166080840152600184015480821660a0850152600160801b90041660c08301529282015460e082015260039091015492810192909252841515146136a557604051630eb5001960e41b815260048101879052602401610d26565b6136b0868289614ef1565b855b84156137a1578560020b826040015160020b126136da57610100909101519092509050613887565b8160e001516000036136f3576000935091506138879050565b5060e090810151600081815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301959095526001600160401b03600160381b82041660608301526001600160801b03600160781b90910481166080830152600183015480821660a0840152600160801b90041660c08201529181015494820194909452600390930154908301526136b2565b8560020b826020015160020b136137c15760e09091015192509050613887565b8161010001516000036137db579250600091506138879050565b5061010090810151600081815260086020908152604091829020825161012081018452815460ff811615158252868104600290810b94830194909452600160201b8104840b94820194909452600160381b84046001600160401b03166060820152600160781b9093046001600160801b039081166080850152600182015480821660a0860152600160801b90041660c08401529081015460e083015260030154928101929092526136b2565b94509492505050565b60008360020b8360020b036138c957604051600162993c7b60e01b03198152600284810b600483015285900b6024820152604401610d26565b8115613902578260020b8560020b13156138e557506000610dfc565b8360020b8560020b126138fa57506002610dfc565b506001610dfc565b8360020b8560020b121561391857506000610dfc565b8260020b8560020b136138fa57506002610dfc565b8254600160781b81046001600160801b03166000908152600260205260409020600160381b9091046001600160401b0316906139698285615592565b6002820180546001600160801b0319166001600160801b039290921691909117905580546001600160a01b0319166001600160a01b0387161781556000806139bb8989670de0b6b3a764000088614816565b8854919350915060ff16156139ed576001808801546001600160801b03838116600160801b0291161790840155613a1d565b6001878101546001600160801b0319600160801b918290046001600160801b039081169092021690841617908401555b865460028401805460ff9092161515600160801b0260ff60801b1990921691909117905560006001880155865460408051600160781b9092046001600160801b031682526001600160a01b038a1660208301527f74ebbab24ee8774729982ac100f931306b160f431f7f1483cc380186d56754bc910160405180910390a1505050505050505050565b6001600160a01b03811660009081526005602052604081205490819003613aeb57604051630b90e8fb60e21b81526001600160a01b0383166004820152602401610d26565b808310156133af576040516360d7e97160e01b81526001600160a01b03831660048201526024810182905260448101849052606401610d26565b60008215613b74576040880151613b6f906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160801b038816614e7a565b613bb6565b6060880151613bb6906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160801b038716614e7a565b60006001600160801b03861615613be557612710613bd68761270f615c2e565b613be09190615c54565b613be8565b60005b905060006001600160801b03861615613c1957612710613c0a8761270f615c2e565b613c149190615c54565b613c1c565b60005b905060006040518061016001604052808c604001516001600160a01b031681526020018c606001516001600160a01b031681526020018c6080015162ffffff1681526020018a60020b81526020018b60020b8152602001896001600160801b03168152602001886001600160801b03168152602001846001600160801b03168152602001836001600160801b03168152602001306001600160a01b0316815260200186815250905060007f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160a01b03166388316456836040518263ffffffff1660e01b8152600401613d129190615c7a565b6080604051808303816000875af1158015613d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d559190615d49565b505050905080600003613d7b576040516327bd01f960e21b815260040160405180910390fd5b868015613e1c575060408c8101519051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f2881166024830152600092169063dd62ed3e90604401602060405180830381865afa158015613df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e1a919061578a565b115b15613e5b5760408c0151613e5b906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286000614e7a565b86158015613efd575060608c0151604051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f2881166024830152600092169063dd62ed3e90604401602060405180830381865afa158015613ed7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613efb919061578a565b115b15613f3c5760608c0151613f3c906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286000614e7a565b9b9a5050505050505050505050565b600080613f5a8787878661343e565b9150915080600014613f875760008181526008602052604080822060020186905585825290206003018190555b8115613fae5760008281526008602052604080822060030186905585825290206002018290555b50505050505050565b600081815260086020526040902060068054600160501b90046001600160801b031690600a613fe583615d87565b82546101009290920a6001600160801b0381810219909316918316021790915582547fff00000000000000000000000000000000ffffffffffffffffffffffffffff0016600160781b929091169190910260ff1916179215159290921790915550565b6000838152600860205260408120805460ff16156140a9576001810180548491906000906140809084906001600160801b0316615dad565b92506101000a8154816001600160801b0302191690836001600160801b031602179055506140f4565b828160010160108282829054906101000a90046001600160801b03166140cf9190615dad565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b80546001600160801b03600160781b909104811660008181526007602090815260408083206001600160a01b038a1684529091528120549192919091169081900361417d578254600160381b90046001600160401b031683600761415783615dcd565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505b6141878582615dad565b6001600160801b0383811660009081526007602090815260408083206001600160a01b038c168452909152902080546001600160801b031916929091169190911790556141d48582615dad565b979650505050505050565b6001600160a01b03858116600090815260036020818152604092839020835160a08101855281548152600182015492810192909252600280820154861694830194909452909101549283166060820152600160a01b90920462ffffff16608083015283810b9085900b131561435357806020015160000361427d576001600160a01b0386166000908152600360205260409020600101859055614457565b602081810151600090815260088252604090819020815161012081018352815460ff811615158252610100808204600290810b968401879052600160201b8304810b95840195909552600160381b82046001600160401b03166060840152600160781b9091046001600160801b039081166080840152600184015480821660a0850152600160801b90041660c08301528383015460e083015260039092015491810191909152919085900b131561434d576001600160a01b03871660009081526003602052604090206001018690555b50614457565b8160020b8460020b121561445757805160000361438a576001600160a01b0386166000908152600360205260409020859055614457565b8051600090815260086020908152604091829020825161012081018452815460ff811615158252610100808204600290810b95840195909552600160201b8204850b958301869052600160381b82046001600160401b03166060840152600160781b9091046001600160801b039081166080840152600184015480821660a0850152600160801b90041660c08301528383015460e083015260039092015491810191909152919084900b1215613fae576001600160a01b0387166000908152600360205260409020869055505b505050505050565b81156144ac5760408601516144a7906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160801b038716614e7a565b6144ee565b60608601516144ee906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160801b038616614e7a565b60006001600160801b0385161561451d5761271061450e8661270f615c2e565b6145189190615c54565b614520565b60005b905060006001600160801b03851615614551576127106145428661270f615c2e565b61454c9190615c54565b614554565b60005b6040805160c0810182528981526001600160801b0389811660208301908152898216838501908152878316606085019081529286166080850190815260a085018a8152955163219f5d1760e01b81528551600482015292516024840152905160448301529151606482015290516084820152915160a48301529192507f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160a01b03169063219f5d179060c4016060604051808303816000875af1158015614623573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146479190615de9565b5050508480156146eb57506040898101519051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f2881166024830152600092169063dd62ed3e90604401602060405180830381865afa1580156146c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146e9919061578a565b115b1561472a57604089015161472a906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286000614e7a565b841580156147cc57506060890151604051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f2881166024830152600092169063dd62ed3e90604401602060405180830381865afa1580156147a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147ca919061578a565b115b1561480b57606089015161480b906001600160a01b03167f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286000614e7a565b505050505050505050565b60008060007f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f286001600160a01b03166399fbab88886040518263ffffffff1660e01b815260040161486991815260200190565b61018060405180830381865afa158015614887573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148ab9190615e20565b50505050975050505050505050670de0b6b3a764000085826001600160801b03166148d6919061555d565b6148e09190615592565b6040805160a0810182528981526001600160801b0380841660208301908152600083850181815260608501828152608086018c81529651630624e65f60e11b81528651600482015293519094166024840152516044830152915160648201529251608484015292935091908190819081906001600160a01b037f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f281690630c49ccbe9060a40160408051808303816000875af11580156149a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149c79190615efc565b604080516080810182526000808252602082018190529181018290526060810191909152919550935091506149f99050565b6000808a670de0b6b3a764000003614a1c57506001600160801b03905080614a22565b50839050825b604080516080810182528e81523060208083018290526001600160801b03958616838501529390941660608201526001600160a01b038e81166000908152600394859052838120600281015495015493516370a0823160e01b815260048101969096529195509283169391909216919083906370a0823190602401602060405180830381865afa158015614aba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614ade919061578a565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa158015614b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614b4c919061578a565b6040805163fc6f786560e01b81528751600482015260208801516001600160a01b039081166024830152918801516001600160801b03908116604483015260608901511660648201529192507f000000000000000000000000b218e4f7cf0533d4696fdfc419a0023d33345f28169063fc6f78659060840160408051808303816000875af1158015614be2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c069190615efc565b50506040516370a0823160e01b8152306004820152600090889084906001600160a01b038816906370a0823190602401602060405180830381865afa158015614c53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c77919061578a565b614c8191906155a6565b614c8b91906158e6565b6040516370a0823160e01b8152306004820152909150600090889084906001600160a01b038816906370a0823190602401602060405180830381865afa158015614cd9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cfd919061578a565b614d0791906155a6565b614d1191906158e6565b90506001600160801b03821615614d59576001600160a01b038616600090815260016020526040812080546001600160801b0385169290614d539084906158b0565b90915550505b6001600160801b03811615614d9f576001600160a01b038516600090815260016020526040812080546001600160801b0384169290614d999084906158b0565b90915550505b50969f959e50949c50505050505050505050505050565b6001600160a01b03821660009081526003602052604090208054600190910154818503614e0c576000828152600860209081526040808320600201546001600160a01b0388168452600390925290912055614e40565b808503614e405760008181526008602090815260408083206003908101546001600160a01b03891685529252909120600101555b5050600280820180546003938401805460009081526008602052604080822090950183905581549281529384209094015581905590555050565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806133585760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610d26565b60e0820151158015614f065750610100820151155b156133af5780518314801590614f20575082816020015114155b156133af57604051630eb5001960e41b815260048101849052602401610d26565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b600060208284031215614f9f57600080fd5b813563ffffffff81168114614fb357600080fd5b9392505050565b6001600160a01b0381168114614fcf57600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561501057615010614fd2565b604052919050565b60006001600160401b0382111561503157615031614fd2565b50601f01601f191660200190565b6000806000806080858703121561505557600080fd5b843561506081614fba565b9350602085013561507081614fba565b92506040850135915060608501356001600160401b0381111561509257600080fd5b8501601f810187136150a357600080fd5b80356150b66150b182615018565b614fe8565b8181528860208385010111156150cb57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b6001600160801b0381168114614fcf57600080fd5b6000806040838503121561511557600080fd5b8235615120816150ed565b9150602083013561513081614fba565b809150509250929050565b60006020828403121561514d57600080fd5b8135614fb3816150ed565b8015158114614fcf57600080fd5b8060020b8114614fcf57600080fd5b6000806000806080858703121561518b57600080fd5b843561519681614fba565b935060208501356151a681615158565b925060408501356151b681615166565b915060608501356151c681615166565b939692955090935050565b6000602082840312156151e357600080fd5b8135614fb381614fba565b6000806000806080858703121561520457600080fd5b843561520f81614fba565b935060208501359250604085013561522681615166565b915060608501356151c681615158565b6000806020838503121561524957600080fd5b82356001600160401b038082111561526057600080fd5b818501915085601f83011261527457600080fd5b81358181111561528357600080fd5b86602082850101111561529557600080fd5b60209290920196919550909350505050565b60008060008060008060c087890312156152c057600080fd5b86356152cb81614fba565b955060208701356152db81615166565b945060408701356152eb816150ed565b935060608701356152fb81615158565b9598949750929560808101359460a0909101359350915050565b60005b83811015615330578181015183820152602001615318565b50506000910152565b60008151808452615351816020860160208601615315565b601f01601f19169290920160200192915050565b8215158152604060208201526000610dfc6040830184615339565b6000806000806080858703121561539657600080fd5b84356153a181614fba565b935060208501356153b181615166565b925060408501356153c181615158565b9396929550929360600135925050565b6000602082840312156153e357600080fd5b5035919050565b61ffff81168114614fcf57600080fd5b60006020828403121561540c57600080fd5b8135614fb3816153ea565b6000806040838503121561542a57600080fd5b823561543581614fba565b946020939093013593505050565b6000806040838503121561545657600080fd5b82359150602083013561513081614fba565b600061012082019050825115158252602083015160020b60208301526040830151615498604084018260020b9052565b5060608301516154b360608401826001600160401b03169052565b5060808301516154ce60808401826001600160801b03169052565b5060a08301516154e960a08401826001600160801b03169052565b5060c083015161550460c08401826001600160801b03169052565b5060e083015160e083015261010080840151818401525092915050565b6020808252600c908201526b15539055551213d49256915160a21b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600081600019048311821515161561557757615577615547565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826155a1576155a161557c565b500490565b818103818111156155b9576155b9615547565b92915050565b80516155ca81615166565b919050565b6000602082840312156155e157600080fd5b8151614fb381615166565b60008260020b806155ff576155ff61557c565b808360020b0791505092915050565b805169ffffffffffffffffffff811681146155ca57600080fd5b600080600080600060a0868803121561564057600080fd5b6156498661560e565b945060208601519350604086015192506060860151915061566c6080870161560e565b90509295509295909350565b60008060006060848603121561568d57600080fd5b833561569881614fba565b925060208401356156a881615158565b929592945050506040919091013590565b600080600080600080600060e0888a0312156156d457600080fd5b87516156df81614fba565b60208901519097506156f081615166565b6040890151909650615701816153ea565b6060890151909550615712816153ea565b6080890151909450615723816153ea565b60a089015190935060ff8116811461573a57600080fd5b60c089015190925061574b81615158565b8091505092959891949750929550565b634e487b7160e01b600052602160045260246000fd5b60006001820161578357615783615547565b5060010190565b60006020828403121561579c57600080fd5b5051919050565b600282810b9082900b03627fffff198112627fffff821317156155b9576155b9615547565b600281810b9083900b01627fffff8113627fffff19821217156155b9576155b9615547565b805460ff811615158352600881901c60020b6020840152602081901c60020b6040840152603881901c6001600160401b031660608401526001600160801b03607882901c8116608085015260018301546001600160801b038282161660a0860152915050608081901c60c084015250600281015460e08301526003015461010090910152565b6001600160a01b038681168252851660208201526001600160801b038481166040830152831660608201526101a08101611f6560808301846157ed565b808201808211156155b9576155b9615547565b60006001600160401b038216806158dc576158dc615547565b6000190192915050565b6001600160801b0382811682821603908082111561590657615906615547565b5092915050565b6001600160a01b03851681526001600160801b03848116602083015283166040820152610180810161594260608301846157ed565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b60006020828403121561597357600080fd5b81516001600160401b0381111561598957600080fd5b8201601f8101841361599a57600080fd5b80516159a86150b182615018565b8181528560208385010111156159bd57600080fd5b615942826020830160208601615315565b600063ffffffff808316818516818304811182151516156159f1576159f1615547565b02949350505050565b600061012080835260148184015250734c696d6974204f7264657220526567697374727960601b610140830152610160806020840152615a3c8184018c615339565b6001600160a01b038b8116604086015263ffffffff8b1660608601528916608085015283810360a08501529050615a738188615339565b9150506001600160601b03851660c083015260ff841660e0830152615aa46101008301846001600160a01b03169052565b9998505050505050505050565b60018060a01b03841681528260208201526060604082015260006159426060830184615339565b600060208284031215615aea57600080fd5b8151614fb381615158565b6020815260008251610100806020850152615b14610120850183615339565b91506020850151601f1980868503016040870152615b328483615339565b935060408701519150615b5060608701836001600160a01b03169052565b606087015163ffffffff81166080880152915060808701516001600160a01b03811660a0880152915060a08701519150808685030160c0870152615b948483615339565b935060c08701519150808685030160e087015250615bb28382615339565b92505060e0850151615bce828601826001600160601b03169052565b5090949350505050565b80516155ca81614fba565b600060208284031215615bf557600080fd5b8151614fb381614fba565b805162ffffff811681146155ca57600080fd5b600060208284031215615c2557600080fd5b614fb382615c00565b60006001600160801b03808316818516818304811182151516156159f1576159f1615547565b60006001600160801b0380841680615c6e57615c6e61557c565b92169190910492915050565b81516001600160a01b0316815261016081016020830151615ca660208401826001600160a01b03169052565b506040830151615cbd604084018262ffffff169052565b506060830151615cd2606084018260020b9052565b506080830151615ce7608084018260020b9052565b5060a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151615d2d828501826001600160a01b03169052565b505061014092830151919092015290565b80516155ca816150ed565b60008060008060808587031215615d5f57600080fd5b845193506020850151615d71816150ed565b6040860151606090960151949790965092505050565b60006001600160801b03808316818103615da357615da3615547565b6001019392505050565b6001600160801b0381811683821601908082111561590657615906615547565b60006001600160401b03808316818103615da357615da3615547565b600080600060608486031215615dfe57600080fd5b8351615e09816150ed565b602085015160409095015190969495509392505050565b6000806000806000806000806000806000806101808d8f031215615e4357600080fd5b8c516001600160601b0381168114615e5a57600080fd5b9b50615e6860208e01615bd8565b9a50615e7660408e01615bd8565b9950615e8460608e01615bd8565b9850615e9260808e01615c00565b9750615ea060a08e016155bf565b9650615eae60c08e016155bf565b9550615ebc60e08e01615d3e565b94506101008d015193506101208d01519250615edb6101408e01615d3e565b9150615eea6101608e01615d3e565b90509295989b509295989b509295989b565b60008060408385031215615f0f57600080fd5b50508051602090910151909290915056fe726567697374657228737472696e672c62797465732c616464726573732c75696e7433322c616464726573732c62797465732c75696e7439362c75696e74382c6164647265737329a2646970667358221220050050e9eeef002bff0490b18116493419b20249972b8e2abbd9ef060d3e423f64736f6c63430008100033

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

000000000000000000000000e75358526Ef4441Db03cCaEB9a87F180fAe80eb9000000000000000000000000B218e4f7cF0533d4696fDfC419A0023D33345F280000000000000000000000004300000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _owner (address): 0xe75358526Ef4441Db03cCaEB9a87F180fAe80eb9
Arg [1] : _positionManager (address): 0xB218e4f7cF0533d4696fDfC419A0023D33345F28
Arg [2] : wrappedNative (address): 0x4300000000000000000000000000000000000004
Arg [3] : link (address): 0x0000000000000000000000000000000000000000
Arg [4] : _registrar (address): 0x0000000000000000000000000000000000000000
Arg [5] : _fastGasFeed (address): 0x0000000000000000000000000000000000000000

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000e75358526Ef4441Db03cCaEB9a87F180fAe80eb9
Arg [1] : 000000000000000000000000B218e4f7cF0533d4696fDfC419A0023D33345F28
Arg [2] : 0000000000000000000000004300000000000000000000000000000000000004
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.