ETH Price: $2,862.88 (-2.67%)

Contract

0x426B751AbA5f49914bFbD4A1E45aEE099d757733

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Exact Input Sing...2356390982024-07-24 18:12:18550 days ago1721844738IN
Gridex : Swap Router Hub
0 ETH0.000000840.01
Exact Input Sing...2356389042024-07-24 18:11:30550 days ago1721844690IN
Gridex : Swap Router Hub
0 ETH0.000000820.01
Multicall2032963692024-04-21 11:14:14644 days ago1713698054IN
Gridex : Swap Router Hub
0.001 ETH0.000001710.01
Multicall1970796652024-04-03 7:19:11662 days ago1712128751IN
Gridex : Swap Router Hub
0.003 ETH0.00001350.01
Exact Input Sing...1923608072024-03-20 14:15:46676 days ago1710944146IN
Gridex : Swap Router Hub
0 ETH0.000001990.01
Multicall1923304652024-03-20 12:09:18676 days ago1710936558IN
Gridex : Swap Router Hub
0 ETH0.0000060.012373
Multicall1916503062024-03-18 12:42:59678 days ago1710765779IN
Gridex : Swap Router Hub
0 ETH0.00003830.1
Multicall1906760942024-03-15 15:55:54681 days ago1710518154IN
Gridex : Swap Router Hub
0 ETH0.000036120.1
Multicall1906735432024-03-15 15:45:13681 days ago1710517513IN
Gridex : Swap Router Hub
0.02 ETH0.000027650.1
Multicall1899439332024-03-13 11:56:48683 days ago1710331008IN
Gridex : Swap Router Hub
0 ETH0.000306580.1
Multicall1898765922024-03-13 7:10:14683 days ago1710313814IN
Gridex : Swap Router Hub
0.025 ETH0.000249630.1
Multicall1895837372024-03-12 10:06:20684 days ago1710237980IN
Gridex : Swap Router Hub
0 ETH0.000321620.1
Multicall1893469352024-03-11 16:14:36685 days ago1710173676IN
Gridex : Swap Router Hub
0.07 ETH0.00044850.1
Uniswap V3Exact ...1889370642024-03-10 10:15:19686 days ago1710065719IN
Gridex : Swap Router Hub
0 ETH0.00023480.1
Multicall1875192152024-03-06 1:42:03691 days ago1709689323IN
Gridex : Swap Router Hub
0.18 ETH0.000174370.1
Multicall1855093792024-02-29 0:47:15697 days ago1709167635IN
Gridex : Swap Router Hub
0 ETH0.00034820.1
Multicall1853813582024-02-28 15:40:20697 days ago1709134820IN
Gridex : Swap Router Hub
0 ETH0.000538310.1
Multicall1849711592024-02-27 10:32:30698 days ago1709029950IN
Gridex : Swap Router Hub
0 ETH0.000248080.1
Multicall1847570722024-02-26 19:13:23699 days ago1708974803IN
Gridex : Swap Router Hub
0 ETH0.000392750.1
Multicall1845931332024-02-26 7:40:37699 days ago1708933237IN
Gridex : Swap Router Hub
0 ETH0.000167240.1
Multicall1837050252024-02-23 15:28:58702 days ago1708702138IN
Gridex : Swap Router Hub
0 ETH0.000454760.1
Multicall1835289172024-02-23 2:57:26703 days ago1708657046IN
Gridex : Swap Router Hub
0 ETH0.000203150.1
Multicall1806363322024-02-14 10:32:07711 days ago1707906727IN
Gridex : Swap Router Hub
0 ETH0.000266710.1
Multicall1797714682024-02-11 20:30:01714 days ago1707683401IN
Gridex : Swap Router Hub
0 ETH0.000257040.1
Multicall1797618282024-02-11 19:45:53714 days ago1707680753IN
Gridex : Swap Router Hub
0 ETH0.000205990.1
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
2032963692024-04-21 11:14:14644 days ago1713698054
Gridex : Swap Router Hub
0.001 ETH
1970796652024-04-03 7:19:11662 days ago1712128751
Gridex : Swap Router Hub
0.003 ETH
1923304652024-03-20 12:09:18676 days ago1710936558
Gridex : Swap Router Hub
0.00354886 ETH
1923304652024-03-20 12:09:18676 days ago1710936558
Gridex : Swap Router Hub
0.00354886 ETH
1916503062024-03-18 12:42:59678 days ago1710765779
Gridex : Swap Router Hub
0.18596516 ETH
1916503062024-03-18 12:42:59678 days ago1710765779
Gridex : Swap Router Hub
0.18596516 ETH
1906735432024-03-15 15:45:13681 days ago1710517513
Gridex : Swap Router Hub
0.02 ETH
1899439332024-03-13 11:56:48683 days ago1710331008
Gridex : Swap Router Hub
0.00021885 ETH
1899439332024-03-13 11:56:48683 days ago1710331008
Gridex : Swap Router Hub
0.00021885 ETH
1898765922024-03-13 7:10:14683 days ago1710313814
Gridex : Swap Router Hub
0.025 ETH
1895837372024-03-12 10:06:20684 days ago1710237980
Gridex : Swap Router Hub
0.04336424 ETH
1895837372024-03-12 10:06:20684 days ago1710237980
Gridex : Swap Router Hub
0.04336424 ETH
1893469352024-03-11 16:14:36685 days ago1710173676
Gridex : Swap Router Hub
0.07 ETH
1875192152024-03-06 1:42:03691 days ago1709689323
Gridex : Swap Router Hub
0.18 ETH
1847570722024-02-26 19:13:23699 days ago1708974803
Gridex : Swap Router Hub
0.10141145 ETH
1847570722024-02-26 19:13:23699 days ago1708974803
Gridex : Swap Router Hub
0.10141145 ETH
1806363322024-02-14 10:32:07711 days ago1707906727
Gridex : Swap Router Hub
0.00787159 ETH
1806363322024-02-14 10:32:07711 days ago1707906727
Gridex : Swap Router Hub
0.00787159 ETH
1797714682024-02-11 20:30:01714 days ago1707683401
Gridex : Swap Router Hub
0.02211643 ETH
1797714682024-02-11 20:30:01714 days ago1707683401
Gridex : Swap Router Hub
0.02211643 ETH
1797618282024-02-11 19:45:53714 days ago1707680753
Gridex : Swap Router Hub
0.00329961 ETH
1797618282024-02-11 19:45:53714 days ago1707680753
Gridex : Swap Router Hub
0.00329961 ETH
1797616732024-02-11 19:45:13714 days ago1707680713
Gridex : Swap Router Hub
0.02258618 ETH
1797616732024-02-11 19:45:13714 days ago1707680713
Gridex : Swap Router Hub
0.02258618 ETH
1794708582024-02-10 23:04:07715 days ago1707606247
Gridex : Swap Router Hub
0.15031942 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SwapRouterHub

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.9;
pragma abicoder v2;

import "./SwapRouter.sol";
import "./UniswapV3Router.sol";
import "./UniswapV2Router.sol";
import "./libraries/SwapPath.sol";
import "./libraries/Protocols.sol";
import "./interfaces/ISwapRouterHub.sol";
import "./CurveRouter.sol";
import "./AbstractSelfPermit2612.sol";

/// @title Gridex, Curve, UniswapV2 and UniswapV3 Swap Router
contract SwapRouterHub is
    SwapRouter,
    UniswapV3Router,
    UniswapV2Router,
    ISwapRouterHub,
    CurveRouter,
    AbstractSelfPermit2612
{
    using SwapPath for bytes;

    constructor(
        address _gridexGridFactory,
        address _uniswapV3PoolFactory,
        address _uniswapV2PoolFactory,
        address _weth9
    )
        AbstractPayments(_gridexGridFactory, _weth9)
        UniswapV3Router(_uniswapV3PoolFactory)
        UniswapV2Router(_uniswapV2PoolFactory)
    {}

    /// @inheritdoc ISwapRouterHub
    function exactMixedInput(
        ExactMixedInputParameters memory parameters
    ) public payable override checkDeadline(parameters.deadline) returns (uint256 amountOut) {
        // msg.sender pays for the first hop
        address payer = _msgSender();
        uint256 i = 0;
        while (true) {
            bool hasMultipleGrids = parameters.path.hasMultipleGrids();
            if (parameters.path.getProtocol() == Protocols.GRIDEX) {
                parameters.amountIn = exactInputInternal(
                    parameters.amountIn,
                    hasMultipleGrids ? address(this) : parameters.recipient, // this contract keep the token of intermediate swaps within the path
                    0,
                    SwapCallbackData({
                        path: parameters.path.getFirstGrid(), // only the first grid in the path is necessary
                        payer: payer
                    })
                );
            } else if (parameters.path.getProtocol() == Protocols.UNISWAPV3) {
                parameters.amountIn = uniswapV3ExactInputInternal(
                    parameters.amountIn,
                    hasMultipleGrids ? address(this) : parameters.recipient, // this contract keep the token of intermediate swaps within the path
                    0,
                    UniswapV3SwapCallbackData({
                        path: parameters.path.getFirstGrid(), // only the first grid in the path is necessary
                        payer: payer
                    })
                );
            } else if (parameters.path.getProtocol() == Protocols.UNISWAPV2) {
                parameters.amountIn = uniswapV2ExactInputInternal(
                    parameters.amountIn,
                    parameters.path,
                    payer,
                    hasMultipleGrids ? address(this) : parameters.recipient
                );
            } else {
                if (i == 0) pay(parameters.path.getTokenA(), payer, address(this), parameters.amountIn);

                parameters.amountIn = curveExactInputInternal(
                    parameters.amountIn,
                    parameters.path,
                    parameters.path.getProtocol(),
                    hasMultipleGrids ? address(this) : parameters.recipient
                );
            }

            // decide whether to continue or terminate
            if (hasMultipleGrids) {
                unchecked {
                    i++;
                }
                // at this point, the caller has paid
                payer = address(this);
                parameters.path = parameters.path.skipToken();
            } else {
                amountOut = parameters.amountIn;
                break;
            }
        }
        // SR_TLR: too little received
        require(amountOut >= parameters.amountOutMinimum, "SR_TLR");
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title Callback for IGrid#swap
/// @notice Any contract that calls IGrid#swap must implement this interface
interface IGridSwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IGrid#swap
    /// @dev In this implementation, you are required to pay the grid tokens owed for the swap.
    /// The caller of the method must be a grid deployed by the canonical GridFactory.
    /// If there is no token swap, both amount0Delta and amount1Delta are 0
    /// @param amount0Delta The grid will send or receive the amount of token0 upon completion of the swap.
    /// In the receiving case, the callback must send this amount of token0 to the grid
    /// @param amount1Delta The grid will send or receive the quantity of token1 upon completion of the swap.
    /// In the receiving case, the callback must send this amount of token1 to the grid
    /// @param data Any data passed through by the caller via the IGrid#swap call
    function gridexSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./IGridStructs.sol";
import "./IGridParameters.sol";

/// @title The interface for Gridex grid
interface IGrid {
    ///==================================== Grid States  ====================================

    /// @notice The first token in the grid, after sorting by address
    function token0() external view returns (address);

    /// @notice The second token in the grid, after sorting by address
    function token1() external view returns (address);

    /// @notice The step size in initialized boundaries for a grid created with a given fee
    function resolution() external view returns (int24);

    /// @notice The fee paid to the grid denominated in hundredths of a bip, i.e. 1e-6
    function takerFee() external view returns (int24);

    /// @notice The 0th slot of the grid holds a lot of values that can be gas-efficiently accessed
    /// externally as a single method
    /// @return priceX96 The current price of the grid, as a Q64.96
    /// @return boundary The current boundary of the grid
    /// @return blockTimestamp The time the oracle was last updated
    /// @return unlocked Whether the grid is unlocked or not
    function slot0() external view returns (uint160 priceX96, int24 boundary, uint32 blockTimestamp, bool unlocked);

    /// @notice Returns the boundary information of token0
    /// @param boundary The boundary of the grid
    /// @return bundle0Id The unique identifier of bundle0
    /// @return bundle1Id The unique identifier of bundle1
    /// @return makerAmountRemaining The remaining amount of token0 that can be swapped out,
    /// which is the sum of bundle0 and bundle1
    function boundaries0(
        int24 boundary
    ) external view returns (uint64 bundle0Id, uint64 bundle1Id, uint128 makerAmountRemaining);

    /// @notice Returns the boundary information of token1
    /// @param boundary The boundary of the grid
    /// @return bundle0Id The unique identifier of bundle0
    /// @return bundle1Id The unique identifier of bundle1
    /// @return makerAmountRemaining The remaining amount of token1 that can be swapped out,
    /// which is the sum of bundle0 and bundle1
    function boundaries1(
        int24 boundary
    ) external view returns (uint64 bundle0Id, uint64 bundle1Id, uint128 makerAmountRemaining);

    /// @notice Returns 256 packed boundary initialized boolean values for token0
    function boundaryBitmaps0(int16 wordPos) external view returns (uint256 word);

    /// @notice Returns 256 packed boundary initialized boolean values for token1
    function boundaryBitmaps1(int16 wordPos) external view returns (uint256 word);

    /// @notice Returns the amount owed for token0 and token1
    /// @param owner The address of owner
    /// @return token0 The amount of token0 owed
    /// @return token1 The amount of token1 owed
    function tokensOweds(address owner) external view returns (uint128 token0, uint128 token1);

    /// @notice Returns the information of a given bundle
    /// @param bundleId The unique identifier of the bundle
    /// @return boundaryLower The lower boundary of the bundle
    /// @return zero When zero is true, it represents token0, otherwise it represents token1
    /// @return makerAmountTotal The total amount of token0 or token1 that the maker added
    /// @return makerAmountRemaining The remaining amount of token0 or token1 that can be swapped out from the makers
    /// @return takerAmountRemaining The remaining amount of token0 or token1 that have been swapped in from the takers
    /// @return takerFeeAmountRemaining The remaining amount of fees that takers have paid in
    function bundles(
        uint64 bundleId
    )
        external
        view
        returns (
            int24 boundaryLower,
            bool zero,
            uint128 makerAmountTotal,
            uint128 makerAmountRemaining,
            uint128 takerAmountRemaining,
            uint128 takerFeeAmountRemaining
        );

    /// @notice Returns the information of a given order
    /// @param orderId The unique identifier of the order
    /// @return bundleId The unique identifier of the bundle -- represents which bundle this order belongs to
    /// @return owner The address of the owner of the order
    /// @return amount The amount of token0 or token1 to add
    function orders(uint256 orderId) external view returns (uint64 bundleId, address owner, uint128 amount);

    ///==================================== Grid Actions ====================================

    /// @notice Initializes the grid with the given parameters
    /// @dev The caller of this method receives a callback in the form of
    /// IGridPlaceMakerOrderCallback#gridexPlaceMakerOrderCallback.
    /// When initializing the grid, token0 and token1's liquidity must be added simultaneously.
    /// @param parameters The parameters used to initialize the grid
    /// @param data Any data to be passed through to the callback
    /// @return orderIds0 The unique identifiers of the orders for token0
    /// @return orderIds1 The unique identifiers of the orders for token1
    function initialize(
        IGridParameters.InitializeParameters memory parameters,
        bytes calldata data
    ) external returns (uint256[] memory orderIds0, uint256[] memory orderIds1);

    /// @notice Swaps token0 for token1, or vice versa
    /// @dev The caller of this method receives a callback in the form of IGridSwapCallback#gridexSwapCallback
    /// @param recipient The address to receive the output of the swap
    /// @param zeroForOne The swap direction, true for token0 to token1 and false otherwise
    /// @param amountSpecified The amount of the swap, configured as an exactInput (positive)
    /// or an exactOutput (negative)
    /// @param priceLimitX96 Swap price limit: if zeroForOne, the price will not be less than this value after swap,
    /// if oneForZero, it will not be greater than this value after swap, as a Q64.96
    /// @param data Any data to be passed through to the callback
    /// @return amount0 The balance change of the grid's token0. When negative, it will reduce the balance
    /// by the exact amount. When positive, it will increase by at least this amount
    /// @return amount1 The balance change of the grid's token1. When negative, it will reduce the balance
    /// by the exact amount. When positive, it will increase by at least this amount.
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 priceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);

    /// @notice Places a maker order on the grid
    /// @dev The caller of this method receives a callback in the form of
    /// IGridPlaceMakerOrderCallback#gridexPlaceMakerOrderCallback
    /// @param parameters The parameters used to place the maker order
    /// @param data Any data to be passed through to the callback
    /// @return orderId The unique identifier of the order
    function placeMakerOrder(
        IGridParameters.PlaceOrderParameters memory parameters,
        bytes calldata data
    ) external returns (uint256 orderId);

    /// @notice Places maker orders on the grid
    /// @dev The caller of this method receives a callback in the form of
    /// IGridPlaceMakerOrderCallback#gridexPlaceMakerOrderCallback
    /// @param parameters The parameters used to place the maker orders
    /// @param data Any data to be passed through to the callback
    /// @return orderIds The unique identifiers of the orders
    function placeMakerOrderInBatch(
        IGridParameters.PlaceOrderInBatchParameters memory parameters,
        bytes calldata data
    ) external returns (uint256[] memory orderIds);

    /// @notice Settles a maker order
    /// @param orderId The unique identifier of the order
    /// @return amount0 The amount of token0 that the maker received
    /// @return amount1 The amount of token1 that the maker received
    function settleMakerOrder(uint256 orderId) external returns (uint128 amount0, uint128 amount1);

    /// @notice Settle maker order and collect
    /// @param recipient The address to receive the output of the settlement
    /// @param orderId The unique identifier of the order
    /// @param unwrapWETH9 Whether to unwrap WETH9 to ETH
    /// @return amount0 The amount of token0 that the maker received
    /// @return amount1 The amount of token1 that the maker received
    function settleMakerOrderAndCollect(
        address recipient,
        uint256 orderId,
        bool unwrapWETH9
    ) external returns (uint128 amount0, uint128 amount1);

    /// @notice Settles maker orders and collects in a batch
    /// @param recipient The address to receive the output of the settlement
    /// @param orderIds The unique identifiers of the orders
    /// @param unwrapWETH9 Whether to unwrap WETH9 to ETH
    /// @return amount0Total The total amount of token0 that the maker received
    /// @return amount1Total The total amount of token1 that the maker received
    function settleMakerOrderAndCollectInBatch(
        address recipient,
        uint256[] memory orderIds,
        bool unwrapWETH9
    ) external returns (uint128 amount0Total, uint128 amount1Total);

    /// @notice For flash swaps. The caller borrows assets and returns them in the callback of the function,
    /// in addition to a fee
    /// @dev The caller of this function receives a callback in the form of IGridFlashCallback#gridexFlashCallback
    /// @param recipient The address which will receive the token0 and token1
    /// @param amount0 The amount of token0 to receive
    /// @param amount1 The amount of token1 to receive
    /// @param data Any data to be passed through to the callback
    function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;

    /// @notice Collects tokens owed
    /// @param recipient The address to receive the collected fees
    /// @param amount0Requested The maximum amount of token0 to send.
    /// Set to 0 if fees should only be collected in token1.
    /// @param amount1Requested The maximum amount of token1 to send.
    /// Set to 0 if fees should only be collected in token0.
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        address recipient,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);
}

File 4 of 38 : IGridParameters.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IGridParameters {
    /// @dev Parameters for initializing the grid
    struct InitializeParameters {
        /// @dev The initial price of the grid, as a Q64.96.
        /// Price is represented as an amountToken1/amountToken0 Q64.96 value.
        uint160 priceX96;
        /// @dev The address to receive orders
        address recipient;
        /// @dev Represents the order parameters for token0
        BoundaryLowerWithAmountParameters[] orders0;
        /// @dev Represents the order parameters for token1
        BoundaryLowerWithAmountParameters[] orders1;
    }

    /// @dev Parameters for placing an order
    struct PlaceOrderParameters {
        /// @dev The address to receive the order
        address recipient;
        /// @dev When zero is true, it represents token0, otherwise it represents token1
        bool zero;
        /// @dev The lower boundary of the order
        int24 boundaryLower;
        /// @dev The amount of token0 or token1 to add
        uint128 amount;
    }

    struct PlaceOrderInBatchParameters {
        /// @dev The address to receive the order
        address recipient;
        /// @dev When zero is true, it represents token0, otherwise it represents token1
        bool zero;
        BoundaryLowerWithAmountParameters[] orders;
    }

    struct BoundaryLowerWithAmountParameters {
        /// @dev The lower boundary of the order
        int24 boundaryLower;
        /// @dev The amount of token0 or token1 to add
        uint128 amount;
    }

    /// @dev Status during swap
    struct SwapState {
        /// @dev When true, token0 is swapped for token1, otherwise token1 is swapped for token0
        bool zeroForOne;
        /// @dev The remaining amount of the swap, which implicitly configures
        /// the swap as exact input (positive), or exact output (negative)
        int256 amountSpecifiedRemaining;
        /// @dev The calculated amount to be inputted
        uint256 amountInputCalculated;
        /// @dev The calculated amount of fee to be inputted
        uint256 feeAmountInputCalculated;
        /// @dev The calculated amount to be outputted
        uint256 amountOutputCalculated;
        /// @dev The price of the grid, as a Q64.96
        uint160 priceX96;
        uint160 priceLimitX96;
        /// @dev The boundary of the grid
        int24 boundary;
        /// @dev The lower boundary of the grid
        int24 boundaryLower;
        uint160 initializedBoundaryLowerPriceX96;
        uint160 initializedBoundaryUpperPriceX96;
        /// @dev Whether the swap has been completed
        bool stopSwap;
    }

    struct SwapForBoundaryState {
        /// @dev The price indicated by the lower boundary, as a Q64.96
        uint160 boundaryLowerPriceX96;
        /// @dev The price indicated by the upper boundary, as a Q64.96
        uint160 boundaryUpperPriceX96;
        /// @dev The price indicated by the lower or upper boundary, as a Q64.96.
        /// When using token0 to exchange token1, it is equal to boundaryLowerPriceX96,
        /// otherwise it is equal to boundaryUpperPriceX96
        uint160 boundaryPriceX96;
        /// @dev The price of the grid, as a Q64.96
        uint160 priceX96;
    }

    struct UpdateBundleForTakerParameters {
        /// @dev The amount to be swapped in to bundle0
        uint256 amountInUsed;
        /// @dev The remaining amount to be swapped in to bundle1
        uint256 amountInRemaining;
        /// @dev The amount to be swapped out to bundle0
        uint128 amountOutUsed;
        /// @dev The remaining amount to be swapped out to bundle1
        uint128 amountOutRemaining;
        /// @dev The amount to be paid to bundle0
        uint128 takerFeeForMakerAmountUsed;
        /// @dev The amount to be paid to bundle1
        uint128 takerFeeForMakerAmountRemaining;
    }
}

File 5 of 38 : IGridStructs.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IGridStructs {
    struct Bundle {
        int24 boundaryLower;
        bool zero;
        uint128 makerAmountTotal;
        uint128 makerAmountRemaining;
        uint128 takerAmountRemaining;
        uint128 takerFeeAmountRemaining;
    }

    struct Boundary {
        uint64 bundle0Id;
        uint64 bundle1Id;
        uint128 makerAmountRemaining;
    }

    struct Order {
        uint64 bundleId;
        address owner;
        uint128 amount;
    }

    struct TokensOwed {
        uint128 token0;
        uint128 token1;
    }

    struct Slot0 {
        uint160 priceX96;
        int24 boundary;
        uint32 blockTimestamp;
        bool unlocked;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IWETHMinimum {
    function deposit() external payable;

    function transfer(address dst, uint256 wad) external returns (bool);

    function withdraw(uint256) external;

    function approve(address guy, uint256 wad) external returns (bool);

    function balanceOf(address dst) external view returns (uint256);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

library BoundaryMath {
    int24 public constant MIN_BOUNDARY = -527400;
    int24 public constant MAX_BOUNDARY = 443635;

    /// @dev The minimum value that can be returned from #getPriceX96AtBoundary. Equivalent to getPriceX96AtBoundary(MIN_BOUNDARY)
    uint160 internal constant MIN_RATIO = 989314;
    /// @dev The maximum value that can be returned from #getPriceX96AtBoundary. Equivalent to getPriceX96AtBoundary(MAX_BOUNDARY)
    uint160 internal constant MAX_RATIO = 1461300573427867316570072651998408279850435624081;

    /// @dev Checks if a boundary is divisible by a resolution
    /// @param boundary The boundary to check
    /// @param resolution The step size in initialized boundaries for a grid created with a given fee
    /// @return isValid Whether or not the boundary is valid
    function isValidBoundary(int24 boundary, int24 resolution) internal pure returns (bool isValid) {
        return boundary % resolution == 0;
    }

    /// @dev Checks if a boundary is within the valid range
    /// @param boundary The boundary to check
    /// @return inRange Whether or not the boundary is in range
    function isInRange(int24 boundary) internal pure returns (bool inRange) {
        return boundary >= MIN_BOUNDARY && boundary <= MAX_BOUNDARY;
    }

    /// @dev Checks if a price is within the valid range
    /// @param priceX96 The price to check, as a Q64.96
    /// @return inRange Whether or not the price is in range
    function isPriceX96InRange(uint160 priceX96) internal pure returns (bool inRange) {
        return priceX96 >= MIN_RATIO && priceX96 <= MAX_RATIO;
    }

    /// @notice Calculates the price at a given boundary
    /// @dev priceX96 = pow(1.0001, boundary) * 2**96
    /// @param boundary The boundary to calculate the price at
    /// @return priceX96 The price at the boundary, as a Q64.96
    function getPriceX96AtBoundary(int24 boundary) internal pure returns (uint160 priceX96) {
        unchecked {
            uint256 absBoundary = boundary < 0 ? uint256(-int256(boundary)) : uint24(boundary);

            uint256 ratio = absBoundary & 0x1 != 0
                ? 0xfff97272373d413259a46990580e213a
                : 0x100000000000000000000000000000000;
            if (absBoundary & 0x2 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
            if (absBoundary & 0x4 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
            if (absBoundary & 0x8 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
            if (absBoundary & 0x10 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
            if (absBoundary & 0x20 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
            if (absBoundary & 0x40 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
            if (absBoundary & 0x80 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
            if (absBoundary & 0x100 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
            if (absBoundary & 0x200 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
            if (absBoundary & 0x400 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
            if (absBoundary & 0x800 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
            if (absBoundary & 0x1000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
            if (absBoundary & 0x2000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
            if (absBoundary & 0x4000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
            if (absBoundary & 0x8000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
            if (absBoundary & 0x10000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
            if (absBoundary & 0x20000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
            if (absBoundary & 0x40000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
            if (absBoundary & 0x80000 != 0) ratio = (ratio * 0x149b34ee7ac263) >> 128;

            if (boundary > 0) ratio = type(uint256).max / ratio;

            // this divides by 1<<32 and rounds up to go from a Q128.128 to a Q128.96.
            // due to out boundary input limitations, we then proceed to downcast as the
            // result will always fit within 160 bits.
            // we round up in the division so that getBoundaryAtPriceX96 of the output price is always consistent
            priceX96 = uint160((ratio + 0xffffffff) >> 32);
        }
    }

    /// @notice Calculates the boundary at a given price
    /// @param priceX96 The price to calculate the boundary at, as a Q64.96
    /// @return boundary The boundary at the price
    function getBoundaryAtPriceX96(uint160 priceX96) internal pure returns (int24 boundary) {
        unchecked {
            uint256 ratio = uint256(priceX96) << 32;

            uint256 r = ratio;
            uint256 msb = 0;

            assembly {
                let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(5, gt(r, 0xFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(4, gt(r, 0xFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(3, gt(r, 0xFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(2, gt(r, 0xF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(1, gt(r, 0x3))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := gt(r, 0x1)
                msb := or(msb, f)
            }

            if (msb >= 128) r = ratio >> (msb - 127);
            else r = ratio << (127 - msb);

            int256 log_2 = (int256(msb) - 128) << 64;

            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(63, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(62, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(61, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(60, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(59, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(58, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(57, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(56, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(55, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(54, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(53, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(52, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(51, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(50, f))
            }

            int256 log10001 = log_2 * 127869479499801913173570;
            // 128.128 number

            int24 boundaryLow = int24((log10001 - 1701496478404566090792001455681771637) >> 128);
            int24 boundaryHi = int24((log10001 + 289637967442836604689790891002483458648) >> 128);

            boundary = boundaryLow == boundaryHi ? boundaryLow : getPriceX96AtBoundary(boundaryHi) <= priceX96
                ? boundaryHi
                : boundaryLow;
        }
    }

    /// @dev Returns the lower boundary for the given boundary and resolution.
    /// The lower boundary may not be valid (if out of the boundary range)
    /// @param boundary The boundary to get the lower boundary for
    /// @param resolution The step size in initialized boundaries for a grid created with a given fee
    /// @return boundaryLower The lower boundary for the given boundary and resolution
    function getBoundaryLowerAtBoundary(int24 boundary, int24 resolution) internal pure returns (int24 boundaryLower) {
        unchecked {
            return boundary - (((boundary % resolution) + resolution) % resolution);
        }
    }

    /// @dev Rewrite the lower boundary that is not in the range to a valid value
    /// @param boundaryLower The lower boundary to rewrite
    /// @param resolution The step size in initialized boundaries for a grid created with a given fee
    /// @return validBoundaryLower The valid lower boundary
    function rewriteToValidBoundaryLower(
        int24 boundaryLower,
        int24 resolution
    ) internal pure returns (int24 validBoundaryLower) {
        unchecked {
            if (boundaryLower < MIN_BOUNDARY) return boundaryLower + resolution;
            else if (boundaryLower + resolution > MAX_BOUNDARY) return boundaryLower - resolution;
            else return boundaryLower;
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./GridAddress.sol";

library CallbackValidator {
    /// @dev Validates the `msg.sender` is the canonical grid address for the given parameters
    /// @param gridFactory The address of the grid factory
    /// @param gridKey The grid key to compute the canonical address for the grid
    function validate(address gridFactory, GridAddress.GridKey memory gridKey) internal view {
        // CV_IC: invalid caller
        require(GridAddress.computeAddress(gridFactory, gridKey) == msg.sender, "CV_IC");
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Create2.sol";

library GridAddress {
    bytes32 internal constant GRID_BYTES_CODE_HASH = 0x884a6891a166f885bf6f0a3b330a25e41d1761a5aa091110a229d9a0e34b2c36;

    struct GridKey {
        address token0;
        address token1;
        int24 resolution;
    }

    /// @notice Constructs the grid key for the given parameters
    /// @dev tokenA and tokenB may be passed in, in the order of either token0/token1 or token1/token0
    /// @param tokenA The contract address of either token0 or token1
    /// @param tokenB The contract address of the other token
    /// @param resolution The step size in initialized boundaries for a grid created with a given fee
    /// @return key The grid key to compute the canonical address for the grid
    function gridKey(address tokenA, address tokenB, int24 resolution) internal pure returns (GridKey memory key) {
        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);

        return GridKey(tokenA, tokenB, resolution);
    }

    /// @dev Computes the CREATE2 address for a grid with the given parameters
    /// @param gridFactory The address of the grid factory
    /// @param key The grid key to compute the canonical address for the grid
    /// @return grid The computed address
    function computeAddress(address gridFactory, GridKey memory key) internal pure returns (address grid) {
        require(key.token0 < key.token1);
        return
            Create2.computeAddress(
                keccak256(abi.encode(key.token0, key.token1, key.resolution)),
                GRID_BYTES_CODE_HASH,
                gridFactory
            );
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

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

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

// 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
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Create2.sol)

pragma solidity ^0.8.0;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(
        uint256 amount,
        bytes32 salt,
        bytes memory bytecode
    ) internal returns (address addr) {
        require(address(this).balance >= amount, "Create2: insufficient balance");
        require(bytecode.length != 0, "Create2: bytecode length is zero");
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Create2: Failed on deploy");
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(
        bytes32 salt,
        bytes32 bytecodeHash,
        address deployer
    ) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

File 16 of 38 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

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

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

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

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

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

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

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

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

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

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

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

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

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

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

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

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

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@gridexprotocol/core/contracts/interfaces/IWETHMinimum.sol";
import "./interfaces/IPayments.sol";

abstract contract AbstractPayments is IPayments, Context {
    /// @dev The address of IGridFactory
    address public immutable gridFactory;
    /// @dev The address of IWETHMinimum
    address public immutable weth9;

    constructor(address _gridFactory, address _weth9) {
        // AP_NC: not contract
        require(Address.isContract(_gridFactory), "AP_NC");
        require(Address.isContract(_weth9), "AP_NC");

        gridFactory = _gridFactory;
        weth9 = _weth9;
    }

    modifier checkDeadline(uint256 deadline) {
        // AP_TTO: transaction too old
        require(block.timestamp <= deadline, "AP_TTO");
        _;
    }

    receive() external payable {
        // AP_WETH9: not WETH9
        require(_msgSender() == weth9, "AP_WETH9");
    }

    /// @inheritdoc IPayments
    function unwrapWETH9(uint256 amountMinimum, address recipient) public payable override {
        uint256 balanceWETH9 = IWETHMinimum(weth9).balanceOf(address(this));
        // AP_IWETH9: insufficient WETH9
        require(balanceWETH9 >= amountMinimum, "AP_IWETH9");

        if (balanceWETH9 > 0) {
            IWETHMinimum(weth9).withdraw(balanceWETH9);
            Address.sendValue(payable(recipient), balanceWETH9);
        }
    }

    /// @inheritdoc IPayments
    function sweepToken(address token, uint256 amountMinimum, address recipient) public payable override {
        uint256 balanceToken = IERC20(token).balanceOf(address(this));
        // AP_ITKN: insufficient token
        require(balanceToken >= amountMinimum, "AP_ITKN");

        if (balanceToken > 0) SafeERC20.safeTransfer(IERC20(token), recipient, balanceToken);
    }

    /// @inheritdoc IPayments
    function refundNativeToken() external payable {
        if (address(this).balance > 0) Address.sendValue(payable(_msgSender()), address(this).balance);
    }

    /// @dev Pays the token to the recipient
    /// @param token The token to pay
    /// @param payer The address of the payment token
    /// @param recipient The address that will receive the payment
    /// @param amount The amount to pay
    function pay(address token, address payer, address recipient, uint256 amount) internal {
        if (token == weth9 && address(this).balance >= amount) {
            // pay with WETH9
            Address.sendValue(payable(weth9), amount);
            IWETHMinimum(weth9).transfer(recipient, amount);
        } else if (payer == address(this)) SafeERC20.safeTransfer(IERC20(token), recipient, amount);
        else SafeERC20.safeTransferFrom(IERC20(token), payer, recipient, amount);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";

/// @dev Backward compatible EIP-2612 contract definitions.
//  For more information, please refer to https://eips.ethereum.org/EIPS/eip-2612#backwards-compatibility
interface IPermit2612Compatible {
    function permit(
        address holder,
        address spender,
        uint256 nonce,
        uint256 expiry,
        bool allowed,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

/// @dev Base contract for supporting the EIP-2612 specification.
/// For more information, please refer to https://eips.ethereum.org/EIPS/eip-2612
abstract contract AbstractSelfPermit2612 {
    function selfPermit(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable {
        IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s);
    }

    function selfPermitIfNecessary(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable {
        if (IERC20(token).allowance(msg.sender, address(this)) < value)
            IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s);
    }

    function selfPermitCompatible(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable {
        IPermit2612Compatible(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s);
    }

    function selfPermitCompatibleIfNecessary(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable {
        if (IERC20(token).allowance(msg.sender, address(this)) < type(uint256).max)
            IPermit2612Compatible(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./AbstractPayments.sol";
import "./interfaces/ICurvePool.sol";
import "./libraries/SwapPath.sol";
import "./libraries/Protocols.sol";

abstract contract CurveRouter is AbstractPayments {
    using SwapPath for bytes;

    struct CurvePayload {
        /// @dev The address of the Curve pool contract that the quote is being requested for
        address poolAddress;
        /// @dev The address of the swap contract that will be used to execute the token swap.
        address swapAddress;
        /// @dev The index of the input token in the Curve pool
        uint8 tokenInIndex;
        /// @dev The index of the output token in the Curve pool
        uint8 tokenOutIndex;
    }

    mapping(address => mapping(address => bool)) private approved;
    uint256 private constant DEFAULT_APPROVED = type(uint256).max;

    function _decodePath(
        bytes memory path
    ) internal pure returns (address tokenIn, address tokenOut, CurvePayload memory payload) {
        (
            tokenIn,
            tokenOut,
            payload.poolAddress,
            payload.swapAddress,
            payload.tokenInIndex,
            payload.tokenOutIndex
        ) = path.decodeFirstCurvePool();
    }

    function curveExactInputInternal(
        uint256 amountIn,
        bytes memory path,
        uint8 protocol,
        address recipient
    ) internal returns (uint256 amountOut) {
        (address tokenIn, address tokenOut, CurvePayload memory payload) = _decodePath(path);
        if (!approved[tokenIn][payload.poolAddress]) {
            IERC20(tokenIn).approve(payload.poolAddress, DEFAULT_APPROVED);
            approved[tokenIn][payload.poolAddress] = true;
        }

        if (protocol == Protocols.CURVE1) {
            ICurvePool(payload.poolAddress).exchange(
                int128(int8(payload.tokenInIndex)),
                int128(int8(payload.tokenOutIndex)),
                amountIn,
                0
            );
        } else if (protocol == Protocols.CURVE2) {
            ICurvePool(payload.poolAddress).exchange_underlying(
                int128(int8(payload.tokenInIndex)),
                int128(int8(payload.tokenOutIndex)),
                amountIn,
                0
            );
        } else if (protocol == Protocols.CURVE3) {
            ICurveCryptoPool(payload.poolAddress).exchange(
                uint256(payload.tokenInIndex),
                uint256(payload.tokenOutIndex),
                amountIn,
                0
            );
        } else if (protocol == Protocols.CURVE4) {
            ICurveCryptoPool(payload.poolAddress).exchange_underlying(
                uint256(payload.tokenInIndex),
                uint256(payload.tokenOutIndex),
                amountIn,
                0
            );
        } else if (protocol == Protocols.CURVE7) {
            uint256[2] memory _amounts;
            _amounts[payload.tokenInIndex] = amountIn;
            ICurveBasePool2Coins(payload.poolAddress).add_liquidity(_amounts, 0);
        } else if (protocol == Protocols.CURVE8) {
            uint256[3] memory _amounts;
            _amounts[payload.tokenInIndex] = amountIn;
            ICurveBasePool3Coins(payload.poolAddress).add_liquidity(_amounts, 0);
        } else if (protocol == Protocols.CURVE9) {
            uint256[3] memory _amounts;
            _amounts[payload.tokenInIndex] = amountIn;
            ICurveLendingBasePool3Coins(payload.poolAddress).add_liquidity(_amounts, 0, true);
        } else if (protocol == Protocols.CURVE10) {
            ICurveBasePool3Coins(payload.poolAddress).remove_liquidity_one_coin(
                amountIn,
                int128(int8(payload.tokenOutIndex)),
                0
            );
        } else if (protocol == Protocols.CURVE11) {
            ICurveLendingBasePool3Coins(payload.poolAddress).remove_liquidity_one_coin(
                amountIn,
                int128(int8(payload.tokenOutIndex)),
                0,
                true
            );
        } else if (protocol == Protocols.CURVE5) {
            ICurveLendingBasePoolMetaZap(payload.poolAddress).exchange_underlying(
                payload.swapAddress,
                int128(int8(payload.tokenInIndex)),
                int128(int8(payload.tokenOutIndex)),
                amountIn,
                0
            );
        } else if (protocol == Protocols.CURVE6) {
            ICurveCryptoMetaZap(payload.poolAddress).exchange(
                payload.swapAddress,
                uint256(payload.tokenInIndex),
                uint256(payload.tokenOutIndex),
                amountIn,
                0,
                false
            );
        } else {
            // CRQ_IP: invalid protocol
            revert("CRQ_IP");
        }
        amountOut = IERC20(tokenOut).balanceOf(address(this));
        if (recipient != address(this)) pay(tokenOut, address(this), recipient, amountOut);
    }

    function approveToCurvePool(address token, address poolAddress) external {
        IERC20(token).approve(poolAddress, DEFAULT_APPROVED);
        approved[token][poolAddress] = true;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface ICurvePool {
    function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable;

    function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external payable;

    function get_dy(int128 i, int128 j, uint256 amount) external view returns (uint256);

    function get_dy_underlying(int128 i, int128 j, uint256 amount) external view returns (uint256);
}

interface ICurveLendingBasePoolMetaZap {
    function exchange_underlying(address pool, int128 i, int128 j, uint256 dx, uint256 min_dy) external;
}

interface ICurveLendingBasePool3Coins {
    function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount, bool use_underlying) external;

    function calc_token_amount(uint256[3] memory amounts, bool is_deposit) external view returns (uint256);

    function remove_liquidity_one_coin(
        uint256 token_amount,
        int128 i,
        uint256 min_amount,
        bool use_underlying
    ) external returns (uint256);

    function calc_withdraw_one_coin(uint256 token_amount, int128 i) external view returns (uint256);
}

interface ICurveCryptoPool {
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external payable;

    function exchange_underlying(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external payable;

    function get_dy(uint256 i, uint256 j, uint256 amount) external view returns (uint256);

    function get_dy_underlying(uint256 i, uint256 j, uint256 amount) external view returns (uint256);
}

interface ICurveCryptoMetaZap {
    function get_dy(address pool, uint256 i, uint256 j, uint256 dx) external view returns (uint256);

    function exchange(address pool, uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth) external payable;
}

interface ICurveBasePool3Coins {
    function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount) external;

    function calc_token_amount(uint256[3] memory amounts, bool is_deposit) external view returns (uint256);

    function remove_liquidity_one_coin(uint256 token_amount, int128 i, uint256 min_amount) external;

    function calc_withdraw_one_coin(uint256 token_amount, int128 i) external view returns (uint256);
}

interface ICurveBasePool2Coins {
    function add_liquidity(uint256[2] memory amounts, uint256 min_mint_amount) external;

    function calc_token_amount(uint256[2] memory amounts, bool is_deposit) external view returns (uint256);

    function remove_liquidity_one_coin(uint256 token_amount, int128 i, uint256 min_amount) external;

    function calc_withdraw_one_coin(uint256 token_amount, int128 i) external view returns (uint256);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IPayments {
    /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
    /// @param amountMinimum The minimum amount of WETH9 to unwrap
    /// @param recipient The address receiving ETH
    function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;

    /// @notice Refunds any native token(e.g. ETH) balance held by this contract to the `msg.sender`
    /// @dev This method is suitable for the following 2 scenarios:
    /// 1. When using exactInput, the inputted Ether is not fully consumed due to insufficient liquidity so,
    ///    remaining Ether can be withdrawn through this method
    /// 2. When using exactOutput, the inputted Ether is not fully consumed because the slippage settings
    /// are too high, henceforth, the remaining Ether can be withdrawn through this method
    function refundNativeToken() external payable;

    /// @notice Transfers the full amount of a token held by this contract to a recipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
    /// @param token The contract address of the tokens which will be transferred to the `recipient`
    /// @param amountMinimum The minimum amount of tokens required for a transfer
    /// @param recipient The destination address of the tokens
    function sweepToken(address token, uint256 amountMinimum, address recipient) external payable;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;

import "@gridexprotocol/core/contracts/interfaces/callback/IGridSwapCallback.sol";

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Gridex
interface ISwapRouter is IGridSwapCallback {
    struct ExactInputSingleParameters {
        /// @dev Address of the input token
        address tokenIn;
        /// @dev Address of the output token
        address tokenOut;
        /// @dev The resolution of the pool to swap on
        int24 resolution;
        /// @dev Address to receive swapped tokens
        address recipient;
        /// @dev The deadline of the transaction execution
        uint256 deadline;
        /// @dev The amount of the input token to swap
        uint256 amountIn;
        /// @dev The minimum amount of the last token to receive. Reverts if actual amount received is less than this value.
        uint256 amountOutMinimum;
        /// @dev If zero for one, the price cannot be less than this value after the swap. If one for zero,
        /// the price cannot be greater than this value after the swap
        uint160 priceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param parameters The parameters necessary for the swap, encoded as `ExactInputSingleParameters` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(
        ExactInputSingleParameters calldata parameters
    ) external payable returns (uint256 amountOut);

    struct ExactInputParameters {
        /// @dev Path of tokens to swap
        bytes path;
        /// @dev Address to receive swapped tokens
        address recipient;
        /// @dev The deadline of the transaction execution
        uint256 deadline;
        /// @dev The amount of the input token to swap
        uint256 amountIn;
        /// @dev The minimum amount of the last token to receive. Reverts if actual amount received is less than this value.
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param parameters The parameters necessary for the multi-hop swap, encoded as `ExactInputParameters` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParameters calldata parameters) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParameters {
        /// @dev Address of the input token
        address tokenIn;
        /// @dev Address of the output token
        address tokenOut;
        /// @dev The resolution of the pool to swap on
        int24 resolution;
        /// @dev Address to receive swapped tokens
        address recipient;
        /// @dev The deadline of the transaction execution
        uint256 deadline;
        /// @dev The amount of the output token to receive
        uint256 amountOut;
        /// @dev The maximum amount of input tokens to spend. Reverts if actual amount spent is greater than this value.
        uint256 amountInMaximum;
        /// @dev If zero for one, the price cannot be less than this value after the swap. If one for zero,
        /// the price cannot be greater than this value after the swap
        uint160 priceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param parameters The parameters necessary for the swap, encoded as `ExactOutputSingleParameters` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(
        ExactOutputSingleParameters calldata parameters
    ) external payable returns (uint256 amountIn);

    struct ExactOutputParameters {
        /// @dev Path of tokens to swap
        bytes path;
        /// @dev Address to receive swapped tokens
        address recipient;
        /// @dev The deadline of the transaction execution
        uint256 deadline;
        /// @dev The amount of the output token to receive
        uint256 amountOut;
        /// @dev The maximum amount of input tokens to spend. Reverts if actual amount spent is greater than this value.
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param parameters The parameters necessary for the multi-hop swap, encoded as `ExactOutputParameters` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParameters calldata parameters) external payable returns (uint256 amountIn);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./ISwapRouter.sol";
import "./IUniswapV2Router.sol";
import "./IUniswapV3Router.sol";

interface ISwapRouterHub is ISwapRouter, IUniswapV2Router, IUniswapV3Router {
    struct ExactMixedInputParameters {
        /// @dev The path of tokens to trade, encoded as SwapPath.
        bytes path;
        /// @dev The address that will receive the output tokens.
        address recipient;
        /// @dev The deadline of the transaction execution.
        uint256 deadline;
        /// @dev The amount of the first token to trade.
        uint256 amountIn;
        /// @dev The minimum amount of the last token to receive. Reverts if actual amount received is less than this value.
        uint256 amountOutMinimum;
    }

    /// @notice This function executes a mixed input swap transaction with the specified input parameters.
    /// @param parameters The parameters necessary for the swap, encoded as `ExactMixedInputParameters` in calldata
    /// @return amountOut The amount of the received token
    function exactMixedInput(
        ExactMixedInputParameters calldata parameters
    ) external payable returns (uint256 amountOut);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

interface IUniswapV2Pair {
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);

    function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;

interface IUniswapV2Router {
    function uniswapV2ExactInput(
        uint256 amountIn,
        uint256 amountOutMinimum,
        address[] calldata path,
        address to
    ) external payable returns (uint256 amountOut);

    function uniswapV2ExactOutput(
        uint256 amountOut,
        uint256 amountInMaximum,
        address[] calldata path,
        address to
    ) external payable returns (uint256 amountIn);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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

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

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

    function tickSpacing() external view returns (int24);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;

interface IUniswapV3Router {
    struct UniswapV3ExactInputSingleParameters {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;

    function uniswapV3ExactInputSingle(
        UniswapV3ExactInputSingleParameters calldata parameters
    ) external payable returns (uint256 amountOut);

    struct UniswapV3ExactInputParameters {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    function uniswapV3ExactInput(
        UniswapV3ExactInputParameters calldata parameters
    ) external payable returns (uint256 amountOut);

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

    function uniswapV3ExactOutputSingle(
        UniswapV3ExactOutputSingleParameters calldata parameters
    ) external payable returns (uint256 amountIn);

    struct UniswapV3ExactOutputParameters {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    function uniswapV3ExactOutput(
        UniswapV3ExactOutputParameters calldata parameters
    ) external payable returns (uint256 amountIn);
}

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity ^0.8.0;

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

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

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

        return tempBytes;
    }

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

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

        return tempAddress;
    }

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

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

        return tempUint;
    }
}

File 29 of 38 : Protocols.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

library Protocols {
    uint8 internal constant GRIDEX = 1;
    uint8 internal constant UNISWAPV3 = 2;
    uint8 internal constant UNISWAPV2 = 3;
    uint8 internal constant CURVE = 4;
    uint8 internal constant CURVE1 = 5;
    uint8 internal constant CURVE2 = 6;
    uint8 internal constant CURVE3 = 7;
    uint8 internal constant CURVE4 = 8;
    uint8 internal constant CURVE5 = 9;
    uint8 internal constant CURVE6 = 10;
    uint8 internal constant CURVE7 = 11;
    uint8 internal constant CURVE8 = 12;
    uint8 internal constant CURVE9 = 13;
    uint8 internal constant CURVE10 = 14;
    uint8 internal constant CURVE11 = 15;
}

File 30 of 38 : Ratio.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

library Ratio {
    uint160 internal constant MIN_SQRT_RATIO_PLUS_ONE = 4295128739 + 1;
    uint160 internal constant MAX_SQRT_RATIO_MINUS_ONE = 1461446703485210103287273052203988822378723970342 - 1;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./BytesLib.sol";
import "./Protocols.sol";

/// @title Functions for manipulating path data for multihop swaps
library SwapPath {
    using BytesLib for bytes;

    /// @dev The length of the bytes encoded token index
    uint256 private constant TOKEN_INDEX_SIZE = 1;

    /// @dev The length of the bytes encoded protocol
    uint256 private constant PROTOCOL_SIZE = 1;

    /// @dev The length of the bytes encoded address
    uint256 private constant ADDR_SIZE = 20;

    /// @dev The length of the bytes encoded resolution
    uint256 private constant RESOLUTION_SIZE = 3;

    /// @dev The offset of the encoded resolution
    uint256 private constant RESOLUTION_OFFSET = ADDR_SIZE + PROTOCOL_SIZE;

    /// @dev The size of the resolution payload
    uint256 private constant RESOLUTION_PAYLOAD_SIZE = PROTOCOL_SIZE + RESOLUTION_SIZE;

    /// @dev The offset of a single token address and resolution payload
    uint256 private constant RESOLUTION_PAYLOAD_NEXT_OFFSET = ADDR_SIZE + RESOLUTION_PAYLOAD_SIZE;

    /// @dev The offset of the encoded resolution payload grid key
    uint256 private constant RESOLUTION_PAYLOAD_POP_OFFSET = RESOLUTION_PAYLOAD_NEXT_OFFSET + ADDR_SIZE;

    /// @dev The offset of the encoded swap address in the curve payload
    uint256 private constant CURVE_PAYLOAD_SWAP_ADDRESS_OFFSET = RESOLUTION_OFFSET + ADDR_SIZE;

    /// @dev The offset of the encoded token A index in the curve payload
    uint256 private constant CURVE_PAYLOAD_TOKEN_A_INDEX_OFFSET = CURVE_PAYLOAD_SWAP_ADDRESS_OFFSET + ADDR_SIZE;

    /// @dev The offset of the encoded token B index in the curve payload
    uint256 private constant CURVE_PAYLOAD_TOKEN_B_INDEX_OFFSET = CURVE_PAYLOAD_TOKEN_A_INDEX_OFFSET + TOKEN_INDEX_SIZE;

    /// @dev The size of the curve payload
    uint256 private constant CURVE_PAYLOAD_SIZE = PROTOCOL_SIZE + ADDR_SIZE * 2 + TOKEN_INDEX_SIZE * 2;

    /// @dev The offset of a single token address and curve payload
    uint256 private constant CURVE_PAYLOAD_NEXT_OFFSET = ADDR_SIZE + CURVE_PAYLOAD_SIZE;

    /// @dev The offset of an encoded curve payload grid key
    uint256 private constant CURVE_PAYLOAD_POP_OFFSET = CURVE_PAYLOAD_NEXT_OFFSET + ADDR_SIZE;

    /// @notice Returns true if the path contains two or more grids
    /// @param path The encoded swap path
    /// @return True if path contains two or more grids, otherwise false
    function hasMultipleGrids(bytes memory path) internal pure returns (bool) {
        if (getProtocol(path) < Protocols.CURVE) {
            return path.length > RESOLUTION_PAYLOAD_POP_OFFSET;
        } else {
            return path.length > CURVE_PAYLOAD_POP_OFFSET;
        }
    }

    /// @notice Decodes the first grid in path
    /// @param path The bytes encoded swap path
    /// @return tokenA The first token of the given grid
    /// @return tokenB The second token of the given grid
    /// @return resolution The resolution of the given grid
    function decodeFirstGrid(
        bytes memory path
    ) internal pure returns (address tokenA, address tokenB, int24 resolution) {
        tokenA = path.toAddress(0);
        resolution = int24(path.toUint24(RESOLUTION_OFFSET));
        tokenB = path.toAddress(RESOLUTION_PAYLOAD_NEXT_OFFSET);
    }

    /// @notice Decodes the first curve pool in path
    /// @param path The bytes encoded swap path
    /// @return tokenA The first token of the given pool
    /// @return tokenB The second token of the given pool
    /// @return poolAddress The address of the given pool
    /// @return swapAddress The swap address only for curve protocol
    /// @return tokenAIndex The index of the tokenA
    /// @return tokenBIndex The index of the tokenB
    function decodeFirstCurvePool(
        bytes memory path
    )
        internal
        pure
        returns (
            address tokenA,
            address tokenB,
            address poolAddress,
            address swapAddress,
            uint8 tokenAIndex,
            uint8 tokenBIndex
        )
    {
        tokenA = path.toAddress(0);
        poolAddress = path.toAddress(RESOLUTION_OFFSET);
        swapAddress = path.toAddress(CURVE_PAYLOAD_SWAP_ADDRESS_OFFSET);
        tokenAIndex = uint8(path[CURVE_PAYLOAD_TOKEN_A_INDEX_OFFSET]);
        tokenBIndex = uint8(path[CURVE_PAYLOAD_TOKEN_B_INDEX_OFFSET]);
        tokenB = path.toAddress(CURVE_PAYLOAD_NEXT_OFFSET);
    }

    /// @notice Gets the segment corresponding to the first grid in the path
    /// @param path The bytes encoded swap path
    /// @return The segment containing all data necessary to target the first grid in the path
    function getFirstGrid(bytes memory path) internal pure returns (bytes memory) {
        if (getProtocol(path) < Protocols.CURVE) return path.slice(0, RESOLUTION_PAYLOAD_POP_OFFSET);
        else return path.slice(0, CURVE_PAYLOAD_POP_OFFSET);
    }

    /// @notice Skips the token and the payload element from the buffer and returns the remainder
    /// @param path The swap path
    /// @return The remaining token + payload elements in the path
    function skipToken(bytes memory path) internal pure returns (bytes memory) {
        if (getProtocol(path) < Protocols.CURVE)
            return path.slice(RESOLUTION_PAYLOAD_NEXT_OFFSET, path.length - RESOLUTION_PAYLOAD_NEXT_OFFSET);
        else return path.slice(CURVE_PAYLOAD_NEXT_OFFSET, path.length - CURVE_PAYLOAD_NEXT_OFFSET);
    }

    /// @notice Returns the protocol identifier for the given path
    /// @param path The encoded swap path
    /// @return The protocol identifier
    function getProtocol(bytes memory path) internal pure returns (uint8) {
        return uint8(path[ADDR_SIZE]);
    }

    /// @notice Returns the first token address for the given path
    /// @param path The encoded swap path
    /// @return The first token address
    function getTokenA(bytes memory path) internal pure returns (address) {
        return path.toAddress(0);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "../interfaces/IUniswapV2Pair.sol";
import "@openzeppelin/contracts/utils/Create2.sol";

library UniswapV2Library {
    bytes32 internal constant POOL_BYTES_CODE_HASH = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f;

    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB);
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0));
    }

    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = Create2.computeAddress(keccak256(abi.encodePacked(token0, token1)), POOL_BYTES_CODE_HASH, factory);
    }

    function getReserves(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (uint256 reserveA, uint256 reserveB) {
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        // UV2L_IIA: insufficient input amount
        require(amountIn > 0, "UV2L_IIA");
        require(reserveIn > 0 && reserveOut > 0);
        uint256 amountInWithFee = amountIn * 997;
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * 1000 + amountInWithFee;
        amountOut = numerator / denominator;
    }

    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountIn) {
        // UV2L_IOA: insufficient output amount
        require(amountOut > 0, "UV2L_IOA");
        require(reserveIn > 0 && reserveOut > 0);
        uint256 numerator = reserveIn * amountOut * 1000;
        uint256 denominator = (reserveOut - amountOut) * 997;
        amountIn = numerator / denominator + 1;
    }

    function getAmountsIn(
        address factory,
        uint256 amountOut,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2);
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./UniswapV3PoolAddress.sol";

library UniswapV3CallbackValidator {
    function validate(address poolFactory, address tokenA, address tokenB, uint24 fee) internal view {
        validate(poolFactory, UniswapV3PoolAddress.poolKey(tokenA, tokenB, fee));
    }

    function validate(address poolFactory, UniswapV3PoolAddress.PoolKey memory poolKey) internal view {
        // CV_IC: invalid caller
        require(UniswapV3PoolAddress.computeAddress(poolFactory, poolKey) == msg.sender, "CV_IC");
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Create2.sol";

library UniswapV3PoolAddress {
    bytes32 internal constant POOL_BYTES_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;

    struct PoolKey {
        address token0;
        address token1;
        uint24 fee;
    }

    function poolKey(address tokenA, address tokenB, uint24 fee) internal pure returns (PoolKey memory) {
        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);

        return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
    }

    function computeAddress(address factory, PoolKey memory key) internal pure returns (address) {
        require(key.token0 < key.token1);
        return
            Create2.computeAddress(
                keccak256(abi.encode(key.token0, key.token1, key.fee)),
                POOL_BYTES_CODE_HASH,
                factory
            );
    }
}

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

import "@openzeppelin/contracts/utils/Address.sol";

abstract contract Multicall {
    function multicall(bytes[] calldata data) external payable virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        unchecked {
            for (uint256 i = 0; i < data.length; i++) {
                results[i] = _functionDelegateCall(data[i]);
            }
        }

        return results;
    }

    function _functionDelegateCall(bytes memory data) private returns (bytes memory) {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(this).delegatecall(data);
        // M_LDCF: low-level delegate call failed
        return Address.verifyCallResult(success, returndata, "M_LDCF");
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@gridexprotocol/core/contracts/interfaces/IGrid.sol";
import "@gridexprotocol/core/contracts/interfaces/callback/IGridSwapCallback.sol";
import "@gridexprotocol/core/contracts/libraries/GridAddress.sol";
import "@gridexprotocol/core/contracts/libraries/CallbackValidator.sol";
import "@gridexprotocol/core/contracts/libraries/BoundaryMath.sol";
import "./interfaces/ISwapRouter.sol";
import "./libraries/SwapPath.sol";
import "./AbstractPayments.sol";
import "./Multicall.sol";

/// @title Gridex Swap Router
/// @notice A stateless execution router adapted for the gridex protocol
abstract contract SwapRouter is IGridSwapCallback, ISwapRouter, AbstractPayments, Multicall {
    using SwapPath for bytes;
    using SafeCast for uint256;

    /// @dev This constant is used as a placeholder value for amountInCached; as the computed amount (for
    /// an exact output swap), will never reach this value
    uint256 private constant DEFAULT_AMOUNT_IN_CACHED = type(uint256).max;

    /// @dev Transient storage variable used for returning the computed amount in for an exact output swap.
    uint256 private amountInCached;

    constructor() {
        amountInCached = DEFAULT_AMOUNT_IN_CACHED;
    }

    /// @dev Returns the grid for the given token pair and resolution. The grid contract may or may not exist.
    function getGrid(address tokenA, address tokenB, int24 resolution) private view returns (IGrid) {
        return IGrid(GridAddress.computeAddress(gridFactory, GridAddress.gridKey(tokenA, tokenB, resolution)));
    }

    struct SwapCallbackData {
        bytes path;
        address payer;
    }

    /// @inheritdoc IGridSwapCallback
    function gridexSwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
        // swaps which are entirely contained within zero liquidity regions are not supported
        // SR_IAD: invalid amount delta
        require(amount0Delta > 0 || amount1Delta > 0, "SR_IAD");
        SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
        (address tokenIn, address tokenOut, int24 resolution) = data.path.decodeFirstGrid();
        CallbackValidator.validate(gridFactory, GridAddress.gridKey(tokenIn, tokenOut, resolution));

        (bool isExactInput, uint256 amountToPay) = amount0Delta > 0
            ? (tokenIn < tokenOut, uint256(amount0Delta))
            : (tokenOut < tokenIn, uint256(amount1Delta));
        if (isExactInput) pay(tokenIn, data.payer, _msgSender(), amountToPay);
        else {
            // either initiate the next swap or pay
            if (data.path.hasMultipleGrids()) {
                data.path = data.path.skipToken();
                exactOutputInternal(amountToPay, _msgSender(), 0, data);
            } else {
                amountInCached = amountToPay;
                // swap in/out because the exact output swaps are reversed
                tokenIn = tokenOut;
                pay(tokenIn, data.payer, _msgSender(), amountToPay);
            }
        }
    }

    /// @dev Performs a single exact input swap
    function exactInputInternal(
        uint256 amountIn,
        address recipient,
        uint160 priceLimitX96,
        SwapCallbackData memory data
    ) internal returns (uint256 amountOut) {
        // allow swapping to the router address with address 0
        recipient = recipient == address(0) ? address(this) : recipient;

        (IGrid grid, bool zeroForOne) = _decodeGridForExactInput(data);

        (int256 amount0, int256 amount1) = grid.swap(
            recipient,
            zeroForOne,
            amountIn.toInt256(),
            priceLimitX96 == 0 ? (zeroForOne ? BoundaryMath.MIN_RATIO : BoundaryMath.MAX_RATIO) : priceLimitX96,
            abi.encode(data)
        );

        return uint256(-(zeroForOne ? amount1 : amount0));
    }

    function _decodeGridForExactInput(SwapCallbackData memory data) private view returns (IGrid grid, bool zeroForOne) {
        (address tokenIn, address tokenOut, int24 resolution) = data.path.decodeFirstGrid();
        return (getGrid(tokenIn, tokenOut, resolution), tokenIn < tokenOut);
    }

    /// @inheritdoc ISwapRouter
    function exactInputSingle(
        ExactInputSingleParameters calldata parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountOut) {
        amountOut = exactInputInternal(
            parameters.amountIn,
            parameters.recipient,
            parameters.priceLimitX96,
            SwapCallbackData({
                path: abi.encodePacked(parameters.tokenIn, uint8(0), parameters.resolution, parameters.tokenOut),
                payer: _msgSender()
            })
        );
        // SR_TLR: too little received
        require(amountOut >= parameters.amountOutMinimum, "SR_TLR");
    }

    /// @inheritdoc ISwapRouter
    function exactInput(
        ExactInputParameters memory parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountOut) {
        // msg.sender pays for the first hop
        address payer = _msgSender();

        while (true) {
            bool hasMultipleGrids = parameters.path.hasMultipleGrids();

            // the output of the previous swap is used as the input of the subsequent swap.
            parameters.amountIn = exactInputInternal(
                parameters.amountIn,
                hasMultipleGrids ? address(this) : parameters.recipient, // this contract keep the token of intermediate swaps within the path
                0,
                SwapCallbackData({
                    path: parameters.path.getFirstGrid(), // only the first grid in the path is necessary
                    payer: payer
                })
            );

            // decide whether to continue or terminate
            if (hasMultipleGrids) {
                // at this point, the caller has paid
                payer = address(this);
                parameters.path = parameters.path.skipToken();
            } else {
                amountOut = parameters.amountIn;
                break;
            }
        }
        // SR_TLR: too little received
        require(amountOut >= parameters.amountOutMinimum, "SR_TLR");
    }

    /// @dev Performs a single exact output swap
    function exactOutputInternal(
        uint256 amountOut,
        address recipient,
        uint160 priceLimitX96,
        SwapCallbackData memory data
    ) private returns (uint256 amountIn) {
        // allow swapping to the router address with address 0
        recipient = recipient == address(0) ? address(this) : recipient;

        (IGrid grid, bool zeroForOne) = _decodeGridForExactOutput(data);

        (int256 amount0Delta, int256 amount1Delta) = grid.swap(
            recipient,
            zeroForOne,
            -amountOut.toInt256(),
            priceLimitX96 == 0 ? (zeroForOne ? BoundaryMath.MIN_RATIO : BoundaryMath.MAX_RATIO) : priceLimitX96,
            abi.encode(data)
        );

        uint256 amountOutReceived;
        (amountIn, amountOutReceived) = zeroForOne
            ? (uint256(amount0Delta), uint256(-amount1Delta))
            : (uint256(amount1Delta), uint256(-amount0Delta));
        // technically, it is possible to not receive all of the output amount,
        // so if PriceLimit is not specified, this possibility needs to be eliminated immediately
        if (priceLimitX96 == 0) require(amountOutReceived == amountOut, "SR_IAOR"); // SR_IAOR: invalid amount out received
    }

    function _decodeGridForExactOutput(
        SwapCallbackData memory data
    ) private view returns (IGrid grid, bool zeroForOne) {
        (address tokenOut, address tokenIn, int24 resolution) = data.path.decodeFirstGrid();
        return (getGrid(tokenIn, tokenOut, resolution), tokenIn < tokenOut);
    }

    /// @inheritdoc ISwapRouter
    function exactOutputSingle(
        ExactOutputSingleParameters calldata parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountIn) {
        // avoid an SLOAD by using the swap return data
        amountIn = exactOutputInternal(
            parameters.amountOut,
            parameters.recipient,
            parameters.priceLimitX96,
            SwapCallbackData({
                path: abi.encodePacked(parameters.tokenOut, uint8(0), parameters.resolution, parameters.tokenIn),
                payer: _msgSender()
            })
        );

        // SR_TMR: too much requested
        require(amountIn <= parameters.amountInMaximum, "SR_TMR");
        // must be reset, despite remaining unused in the single hop case
        amountInCached = DEFAULT_AMOUNT_IN_CACHED;
    }

    /// @inheritdoc ISwapRouter
    function exactOutput(
        ExactOutputParameters calldata parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountIn) {
        // the payer is fixed as _msgSender() here, this is a non-issue as they only pay for the “final” exactOutput
        // swap, which happens first, swaps that follow are paid within nested callbacks
        exactOutputInternal(
            parameters.amountOut,
            parameters.recipient,
            0,
            SwapCallbackData({path: parameters.path, payer: _msgSender()})
        );

        amountIn = amountInCached;
        // SR_TMR: too much requested
        require(amountIn <= parameters.amountInMaximum, "SR_TMR");
        amountInCached = DEFAULT_AMOUNT_IN_CACHED;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./AbstractPayments.sol";
import "./interfaces/IUniswapV2Router.sol";
import "./libraries/UniswapV2Library.sol";
import "./libraries/SwapPath.sol";

/// @title Uniswap V2 Swap Router
/// @notice A stateless execution router adapted for the Uniswap V2 protocol
abstract contract UniswapV2Router is IUniswapV2Router, AbstractPayments {
    using SwapPath for bytes;
    address public immutable uniswapV2PoolFactory;

    constructor(address _uniswapV2PoolFactory) {
        uniswapV2PoolFactory = _uniswapV2PoolFactory;
    }

    // supports fee-on-transfer tokens
    // requires the initial amount to have already been sent to the first pair
    function _swap(address[] memory path, address _to) private {
        unchecked {
            for (uint256 i; i < path.length - 1; i++) {
                (address input, address output) = (path[i], path[i + 1]);
                address to = i < path.length - 2
                    ? UniswapV2Library.pairFor(uniswapV2PoolFactory, output, path[i + 2])
                    : _to;
                _swapOnce(input, output, to);
            }
        }
    }

    function _swapOnce(address input, address output, address recipient) private {
        (address token0, ) = UniswapV2Library.sortTokens(input, output);
        IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(uniswapV2PoolFactory, input, output));
        uint256 amountInput;
        uint256 amountOutput;
        // scope to avoid stack too deep errors
        {
            (uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
            (uint256 reserveInput, uint256 reserveOutput) = input == token0
                ? (reserve0, reserve1)
                : (reserve1, reserve0);
            amountInput = IERC20(input).balanceOf(address(pair)) - reserveInput;
            amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
        }
        (uint256 amount0Out, uint256 amount1Out) = input == token0
            ? (uint256(0), amountOutput)
            : (amountOutput, uint256(0));

        pair.swap(amount0Out, amount1Out, recipient, new bytes(0));
    }

    function uniswapV2ExactInputInternal(
        uint256 amountIn,
        bytes memory path,
        address payer,
        address recipient
    ) internal returns (uint256 amountOut) {
        (address input, address output, ) = path.decodeFirstGrid();
        pay(input, payer, UniswapV2Library.pairFor(uniswapV2PoolFactory, input, output), amountIn);
        uint256 balanceBefore = IERC20(output).balanceOf(recipient);
        _swapOnce(input, output, recipient);
        amountOut = IERC20(output).balanceOf(recipient) - balanceBefore;
    }

    /// @inheritdoc IUniswapV2Router
    function uniswapV2ExactInput(
        uint256 amountIn,
        uint256 amountOutMinimum,
        address[] calldata path,
        address to
    ) external payable override returns (uint256 amountOut) {
        pay(path[0], _msgSender(), UniswapV2Library.pairFor(uniswapV2PoolFactory, path[0], path[1]), amountIn);

        // allows swapping to the router address with address 0
        to = to == address(0) ? address(this) : to;

        uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);

        _swap(path, to);

        amountOut = IERC20(path[path.length - 1]).balanceOf(to) - balanceBefore;
        // UV2R_TLR: too little received
        require(amountOut >= amountOutMinimum, "UV2R_TLR");
    }

    /// @inheritdoc IUniswapV2Router
    function uniswapV2ExactOutput(
        uint256 amountOut,
        uint256 amountInMaximum,
        address[] calldata path,
        address to
    ) external payable override returns (uint256 amountIn) {
        amountIn = UniswapV2Library.getAmountsIn(uniswapV2PoolFactory, amountOut, path)[0];
        // UV2R_TMR: Too much requested
        require(amountIn <= amountInMaximum, "UV2R_TMR");

        pay(path[0], _msgSender(), UniswapV2Library.pairFor(uniswapV2PoolFactory, path[0], path[1]), amountIn);

        // allows swapping to the router address with address 0
        to = to == address(0) ? address(this) : to;

        _swap(path, to);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
pragma abicoder v2;

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "./AbstractPayments.sol";
import "./interfaces/IUniswapV3Router.sol";
import "./interfaces/IUniswapV3PoolMinimum.sol";
import "./libraries/SwapPath.sol";
import "./libraries/UniswapV3PoolAddress.sol";
import "./libraries/UniswapV3CallbackValidator.sol";
import "./libraries/Ratio.sol";

/// @title Uniswap V3 Swap Router
/// @notice A stateless execution router adapted for the Uniswap V3 protocol
abstract contract UniswapV3Router is IUniswapV3Router, AbstractPayments {
    using SwapPath for bytes;
    using SafeCast for uint256;

    uint256 private constant DEFAULT_AMOUNT_IN_CACHED = type(uint256).max;

    uint256 private amountInCached;

    address public immutable uniswapV3PoolFactory;

    constructor(address _uniswapV3PoolFactory) {
        uniswapV3PoolFactory = _uniswapV3PoolFactory;
        amountInCached = DEFAULT_AMOUNT_IN_CACHED;
    }

    /// @dev Returns the pool for the given token pair and fee. The pool contract may or may not exist.
    function getUniswapV3Pool(address tokenA, address tokenB, int24 fee) private view returns (IUniswapV3PoolMinimum) {
        return
            IUniswapV3PoolMinimum(
                UniswapV3PoolAddress.computeAddress(
                    uniswapV3PoolFactory,
                    UniswapV3PoolAddress.poolKey(tokenA, tokenB, uint24(fee))
                )
            );
    }

    struct UniswapV3SwapCallbackData {
        bytes path;
        address payer;
    }

    /// @inheritdoc IUniswapV3Router
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
        // swaps which are entirely contained within zero liquidity regions are not supported
        require(amount0Delta > 0 || amount1Delta > 0);
        UniswapV3SwapCallbackData memory data = abi.decode(_data, (UniswapV3SwapCallbackData));
        (address tokenIn, address tokenOut, int24 fee) = data.path.decodeFirstGrid();
        UniswapV3CallbackValidator.validate(uniswapV3PoolFactory, tokenIn, tokenOut, uint24(fee));

        (bool isExactInput, uint256 amountToPay) = amount0Delta > 0
            ? (tokenIn < tokenOut, uint256(amount0Delta))
            : (tokenOut < tokenIn, uint256(amount1Delta));

        if (isExactInput) pay(tokenIn, data.payer, _msgSender(), amountToPay);
        else {
            // either initiate the next swap or pay
            if (data.path.hasMultipleGrids()) {
                data.path = data.path.skipToken();
                uniswapV3ExactOutputInternal(amountToPay, _msgSender(), 0, data);
            } else {
                amountInCached = amountToPay;
                // note that tokenOut is actually tokenIn because exactOutput swaps are executed in reverse order
                pay(tokenOut, data.payer, _msgSender(), amountToPay);
            }
        }
    }

    /// @dev Performs a single exact input swap
    function uniswapV3ExactInputInternal(
        uint256 amountIn,
        address recipient,
        uint160 sqrtPriceLimitX96,
        UniswapV3SwapCallbackData memory data
    ) internal returns (uint256 amountOut) {
        // allow swapping to the router address with address 0
        recipient = recipient == address(0) ? address(this) : recipient;

        (address tokenIn, address tokenOut, int24 fee) = data.path.decodeFirstGrid();

        bool zeroForOne = tokenIn < tokenOut;

        (int256 amount0, int256 amount1) = getUniswapV3Pool(tokenIn, tokenOut, fee).swap(
            recipient,
            zeroForOne,
            amountIn.toInt256(),
            sqrtPriceLimitX96 == 0
                ? (zeroForOne ? Ratio.MIN_SQRT_RATIO_PLUS_ONE : Ratio.MAX_SQRT_RATIO_MINUS_ONE)
                : sqrtPriceLimitX96,
            abi.encode(data)
        );

        return uint256(-(zeroForOne ? amount1 : amount0));
    }

    /// @inheritdoc IUniswapV3Router
    function uniswapV3ExactInputSingle(
        UniswapV3ExactInputSingleParameters calldata parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountOut) {
        amountOut = uniswapV3ExactInputInternal(
            parameters.amountIn,
            parameters.recipient,
            parameters.sqrtPriceLimitX96,
            UniswapV3SwapCallbackData({
                path: abi.encodePacked(parameters.tokenIn, uint8(0), parameters.fee, parameters.tokenOut),
                payer: _msgSender()
            })
        );
        // UV3R_TLR: too little received
        require(amountOut >= parameters.amountOutMinimum, "UV3R_TLR");
    }

    /// @inheritdoc IUniswapV3Router
    function uniswapV3ExactInput(
        UniswapV3ExactInputParameters memory parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountOut) {
        // the first hop is paid for by msg.sender
        address payer = _msgSender();

        while (true) {
            bool hasMultipleGrids = parameters.path.hasMultipleGrids();

            // the output of the previous swap is used as the input of the subsequent swap
            parameters.amountIn = uniswapV3ExactInputInternal(
                parameters.amountIn,
                hasMultipleGrids ? address(this) : parameters.recipient, // this contract keep the token of intermediate swaps within the path
                0,
                UniswapV3SwapCallbackData({
                    path: parameters.path.getFirstGrid(), // only the first pool in the path is necessary
                    payer: payer
                })
            );

            // decide whether to continue or terminate
            if (hasMultipleGrids) {
                // at this point, the caller has paid
                payer = address(this);
                parameters.path = parameters.path.skipToken();
            } else {
                amountOut = parameters.amountIn;
                break;
            }
        }
        // UV3R_TLR: too little received
        require(amountOut >= parameters.amountOutMinimum, "UV3R_TLR");
    }

    /// @dev Performs a single exact output swap
    function uniswapV3ExactOutputInternal(
        uint256 amountOut,
        address recipient,
        uint160 sqrtPriceLimitX96,
        UniswapV3SwapCallbackData memory data
    ) internal returns (uint256 amountIn) {
        // allow swapping to the router address with address 0
        recipient = recipient == address(0) ? address(this) : recipient;

        (address tokenOut, address tokenIn, int24 fee) = data.path.decodeFirstGrid();

        bool zeroForOne = tokenIn < tokenOut;

        (int256 amount0Delta, int256 amount1Delta) = getUniswapV3Pool(tokenIn, tokenOut, fee).swap(
            recipient,
            zeroForOne,
            -amountOut.toInt256(),
            sqrtPriceLimitX96 == 0
                ? (zeroForOne ? Ratio.MIN_SQRT_RATIO_PLUS_ONE : Ratio.MAX_SQRT_RATIO_MINUS_ONE)
                : sqrtPriceLimitX96,
            abi.encode(data)
        );

        uint256 amountOutReceived;
        (amountIn, amountOutReceived) = zeroForOne
            ? (uint256(amount0Delta), uint256(-amount1Delta))
            : (uint256(amount1Delta), uint256(-amount0Delta));
        // technically, it is possible to not receive all of the output amount,
        // so if PriceLimit is not specified, this possibility needs to be eliminated immediately
        if (sqrtPriceLimitX96 == 0) require(amountOutReceived == amountOut, "UV3R_IAOR"); // UV3R_IAOR: invalid amount out received
    }

    /// @inheritdoc IUniswapV3Router
    function uniswapV3ExactOutputSingle(
        UniswapV3ExactOutputSingleParameters calldata parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountIn) {
        // avoid an SLOAD by using the swap return data
        amountIn = uniswapV3ExactOutputInternal(
            parameters.amountOut,
            parameters.recipient,
            parameters.sqrtPriceLimitX96,
            UniswapV3SwapCallbackData({
                path: abi.encodePacked(parameters.tokenOut, uint8(0), parameters.fee, parameters.tokenIn),
                payer: _msgSender()
            })
        );

        // UV3R_TMR: too much requested
        require(amountIn <= parameters.amountInMaximum, "UV3R_TMR");
        // must be reset, despite remaining unused in the single hop case
        amountInCached = DEFAULT_AMOUNT_IN_CACHED;
    }

    /// @inheritdoc IUniswapV3Router
    function uniswapV3ExactOutput(
        UniswapV3ExactOutputParameters calldata parameters
    ) external payable override checkDeadline(parameters.deadline) returns (uint256 amountIn) {
        uniswapV3ExactOutputInternal(
            parameters.amountOut,
            parameters.recipient,
            0,
            UniswapV3SwapCallbackData({path: parameters.path, payer: _msgSender()})
        );

        amountIn = amountInCached;
        // UV3R_TMR: too much requested
        require(amountIn <= parameters.amountInMaximum, "UV3R_TMR");
        amountInCached = DEFAULT_AMOUNT_IN_CACHED;
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_gridexGridFactory","type":"address"},{"internalType":"address","name":"_uniswapV3PoolFactory","type":"address"},{"internalType":"address","name":"_uniswapV2PoolFactory","type":"address"},{"internalType":"address","name":"_weth9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"poolAddress","type":"address"}],"name":"approveToCurvePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"}],"internalType":"struct ISwapRouter.ExactInputParameters","name":"parameters","type":"tuple"}],"name":"exactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"int24","name":"resolution","type":"int24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"uint160","name":"priceLimitX96","type":"uint160"}],"internalType":"struct ISwapRouter.ExactInputSingleParameters","name":"parameters","type":"tuple"}],"name":"exactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"}],"internalType":"struct ISwapRouterHub.ExactMixedInputParameters","name":"parameters","type":"tuple"}],"name":"exactMixedInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"}],"internalType":"struct ISwapRouter.ExactOutputParameters","name":"parameters","type":"tuple"}],"name":"exactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"int24","name":"resolution","type":"int24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"uint160","name":"priceLimitX96","type":"uint160"}],"internalType":"struct ISwapRouter.ExactOutputSingleParameters","name":"parameters","type":"tuple"}],"name":"exactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"gridFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"gridexSwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundNativeToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitCompatible","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitCompatibleIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"uniswapV2ExactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"uniswapV2ExactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"uniswapV2PoolFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"}],"internalType":"struct IUniswapV3Router.UniswapV3ExactInputParameters","name":"parameters","type":"tuple"}],"name":"uniswapV3ExactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IUniswapV3Router.UniswapV3ExactInputSingleParameters","name":"parameters","type":"tuple"}],"name":"uniswapV3ExactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"}],"internalType":"struct IUniswapV3Router.UniswapV3ExactOutputParameters","name":"parameters","type":"tuple"}],"name":"uniswapV3ExactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IUniswapV3Router.UniswapV3ExactOutputSingleParameters","name":"parameters","type":"tuple"}],"name":"uniswapV3ExactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"uniswapV3PoolFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"weth9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

6101006040523480156200001257600080fd5b506040516200588638038062005886833981016040819052620000359162000137565b818385836200004f826200010b60201b620021f81760201c565b620000895760405162461bcd60e51b815260206004820152600560248201526441505f4e4360d81b60448201526064015b60405180910390fd5b6200009f816200010b60201b620021f81760201c565b620000d55760405162461bcd60e51b815260206004820152600560248201526441505f4e4360d81b604482015260640162000080565b6001600160a01b03918216608052811660a052600019600081905591811660c0526001919091551660e052506200019492505050565b6001600160a01b03163b151590565b80516001600160a01b03811681146200013257600080fd5b919050565b600080600080608085870312156200014e57600080fd5b62000159856200011a565b935062000169602086016200011a565b925062000179604086016200011a565b915062000189606086016200011a565b905092959194509250565b60805160a05160c05160e0516156486200023e600039600081816102eb015281816111e3015281816112d901528181611f7401528181612867015281816136f20152613e7101526000818161049f01528181611e830152613baa0152600081816101ad0152818161034d01528181610c8c01528181610d95015281816129ef01528181612a380152612a9c0152600081816102400152818161143b015261484101526156486000f3fe6080604052600436106101a55760003560e01c806380dd0e49116100e1578063c714e8381161008a578063f3995c6711610064578063f3995c671461047a578063f91841f11461048d578063fa461e33146104c1578063fe552424146104e157600080fd5b8063c714e83814610441578063df2ab5bb14610454578063f28c04981461046757600080fd5b8063ac9650d8116100bb578063ac9650d8146103fb578063c04b8d591461041b578063c2e3140a1461042e57600080fd5b806380dd0e49146103b55780638870c4f8146103c8578063a026383e146103e857600080fd5b8063418652701161014e57806350879c1c1161012857806350879c1c1461033b5780635d980a461461036f5780637400b0f01461038f5780637e71c50c146103a257600080fd5b8063418652701461030d578063445150551461031557806349404b7c1461032857600080fd5b80632ae2c4cf1161017f5780632ae2c4cf146102b357806337584a60146102c65780633c99d55b146102d957600080fd5b806303a7dcdc1461022e578063101183f81461027f5780631872c5a2146102a057600080fd5b3661022957337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146102275760405162461bcd60e51b815260206004820152600860248201527f41505f574554483900000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b005b600080fd5b34801561023a57600080fd5b506102627f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61029261028d366004614b9d565b6104f4565b604051908152602001610276565b6102276102ae366004614bea565b61062d565b6102926102c1366004614dbe565b6106db565b6102926102d4366004614dbe565b61081d565b3480156102e557600080fd5b506102627f000000000000000000000000000000000000000000000000000000000000000081565b610227610a71565b610292610323366004614e06565b610a83565b610227610336366004614e23565b610c5b565b34801561034757600080fd5b506102627f000000000000000000000000000000000000000000000000000000000000000081565b34801561037b57600080fd5b5061022761038a366004614e53565b610e08565b61022761039d366004614bea565b610ef7565b6102926103b0366004614e06565b611017565b6102926103c3366004614ec6565b6111dc565b3480156103d457600080fd5b506102276103e3366004614f31565b6113b3565b6102926103f6366004614e06565b611510565b61040e610409366004614fb1565b6116c0565b6040516102769190615069565b610292610429366004614dbe565b6117a9565b61022761043c366004614bea565b6118e4565b61029261044f366004614e06565b6119f2565b6102276104623660046150e9565b611bb1565b610292610475366004614b9d565b611cad565b610227610488366004614bea565b611dce565b34801561049957600080fd5b506102627f000000000000000000000000000000000000000000000000000000000000000081565b3480156104cd57600080fd5b506102276104dc366004614f31565b611e3e565b6102926104ef366004614ec6565b611f41565b600081604001358042111561054b5760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b6105c76060840135610563604086016020870161512b565b604080518082019091526000908061057b8980615148565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001335b6001600160a01b03169052612207565b50600154915082608001358211156106215760405162461bcd60e51b815260206004820152600860248201527f555633525f544d52000000000000000000000000000000000000000000000000604482015260640161021e565b50600019600155919050565b6040517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290526001600160a01b03871690638fcbaf0c90610104015b600060405180830381600087803b1580156106bb57600080fd5b505af11580156106cf573d6000803e3d6000fd5b50505050505050505050565b60008160400151804211156107325760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b335b600061074385600001516123db565b905061078f85606001518261075c57866020015161075e565b305b600060405180604001604052806107788b60000151612452565b8152602001876001600160a01b03168152506124d2565b606086015280156107af5784513092506107a890612633565b85526107bc565b84606001519350506107c2565b50610734565b83608001518310156108165760405162461bcd60e51b815260206004820152600860248201527f555633525f544c52000000000000000000000000000000000000000000000000604482015260640161021e565b5050919050565b60008160400151804211156108745760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b3360005b600061088786600001516123db565b8651909150600190610898906126ee565b60ff1614156108f5576108eb8660600151826108b85787602001516108ba565b305b600060405180604001604052806108d48c60000151612452565b8152602001886001600160a01b0316815250612711565b60608701526109e1565b8551600290610903906126ee565b60ff161415610956576108eb866060015182610923578760200151610925565b305b6000604051806040016040528061093f8c60000151612452565b8152602001886001600160a01b03168152506124d2565b8551600390610964906126ee565b60ff161415610990576108eb86606001518760000151858461098a57896020015161284a565b3061284a565b816109b1576109b16109a587600001516129e1565b843089606001516129ed565b606086015186516109db91906109c6816126ee565b846109d5578960200151612b4b565b30612b4b565b60608701525b8015610a025785513093506001909201916109fb90612633565b8652610a0f565b8560600151945050610a15565b50610878565b8460800151841015610a695760405162461bcd60e51b815260206004820152600660248201527f53525f544c520000000000000000000000000000000000000000000000000000604482015260640161021e565b505050919050565b4715610a8157610a813347613383565b565b6000816080013580421115610ada5760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b610bff60a0840135610af2608086016060870161512b565b610b03610100870160e0880161512b565b6040805180820190915280610b1b60208a018a61512b565b6000610b2d60608c0160408d016151ad565b610b3d60408d0160208e0161512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b7fffffff000000000000000000000000000000000000000000000000000000000016603583015290921b166038820152604c016040516020818303038152906040528152602001610bef3390565b6001600160a01b031690526124d2565b91508260c00135821015610c555760405162461bcd60e51b815260206004820152600860248201527f555633525f544c52000000000000000000000000000000000000000000000000604482015260640161021e565b50919050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015610cd657600080fd5b505afa158015610cea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0e91906151d2565b905082811015610d605760405162461bcd60e51b815260206004820152600960248201527f41505f4957455448390000000000000000000000000000000000000000000000604482015260640161021e565b8015610e03576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015610de157600080fd5b505af1158015610df5573d6000803e3d6000fd5b50505050610e038282613383565b505050565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152600019602483015283169063095ea7b390604401602060405180830381600087803b158015610e6c57600080fd5b505af1158015610e80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea491906151eb565b506001600160a01b039182166000908152600260209081526040808320939094168252919091522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6040517fdd62ed3e000000000000000000000000000000000000000000000000000000008152336004820152306024820152600019906001600160a01b0388169063dd62ed3e9060440160206040518083038186803b158015610f5957600080fd5b505afa158015610f6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9191906151d2565b101561100f576040517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290526001600160a01b03871690638fcbaf0c90610104016106a1565b505050505050565b600081608001358042111561106e5760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b61118660a0840135611086608086016060870161512b565b611097610100870160e0880161512b565b60405180604001604052808860200160208101906110b5919061512b565b60006110c760608c0160408d016151ad565b6110d460208d018d61512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b7fffffff000000000000000000000000000000000000000000000000000000000016603583015290921b166038820152604c0160405160208183030381529060405281526020016105b73390565b91508260c001358211156106215760405162461bcd60e51b815260206004820152600860248201527f555633525f544d52000000000000000000000000000000000000000000000000604482015260640161021e565b600061123c7f00000000000000000000000000000000000000000000000000000000000000008786868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061349c92505050565b60008151811061124e5761124e61520d565b60200260200101519050848111156112a85760405162461bcd60e51b815260206004820152600860248201527f555632525f544d52000000000000000000000000000000000000000000000000604482015260640161021e565b611353848460008181106112be576112be61520d565b90506020020160208101906112d3919061512b565b3361134d7f00000000000000000000000000000000000000000000000000000000000000008888600081811061130b5761130b61520d565b9050602002016020810190611320919061512b565b898960018181106113335761133361520d565b9050602002016020810190611348919061512b565b6135ef565b846129ed565b6001600160a01b03821615611368578161136a565b305b91506113aa84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525086925061368d915050565b95945050505050565b60008413806113c25750600083135b61140e5760405162461bcd60e51b815260206004820152600660248201527f53525f4941440000000000000000000000000000000000000000000000000000604482015260640161021e565b600061141c828401846152b5565b90506000806000611430846000015161374c565b92509250925061146a7f000000000000000000000000000000000000000000000000000000000000000061146585858561379e565b613813565b60008060008a1361149057846001600160a01b0316846001600160a01b031610896114a7565b836001600160a01b0316856001600160a01b0316108a5b9150915081156114c6576114c185876020015161134d3390565b6106cf565b85516114d1906123db565b156114f65785516114e190612633565b86526114f08133600089613878565b506106cf565b806000819055508394506106cf85876020015161134d3390565b60008160800135804211156115675760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b61166a60a084013561157f608086016060870161512b565b611590610100870160e0880161512b565b60408051808201909152806115a860208a018a61512b565b60006115ba60608c0160408d016152ea565b6115ca60408d0160208e0161512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b603583015290921b166038820152604c01604051602081830303815290604052815260200161165a3390565b6001600160a01b03169052612711565b91508260c00135821015610c555760405162461bcd60e51b815260206004820152600660248201527f53525f544c520000000000000000000000000000000000000000000000000000604482015260640161021e565b60608167ffffffffffffffff8111156116db576116db614c4c565b60405190808252806020026020018201604052801561170e57816020015b60608152602001906001900390816116f95790505b50905060005b828110156117a25761177d8484838181106117315761173161520d565b90506020028101906117439190615148565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a2692505050565b82828151811061178f5761178f61520d565b6020908102919091010152600101611714565b5092915050565b60008160400151804211156118005760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b335b600061181185600001516123db565b905061185d85606001518261182a57866020015161182c565b305b600060405180604001604052806118468b60000151612452565b8152602001876001600160a01b0316815250612711565b6060860152801561187d57845130925061187690612633565b855261188a565b8460600151935050611890565b50611802565b83608001518310156108165760405162461bcd60e51b815260206004820152600660248201527f53525f544c520000000000000000000000000000000000000000000000000000604482015260640161021e565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015285906001600160a01b0388169063dd62ed3e9060440160206040518083038186803b15801561194457600080fd5b505afa158015611958573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197c91906151d2565b101561100f576040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018690526064810185905260ff8416608482015260a4810183905260c481018290526001600160a01b0387169063d505accf9060e4016106a1565b6000816080013580421115611a495760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b611b4f60a0840135611a61608086016060870161512b565b611a72610100870160e0880161512b565b6040518060400160405280886020016020810190611a90919061512b565b6000611aa260608c0160408d016152ea565b611aaf60208d018d61512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b603583015290921b166038820152604c016040516020818303038152906040528152602001611b3f3390565b6001600160a01b03169052613878565b91508260c00135821115611ba55760405162461bcd60e51b815260206004820152600660248201527f53525f544d520000000000000000000000000000000000000000000000000000604482015260640161021e565b50600019600055919050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b158015611c0c57600080fd5b505afa158015611c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4491906151d2565b905082811015611c965760405162461bcd60e51b815260206004820152600760248201527f41505f49544b4e00000000000000000000000000000000000000000000000000604482015260640161021e565b8015611ca757611ca7848383613ac8565b50505050565b6000816040013580421115611d045760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b611d746060840135611d1c604086016020870161512b565b6040805180820190915260009080611d348980615148565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200133611b3f565b5060005491508260800135821115611ba55760405162461bcd60e51b815260206004820152600660248201527f53525f544d520000000000000000000000000000000000000000000000000000604482015260640161021e565b6040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018690526064810185905260ff8416608482015260a4810183905260c481018290526001600160a01b0387169063d505accf9060e4016106a1565b6000841380611e4d5750600083135b611e5657600080fd5b6000611e64828401846152b5565b90506000806000611e78846000015161374c565b925092509250611eaa7f0000000000000000000000000000000000000000000000000000000000000000848484613b8f565b60008060008a13611ed057846001600160a01b0316846001600160a01b03161089611ee7565b836001600160a01b0316856001600160a01b0316108a5b915091508115611f01576114c185876020015161134d3390565b8551611f0c906123db565b15611f2b578551611f1c90612633565b86526114f08133600089612207565b600181905560208601516106cf9085903361134d565b6000611fac84846000818110611f5957611f5961520d565b9050602002016020810190611f6e919061512b565b33611fa67f00000000000000000000000000000000000000000000000000000000000000008888600081811061130b5761130b61520d565b896129ed565b6001600160a01b03821615611fc15781611fc3565b305b915060008484611fd460018261533c565b818110611fe357611fe361520d565b9050602002016020810190611ff8919061512b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03858116600483015291909116906370a082319060240160206040518083038186803b15801561205457600080fd5b505afa158015612068573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208c91906151d2565b90506120cc85858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525087925061368d915050565b8085856120da60018261533c565b8181106120e9576120e961520d565b90506020020160208101906120fe919061512b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b15801561215a57600080fd5b505afa15801561216e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219291906151d2565b61219c919061533c565b9150858210156121ee5760405162461bcd60e51b815260206004820152600860248201527f555632525f544c52000000000000000000000000000000000000000000000000604482015260640161021e565b5095945050505050565b6001600160a01b03163b151590565b60006001600160a01b0384161561221e5783612220565b305b93506000806000612234856000015161374c565b919450925090506001600160a01b0380841690831610600080612258858786613ba3565b6001600160a01b031663128acb088b856122718f613bd9565b61227a90615353565b6001600160a01b038e161561228f578d6122b5565b876122ae5773fffd8963efd1fc6a506488495d951d5263988d256122b5565b6401000276a45b8d6040516020016122c691906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016122f59594939291906153d1565b6040805180830381600087803b15801561230e57600080fd5b505af1158015612322573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612346919061540b565b91509150600083612360578161235b84615353565b61236a565b8261236a83615353565b90985090506001600160a01b038a166123cc578b81146123cc5760405162461bcd60e51b815260206004820152600960248201527f555633525f49414f520000000000000000000000000000000000000000000000604482015260640161021e565b50505050505050949350505050565b600060046123e8836126ee565b60ff16101561241e5760146123ff6003600161542f565b61240a90601461542f565b612414919061542f565b8251119050919050565b601461242c60016002615447565b61243860146002615447565b61244390600161542f565b6123ff919061542f565b919050565b6060600461245f836126ee565b60ff16101561249e576124986000601461247b6003600161542f565b61248690601461542f565b612490919061542f565b849190613c75565b92915050565b612498600060146124b160016002615447565b6124bd60146002615447565b6124c890600161542f565b61247b919061542f565b60006001600160a01b038416156124e957836124eb565b305b935060008060006124ff856000015161374c565b919450925090506001600160a01b0380831690841610600080612523868686613ba3565b6001600160a01b031663128acb088b8561253c8f613bd9565b6001600160a01b038e1615612551578d612577565b876125705773fffd8963efd1fc6a506488495d951d5263988d25612577565b6401000276a45b8d60405160200161258891906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016125b79594939291906153d1565b6040805180830381600087803b1580156125d057600080fd5b505af11580156125e4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612608919061540b565b91509150826126175781612619565b805b61262290615353565b96505050505050505b949350505050565b60606004612640836126ee565b60ff161015612686576124986126586003600161542f565b61266390601461542f565b61266f6003600161542f565b61267a90601461542f565b8451612490919061533c565b61249861269560016002615447565b6126a160146002615447565b6126ac90600161542f565b6126b6919061542f565b6126c190601461542f565b6126cd60016002615447565b6126d960146002615447565b6126e490600161542f565b61266f919061542f565b6000816014815181106127035761270361520d565b016020015160f81c92915050565b60006001600160a01b03841615612728578361272a565b305b935060008061273884613e14565b91509150600080836001600160a01b031663128acb0889856127598d613bd9565b6001600160a01b038c161561276e578b612792565b8761278d5773fff6fbe64b68d618d47c209fe40b0d8ee6e23c91612792565b620f18825b8b6040516020016127a391906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016127d29594939291906153d1565b6040805180830381600087803b1580156127eb57600080fd5b505af11580156127ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612823919061540b565b91509150826128325781612834565b805b61283d90615353565b9998505050505050505050565b60008060006128588661374c565b5091509150612893828661288d7f000000000000000000000000000000000000000000000000000000000000000086866135ef565b8a6129ed565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152600091908316906370a082319060240160206040518083038186803b1580156128f157600080fd5b505afa158015612905573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292991906151d2565b9050612936838387613e5b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301528291908416906370a082319060240160206040518083038186803b15801561299357600080fd5b505afa1580156129a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129cb91906151d2565b6129d5919061533c565b98975050505050505050565b600061249882826140d4565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316846001600160a01b0316148015612a2e5750804710155b15612b1e57612a5d7f000000000000000000000000000000000000000000000000000000000000000082613383565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb90604401602060405180830381600087803b158015612ae057600080fd5b505af1158015612af4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1891906151eb565b50611ca7565b6001600160a01b038316301415612b3f57612b3a848383613ac8565b611ca7565b611ca7848484846141a4565b600080600080612b5a876141f5565b6001600160a01b038084166000908152600260209081526040808320855190941683529290522054929550909350915060ff16612c825780516040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260001960248201529084169063095ea7b390604401602060405180830381600087803b158015612bf857600080fd5b505af1158015612c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3091906151eb565b506001600160a01b038084166000908152600260209081526040808320855190941683529290522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b60ff861660051415612d2b578051604080830151606084015191517f3df02124000000000000000000000000000000000000000000000000000000008152600091820b600482015291810b6024830152604482018b905260648201526001600160a01b0390911690633df02124906084015b600060405180830381600087803b158015612d0e57600080fd5b505af1158015612d22573d6000803e3d6000fd5b505050506132ca565b60ff861660061415612da1578051604080830151606084015191517fa6417ed6000000000000000000000000000000000000000000000000000000008152600091820b600482015291810b6024830152604482018b905260648201526001600160a01b039091169063a6417ed690608401612cf4565b60ff861660071415612e18578051604080830151606084015191517f5b41b90800000000000000000000000000000000000000000000000000000000815260ff918216600482015291166024820152604481018a9052600060648201526001600160a01b0390911690635b41b90890608401612cf4565b60ff861660081415612e8f578051604080830151606084015191517f65b2489b00000000000000000000000000000000000000000000000000000000815260ff918216600482015291166024820152604481018a9052600060648201526001600160a01b03909116906365b2489b90608401612cf4565b60ff8616600b1415612f4557612ea3614b4f565b8881836040015160ff1660028110612ebd57612ebd61520d565b602002015281516040517f0b4c7e4d0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690630b4c7e4d90612f0d908490600090600401615466565b600060405180830381600087803b158015612f2757600080fd5b505af1158015612f3b573d6000803e3d6000fd5b50505050506132ca565b60ff8616600c1415612fc357612f59614b6d565b8881836040015160ff1660038110612f7357612f7361520d565b602002015281516040517f4515cef30000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690634515cef390612f0d9084906000906004016154c1565b60ff8616600d141561304457612fd7614b6d565b8881836040015160ff1660038110612ff157612ff161520d565b602002015281516040517f2b6e993a0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690632b6e993a90612f0d9084906000906001906004016154dc565b60ff8616600e14156130b057805160608201516040517f1a4d01d2000000000000000000000000000000000000000000000000000000008152600481018b9052600091820b602482015260448101919091526001600160a01b0390911690631a4d01d290606401612cf4565b60ff8616600f141561317657805160608201516040517f517a55a3000000000000000000000000000000000000000000000000000000008152600481018b9052600091820b60248201526044810191909152600160648201526001600160a01b039091169063517a55a390608401602060405180830381600087803b15801561313857600080fd5b505af115801561314c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317091906151d2565b506132ca565b60ff8616600914156131f85780516020820151604080840151606085015191517f7981c43e0000000000000000000000000000000000000000000000000000000081526001600160a01b039384166004820152600091820b602482015291810b6044830152606482018c90526084820152911690637981c43e9060a401612cf4565b60ff8616600a14156132825780516020820151604080840151606085015191517f2bf78c610000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015260ff918216602482015291166044820152606481018b905260006084820181905260a4820152911690632bf78c619060c401612cf4565b60405162461bcd60e51b815260206004820152600660248201527f4352515f49500000000000000000000000000000000000000000000000000000604482015260640161021e565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a082319060240160206040518083038186803b15801561332257600080fd5b505afa158015613336573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335a91906151d2565b93506001600160a01b038516301461337857613378823087876129ed565b505050949350505050565b804710156133d35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161021e565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613420576040519150601f19603f3d011682016040523d82523d6000602084013e613425565b606091505b5050905080610e035760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161021e565b60606002825110156134ad57600080fd5b815167ffffffffffffffff8111156134c7576134c7614c4c565b6040519080825280602002602001820160405280156134f0578160200160208202803683370190505b509050828160018351613503919061533c565b815181106135135761351361520d565b60200260200101818152505060006001835161352f919061533c565b90505b80156135e757600080613582878661354b60018761533c565b8151811061355b5761355b61520d565b60200260200101518786815181106135755761357561520d565b6020026020010151614252565b915091506135aa84848151811061359b5761359b61520d565b60200260200101518383614339565b846135b660018661533c565b815181106135c6576135c661520d565b602002602001018181525050505080806135df90615500565b915050613532565b509392505050565b60008060006135fe85856143ec565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b166034820152919350915061368390604801604051602081830303815290604052805190602001207f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f60001b88614450565b9695505050505050565b60005b6001835103811015610e03576000808483815181106136b1576136b161520d565b60200260200101518584600101815181106136ce576136ce61520d565b6020026020010151915091506000600286510384106136ed5784613734565b6137347f0000000000000000000000000000000000000000000000000000000000000000838887600201815181106137275761372761520d565b60200260200101516135ef565b9050613741838383613e5b565b505050600101613690565b6000808061375a84826140d4565b925061377261376b6001601461542f565b859061447a565b90506137956137836003600161542f565b61378e90601461542f565b85906140d4565b91509193909250565b6040805160608101825260008082526020820181905291810191909152826001600160a01b0316846001600160a01b031611156137d9579192915b6040518060600160405280856001600160a01b03168152602001846001600160a01b031681526020018360020b81525090505b9392505050565b3361381e838361453a565b6001600160a01b0316146138745760405162461bcd60e51b815260206004820152600560248201527f43565f4943000000000000000000000000000000000000000000000000000000604482015260640161021e565b5050565b60006001600160a01b0384161561388f5783613891565b305b935060008061389f846145de565b91509150600080836001600160a01b031663128acb0889856138c08d613bd9565b6138c990615353565b6001600160a01b038c16156138de578b613902565b876138fd5773fff6fbe64b68d618d47c209fe40b0d8ee6e23c91613902565b620f18825b8b60405160200161391391906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016139429594939291906153d1565b6040805180830381600087803b15801561395b57600080fd5b505af115801561396f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613993919061540b565b915091506000836139ad57816139a884615353565b6139b7565b826139b783615353565b90965090506001600160a01b038816613a1957898114613a195760405162461bcd60e51b815260206004820152600760248201527f53525f49414f5200000000000000000000000000000000000000000000000000604482015260640161021e565b5050505050949350505050565b6060600080306001600160a01b031684604051613a439190615517565b600060405180830381855af49150503d8060008114613a7e576040519150601f19603f3d011682016040523d82523d6000602084013e613a83565b606091505b509150915061262b82826040518060400160405280600681526020017f4d5f4c4443460000000000000000000000000000000000000000000000000000815250614625565b6040516001600160a01b038316602482015260448101829052610e039084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261463e565b611ca784613b9e858585614723565b61478e565b600061262b7f0000000000000000000000000000000000000000000000000000000000000000613bd4868686614723565b614795565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115613c715760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e74323536000000000000000000000000000000000000000000000000606482015260840161021e565b5090565b606081613c8381601f61542f565b1015613cd15760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161021e565b82613cdc838261542f565b1015613d2a5760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161021e565b613d34828461542f565b84511015613d845760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015260640161021e565b606082158015613da35760405191506000825260208201604052613e0b565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613ddc578051835260209283019201613dc4565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6000806000806000613e29866000015161374c565b925092509250613e3a83838361483a565b826001600160a01b0316846001600160a01b03161094509450505050915091565b6000613e6784846143ec565b5090506000613e977f000000000000000000000000000000000000000000000000000000000000000086866135ef565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613ed857600080fd5b505afa158015613eec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f109190615551565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff169150600080876001600160a01b03168b6001600160a01b031614613f58578284613f5b565b83835b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015292945090925083918d16906370a082319060240160206040518083038186803b158015613fbd57600080fd5b505afa158015613fd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff591906151d2565b613fff919061533c565b955061400c868383614870565b945050505050600080856001600160a01b0316896001600160a01b0316146140365782600061403a565b6000835b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915291935091506001600160a01b0386169063022c0d9f9061409790859085908c9060248101615596565b600060405180830381600087803b1580156140b157600080fd5b505af11580156140c5573d6000803e3d6000fd5b50505050505050505050505050565b6000816140e281601461542f565b10156141305760405162461bcd60e51b815260206004820152601260248201527f746f416464726573735f6f766572666c6f770000000000000000000000000000604482015260640161021e565b61413b82601461542f565b8351101561418b5760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e64730000000000000000000000604482015260640161021e565b5001602001516c01000000000000000000000000900490565b6040516001600160a01b0380851660248301528316604482015260648101829052611ca79085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613b0d565b6040805160808101825260008082526020820181905291810182905260608101829052819061422384614928565b60ff90811660608801521660408601526001600160a01b03908116602086015216835290959094509092509050565b600080600061426185856143ec565b5090506000806142728888886135ef565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156142aa57600080fd5b505afa1580156142be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e29190615551565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff169150826001600160a01b0316876001600160a01b03161461432757808261432a565b81815b90999098509650505050505050565b600080841161438a5760405162461bcd60e51b815260206004820152600860248201527f5556324c5f494f41000000000000000000000000000000000000000000000000604482015260640161021e565b60008311801561439a5750600082115b6143a357600080fd5b60006143af8585615447565b6143bb906103e8615447565b905060006143c9868561533c565b6143d5906103e5615447565b90506143e181836155c4565b61368390600161542f565b600080826001600160a01b0316846001600160a01b0316141561440e57600080fd5b826001600160a01b0316846001600160a01b03161061442e578284614431565b83835b90925090506001600160a01b03821661444957600080fd5b9250929050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b60008161448881600361542f565b10156144d65760405162461bcd60e51b815260206004820152601160248201527f746f55696e7432345f6f766572666c6f77000000000000000000000000000000604482015260640161021e565b6144e182600361542f565b835110156145315760405162461bcd60e51b815260206004820152601460248201527f746f55696e7432345f6f75744f66426f756e6473000000000000000000000000604482015260640161021e565b50016003015190565b600081602001516001600160a01b031682600001516001600160a01b03161061456257600080fd5b815160208084015160408086015181516001600160a01b0395861694810194909452939091169082015260029190910b606082015261380c90608001604051602081830303815290604052805190602001207f884a6891a166f885bf6f0a3b330a25e41d1761a5aa091110a229d9a0e34b2c3660001b85614450565b60008060008060006145f3866000015161374c565b92509250925061460482848361483a565b836001600160a01b0316836001600160a01b03161094509450505050915091565b6060831561463457508161380c565b61380c8383614a3a565b6000614693826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614a649092919063ffffffff16565b805190915015610e0357808060200190518101906146b191906151eb565b610e035760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161021e565b6040805160608101825260008082526020820181905291810191909152826001600160a01b0316846001600160a01b0316111561475e579192915b50604080516060810182526001600160a01b03948516815292909316602083015262ffffff169181019190915290565b3361381e83835b600081602001516001600160a01b031682600001516001600160a01b0316106147bd57600080fd5b815160208084015160408086015181516001600160a01b0395861694810194909452939091169082015262ffffff909116606082015261380c90608001604051602081830303815290604052805190602001207fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460001b85614450565b600061262b7f000000000000000000000000000000000000000000000000000000000000000061486b86868661379e565b61453a565b60008084116148c15760405162461bcd60e51b815260206004820152600860248201527f5556324c5f494941000000000000000000000000000000000000000000000000604482015260640161021e565b6000831180156148d15750600082115b6148da57600080fd5b60006148e8856103e5615447565b905060006148f68483615447565b9050600082614907876103e8615447565b614911919061542f565b905061491d81836155c4565b979650505050505050565b6000808080808061493987826140d4565b955061495161494a6001601461542f565b88906140d4565b935061496d601461496360018261542f565b61494a919061542f565b92508660148061497e60018261542f565b614988919061542f565b614992919061542f565b815181106149a2576149a261520d565b016020015160f81c91508660016014806149bc838261542f565b6149c6919061542f565b6149d0919061542f565b6149da919061542f565b815181106149ea576149ea61520d565b016020015160f81c9050614a2f614a0360016002615447565b614a0f60146002615447565b614a1a90600161542f565b614a24919061542f565b61494a90601461542f565b945091939550919395565b815115614a4a5781518083602001fd5b8060405162461bcd60e51b815260040161021e91906155ff565b606061262b848460008585600080866001600160a01b03168587604051614a8b9190615517565b60006040518083038185875af1925050503d8060008114614ac8576040519150601f19603f3d011682016040523d82523d6000602084013e614acd565b606091505b509150915061491d8783838760608315614b45578251614b3e576001600160a01b0385163b614b3e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161021e565b508161262b565b61262b8383614a3a565b60405180604001604052806002906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b600060a08284031215610c5557600080fd5b600060208284031215614baf57600080fd5b813567ffffffffffffffff811115614bc657600080fd5b61262b84828501614b8b565b6001600160a01b0381168114614be757600080fd5b50565b60008060008060008060c08789031215614c0357600080fd5b8635614c0e81614bd2565b95506020870135945060408701359350606087013560ff81168114614c3257600080fd5b9598949750929560808101359460a0909101359350915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112614c8c57600080fd5b813567ffffffffffffffff80821115614ca757614ca7614c4c565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715614ced57614ced614c4c565b81604052838152866020858801011115614d0657600080fd5b836020870160208301376000602085830101528094505050505092915050565b600060a08284031215614d3857600080fd5b60405160a0810167ffffffffffffffff8282108183111715614d5c57614d5c614c4c565b816040528293508435915080821115614d7457600080fd5b50614d8185828601614c7b565b8252506020830135614d9281614bd2565b806020830152506040830135604082015260608301356060820152608083013560808201525092915050565b600060208284031215614dd057600080fd5b813567ffffffffffffffff811115614de757600080fd5b61262b84828501614d26565b60006101008284031215610c5557600080fd5b60006101008284031215614e1957600080fd5b61380c8383614df3565b60008060408385031215614e3657600080fd5b823591506020830135614e4881614bd2565b809150509250929050565b60008060408385031215614e6657600080fd5b8235614e7181614bd2565b91506020830135614e4881614bd2565b60008083601f840112614e9357600080fd5b50813567ffffffffffffffff811115614eab57600080fd5b6020830191508360208260051b850101111561444957600080fd5b600080600080600060808688031215614ede57600080fd5b8535945060208601359350604086013567ffffffffffffffff811115614f0357600080fd5b614f0f88828901614e81565b9094509250506060860135614f2381614bd2565b809150509295509295909350565b60008060008060608587031215614f4757600080fd5b8435935060208501359250604085013567ffffffffffffffff80821115614f6d57600080fd5b818701915087601f830112614f8157600080fd5b813581811115614f9057600080fd5b886020828501011115614fa257600080fd5b95989497505060200194505050565b60008060208385031215614fc457600080fd5b823567ffffffffffffffff811115614fdb57600080fd5b614fe785828601614e81565b90969095509350505050565b60005b8381101561500e578181015183820152602001614ff6565b83811115611ca75750506000910152565b60008151808452615037816020860160208601614ff3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156150dc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526150ca85835161501f565b94509285019290850190600101615090565b5092979650505050505050565b6000806000606084860312156150fe57600080fd5b833561510981614bd2565b925060208401359150604084013561512081614bd2565b809150509250925092565b60006020828403121561513d57600080fd5b813561380c81614bd2565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261517d57600080fd5b83018035915067ffffffffffffffff82111561519857600080fd5b60200191503681900382131561444957600080fd5b6000602082840312156151bf57600080fd5b813562ffffff8116811461380c57600080fd5b6000602082840312156151e457600080fd5b5051919050565b6000602082840312156151fd57600080fd5b8151801515811461380c57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006040828403121561524e57600080fd5b6040516040810167ffffffffffffffff828210818311171561527257615272614c4c565b81604052829350843591508082111561528a57600080fd5b5061529785828601614c7b565b82525060208301356152a881614bd2565b6020919091015292915050565b6000602082840312156152c757600080fd5b813567ffffffffffffffff8111156152de57600080fd5b61262b8482850161523c565b6000602082840312156152fc57600080fd5b81358060020b811461380c57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561534e5761534e61530d565b500390565b60007f80000000000000000000000000000000000000000000000000000000000000008214156153855761538561530d565b5060000390565b60008151604084526153a1604085018261501f565b6020938401516001600160a01b0316949093019390935250919050565b60208152600061380c602083018461538c565b60006001600160a01b038088168352861515602084015285604084015280851660608401525060a0608083015261491d60a083018461501f565b6000806040838503121561541e57600080fd5b505080516020909101519092909150565b600082198211156154425761544261530d565b500190565b60008160001904831182151516156154615761546161530d565b500290565b60608101818460005b600281101561548e57815183526020928301929091019060010161546f565b5050508260408301529392505050565b8060005b6003811015611ca75781518452602093840193909101906001016154a2565b608081016154cf828561549e565b8260608301529392505050565b60a081016154ea828661549e565b8360608301528215156080830152949350505050565b60008161550f5761550f61530d565b506000190190565b60008251615529818460208701614ff3565b9190910192915050565b80516dffffffffffffffffffffffffffff8116811461244d57600080fd5b60008060006060848603121561556657600080fd5b61556f84615533565b925061557d60208501615533565b9150604084015163ffffffff8116811461512057600080fd5b8481528360208201526001600160a01b0383166040820152608060608201526000613683608083018461501f565b6000826155fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60208152600061380c602083018461501f56fea2646970667358221220db31f1b9509a9ae1aa16f9965cbbd2c30d8d00524e839300031e477739a3940a64736f6c6343000809003300000000000000000000000032d1f0dce675902f89d72251db4ab1d728efa19c0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f984000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1

Deployed Bytecode

0x6080604052600436106101a55760003560e01c806380dd0e49116100e1578063c714e8381161008a578063f3995c6711610064578063f3995c671461047a578063f91841f11461048d578063fa461e33146104c1578063fe552424146104e157600080fd5b8063c714e83814610441578063df2ab5bb14610454578063f28c04981461046757600080fd5b8063ac9650d8116100bb578063ac9650d8146103fb578063c04b8d591461041b578063c2e3140a1461042e57600080fd5b806380dd0e49146103b55780638870c4f8146103c8578063a026383e146103e857600080fd5b8063418652701161014e57806350879c1c1161012857806350879c1c1461033b5780635d980a461461036f5780637400b0f01461038f5780637e71c50c146103a257600080fd5b8063418652701461030d578063445150551461031557806349404b7c1461032857600080fd5b80632ae2c4cf1161017f5780632ae2c4cf146102b357806337584a60146102c65780633c99d55b146102d957600080fd5b806303a7dcdc1461022e578063101183f81461027f5780631872c5a2146102a057600080fd5b3661022957337f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b0316146102275760405162461bcd60e51b815260206004820152600860248201527f41505f574554483900000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b005b600080fd5b34801561023a57600080fd5b506102627f00000000000000000000000032d1f0dce675902f89d72251db4ab1d728efa19c81565b6040516001600160a01b0390911681526020015b60405180910390f35b61029261028d366004614b9d565b6104f4565b604051908152602001610276565b6102276102ae366004614bea565b61062d565b6102926102c1366004614dbe565b6106db565b6102926102d4366004614dbe565b61081d565b3480156102e557600080fd5b506102627f000000000000000000000000000000000000000000000000000000000000000081565b610227610a71565b610292610323366004614e06565b610a83565b610227610336366004614e23565b610c5b565b34801561034757600080fd5b506102627f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab181565b34801561037b57600080fd5b5061022761038a366004614e53565b610e08565b61022761039d366004614bea565b610ef7565b6102926103b0366004614e06565b611017565b6102926103c3366004614ec6565b6111dc565b3480156103d457600080fd5b506102276103e3366004614f31565b6113b3565b6102926103f6366004614e06565b611510565b61040e610409366004614fb1565b6116c0565b6040516102769190615069565b610292610429366004614dbe565b6117a9565b61022761043c366004614bea565b6118e4565b61029261044f366004614e06565b6119f2565b6102276104623660046150e9565b611bb1565b610292610475366004614b9d565b611cad565b610227610488366004614bea565b611dce565b34801561049957600080fd5b506102627f0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f98481565b3480156104cd57600080fd5b506102276104dc366004614f31565b611e3e565b6102926104ef366004614ec6565b611f41565b600081604001358042111561054b5760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b6105c76060840135610563604086016020870161512b565b604080518082019091526000908061057b8980615148565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001335b6001600160a01b03169052612207565b50600154915082608001358211156106215760405162461bcd60e51b815260206004820152600860248201527f555633525f544d52000000000000000000000000000000000000000000000000604482015260640161021e565b50600019600155919050565b6040517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290526001600160a01b03871690638fcbaf0c90610104015b600060405180830381600087803b1580156106bb57600080fd5b505af11580156106cf573d6000803e3d6000fd5b50505050505050505050565b60008160400151804211156107325760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b335b600061074385600001516123db565b905061078f85606001518261075c57866020015161075e565b305b600060405180604001604052806107788b60000151612452565b8152602001876001600160a01b03168152506124d2565b606086015280156107af5784513092506107a890612633565b85526107bc565b84606001519350506107c2565b50610734565b83608001518310156108165760405162461bcd60e51b815260206004820152600860248201527f555633525f544c52000000000000000000000000000000000000000000000000604482015260640161021e565b5050919050565b60008160400151804211156108745760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b3360005b600061088786600001516123db565b8651909150600190610898906126ee565b60ff1614156108f5576108eb8660600151826108b85787602001516108ba565b305b600060405180604001604052806108d48c60000151612452565b8152602001886001600160a01b0316815250612711565b60608701526109e1565b8551600290610903906126ee565b60ff161415610956576108eb866060015182610923578760200151610925565b305b6000604051806040016040528061093f8c60000151612452565b8152602001886001600160a01b03168152506124d2565b8551600390610964906126ee565b60ff161415610990576108eb86606001518760000151858461098a57896020015161284a565b3061284a565b816109b1576109b16109a587600001516129e1565b843089606001516129ed565b606086015186516109db91906109c6816126ee565b846109d5578960200151612b4b565b30612b4b565b60608701525b8015610a025785513093506001909201916109fb90612633565b8652610a0f565b8560600151945050610a15565b50610878565b8460800151841015610a695760405162461bcd60e51b815260206004820152600660248201527f53525f544c520000000000000000000000000000000000000000000000000000604482015260640161021e565b505050919050565b4715610a8157610a813347613383565b565b6000816080013580421115610ada5760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b610bff60a0840135610af2608086016060870161512b565b610b03610100870160e0880161512b565b6040805180820190915280610b1b60208a018a61512b565b6000610b2d60608c0160408d016151ad565b610b3d60408d0160208e0161512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b7fffffff000000000000000000000000000000000000000000000000000000000016603583015290921b166038820152604c016040516020818303038152906040528152602001610bef3390565b6001600160a01b031690526124d2565b91508260c00135821015610c555760405162461bcd60e51b815260206004820152600860248201527f555633525f544c52000000000000000000000000000000000000000000000000604482015260640161021e565b50919050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b0316906370a082319060240160206040518083038186803b158015610cd657600080fd5b505afa158015610cea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d0e91906151d2565b905082811015610d605760405162461bcd60e51b815260206004820152600960248201527f41505f4957455448390000000000000000000000000000000000000000000000604482015260640161021e565b8015610e03576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015610de157600080fd5b505af1158015610df5573d6000803e3d6000fd5b50505050610e038282613383565b505050565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152600019602483015283169063095ea7b390604401602060405180830381600087803b158015610e6c57600080fd5b505af1158015610e80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea491906151eb565b506001600160a01b039182166000908152600260209081526040808320939094168252919091522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055565b6040517fdd62ed3e000000000000000000000000000000000000000000000000000000008152336004820152306024820152600019906001600160a01b0388169063dd62ed3e9060440160206040518083038186803b158015610f5957600080fd5b505afa158015610f6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9191906151d2565b101561100f576040517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101869052606481018590526001608482015260ff841660a482015260c4810183905260e481018290526001600160a01b03871690638fcbaf0c90610104016106a1565b505050505050565b600081608001358042111561106e5760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b61118660a0840135611086608086016060870161512b565b611097610100870160e0880161512b565b60405180604001604052808860200160208101906110b5919061512b565b60006110c760608c0160408d016151ad565b6110d460208d018d61512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b7fffffff000000000000000000000000000000000000000000000000000000000016603583015290921b166038820152604c0160405160208183030381529060405281526020016105b73390565b91508260c001358211156106215760405162461bcd60e51b815260206004820152600860248201527f555633525f544d52000000000000000000000000000000000000000000000000604482015260640161021e565b600061123c7f00000000000000000000000000000000000000000000000000000000000000008786868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061349c92505050565b60008151811061124e5761124e61520d565b60200260200101519050848111156112a85760405162461bcd60e51b815260206004820152600860248201527f555632525f544d52000000000000000000000000000000000000000000000000604482015260640161021e565b611353848460008181106112be576112be61520d565b90506020020160208101906112d3919061512b565b3361134d7f00000000000000000000000000000000000000000000000000000000000000008888600081811061130b5761130b61520d565b9050602002016020810190611320919061512b565b898960018181106113335761133361520d565b9050602002016020810190611348919061512b565b6135ef565b846129ed565b6001600160a01b03821615611368578161136a565b305b91506113aa84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525086925061368d915050565b95945050505050565b60008413806113c25750600083135b61140e5760405162461bcd60e51b815260206004820152600660248201527f53525f4941440000000000000000000000000000000000000000000000000000604482015260640161021e565b600061141c828401846152b5565b90506000806000611430846000015161374c565b92509250925061146a7f00000000000000000000000032d1f0dce675902f89d72251db4ab1d728efa19c61146585858561379e565b613813565b60008060008a1361149057846001600160a01b0316846001600160a01b031610896114a7565b836001600160a01b0316856001600160a01b0316108a5b9150915081156114c6576114c185876020015161134d3390565b6106cf565b85516114d1906123db565b156114f65785516114e190612633565b86526114f08133600089613878565b506106cf565b806000819055508394506106cf85876020015161134d3390565b60008160800135804211156115675760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b61166a60a084013561157f608086016060870161512b565b611590610100870160e0880161512b565b60408051808201909152806115a860208a018a61512b565b60006115ba60608c0160408d016152ea565b6115ca60408d0160208e0161512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b603583015290921b166038820152604c01604051602081830303815290604052815260200161165a3390565b6001600160a01b03169052612711565b91508260c00135821015610c555760405162461bcd60e51b815260206004820152600660248201527f53525f544c520000000000000000000000000000000000000000000000000000604482015260640161021e565b60608167ffffffffffffffff8111156116db576116db614c4c565b60405190808252806020026020018201604052801561170e57816020015b60608152602001906001900390816116f95790505b50905060005b828110156117a25761177d8484838181106117315761173161520d565b90506020028101906117439190615148565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a2692505050565b82828151811061178f5761178f61520d565b6020908102919091010152600101611714565b5092915050565b60008160400151804211156118005760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b335b600061181185600001516123db565b905061185d85606001518261182a57866020015161182c565b305b600060405180604001604052806118468b60000151612452565b8152602001876001600160a01b0316815250612711565b6060860152801561187d57845130925061187690612633565b855261188a565b8460600151935050611890565b50611802565b83608001518310156108165760405162461bcd60e51b815260206004820152600660248201527f53525f544c520000000000000000000000000000000000000000000000000000604482015260640161021e565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015285906001600160a01b0388169063dd62ed3e9060440160206040518083038186803b15801561194457600080fd5b505afa158015611958573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197c91906151d2565b101561100f576040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018690526064810185905260ff8416608482015260a4810183905260c481018290526001600160a01b0387169063d505accf9060e4016106a1565b6000816080013580421115611a495760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b611b4f60a0840135611a61608086016060870161512b565b611a72610100870160e0880161512b565b6040518060400160405280886020016020810190611a90919061512b565b6000611aa260608c0160408d016152ea565b611aaf60208d018d61512b565b604051606094851b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000908116602083015260f89490941b7fff0000000000000000000000000000000000000000000000000000000000000016603482015260e89290921b603583015290921b166038820152604c016040516020818303038152906040528152602001611b3f3390565b6001600160a01b03169052613878565b91508260c00135821115611ba55760405162461bcd60e51b815260206004820152600660248201527f53525f544d520000000000000000000000000000000000000000000000000000604482015260640161021e565b50600019600055919050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038516906370a082319060240160206040518083038186803b158015611c0c57600080fd5b505afa158015611c20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4491906151d2565b905082811015611c965760405162461bcd60e51b815260206004820152600760248201527f41505f49544b4e00000000000000000000000000000000000000000000000000604482015260640161021e565b8015611ca757611ca7848383613ac8565b50505050565b6000816040013580421115611d045760405162461bcd60e51b815260206004820152600660248201527f41505f54544f0000000000000000000000000000000000000000000000000000604482015260640161021e565b611d746060840135611d1c604086016020870161512b565b6040805180820190915260009080611d348980615148565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060200133611b3f565b5060005491508260800135821115611ba55760405162461bcd60e51b815260206004820152600660248201527f53525f544d520000000000000000000000000000000000000000000000000000604482015260640161021e565b6040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018690526064810185905260ff8416608482015260a4810183905260c481018290526001600160a01b0387169063d505accf9060e4016106a1565b6000841380611e4d5750600083135b611e5657600080fd5b6000611e64828401846152b5565b90506000806000611e78846000015161374c565b925092509250611eaa7f0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f984848484613b8f565b60008060008a13611ed057846001600160a01b0316846001600160a01b03161089611ee7565b836001600160a01b0316856001600160a01b0316108a5b915091508115611f01576114c185876020015161134d3390565b8551611f0c906123db565b15611f2b578551611f1c90612633565b86526114f08133600089612207565b600181905560208601516106cf9085903361134d565b6000611fac84846000818110611f5957611f5961520d565b9050602002016020810190611f6e919061512b565b33611fa67f00000000000000000000000000000000000000000000000000000000000000008888600081811061130b5761130b61520d565b896129ed565b6001600160a01b03821615611fc15781611fc3565b305b915060008484611fd460018261533c565b818110611fe357611fe361520d565b9050602002016020810190611ff8919061512b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03858116600483015291909116906370a082319060240160206040518083038186803b15801561205457600080fd5b505afa158015612068573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208c91906151d2565b90506120cc85858080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525087925061368d915050565b8085856120da60018261533c565b8181106120e9576120e961520d565b90506020020160208101906120fe919061512b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b03868116600483015291909116906370a082319060240160206040518083038186803b15801561215a57600080fd5b505afa15801561216e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219291906151d2565b61219c919061533c565b9150858210156121ee5760405162461bcd60e51b815260206004820152600860248201527f555632525f544c52000000000000000000000000000000000000000000000000604482015260640161021e565b5095945050505050565b6001600160a01b03163b151590565b60006001600160a01b0384161561221e5783612220565b305b93506000806000612234856000015161374c565b919450925090506001600160a01b0380841690831610600080612258858786613ba3565b6001600160a01b031663128acb088b856122718f613bd9565b61227a90615353565b6001600160a01b038e161561228f578d6122b5565b876122ae5773fffd8963efd1fc6a506488495d951d5263988d256122b5565b6401000276a45b8d6040516020016122c691906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016122f59594939291906153d1565b6040805180830381600087803b15801561230e57600080fd5b505af1158015612322573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612346919061540b565b91509150600083612360578161235b84615353565b61236a565b8261236a83615353565b90985090506001600160a01b038a166123cc578b81146123cc5760405162461bcd60e51b815260206004820152600960248201527f555633525f49414f520000000000000000000000000000000000000000000000604482015260640161021e565b50505050505050949350505050565b600060046123e8836126ee565b60ff16101561241e5760146123ff6003600161542f565b61240a90601461542f565b612414919061542f565b8251119050919050565b601461242c60016002615447565b61243860146002615447565b61244390600161542f565b6123ff919061542f565b919050565b6060600461245f836126ee565b60ff16101561249e576124986000601461247b6003600161542f565b61248690601461542f565b612490919061542f565b849190613c75565b92915050565b612498600060146124b160016002615447565b6124bd60146002615447565b6124c890600161542f565b61247b919061542f565b60006001600160a01b038416156124e957836124eb565b305b935060008060006124ff856000015161374c565b919450925090506001600160a01b0380831690841610600080612523868686613ba3565b6001600160a01b031663128acb088b8561253c8f613bd9565b6001600160a01b038e1615612551578d612577565b876125705773fffd8963efd1fc6a506488495d951d5263988d25612577565b6401000276a45b8d60405160200161258891906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016125b79594939291906153d1565b6040805180830381600087803b1580156125d057600080fd5b505af11580156125e4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612608919061540b565b91509150826126175781612619565b805b61262290615353565b96505050505050505b949350505050565b60606004612640836126ee565b60ff161015612686576124986126586003600161542f565b61266390601461542f565b61266f6003600161542f565b61267a90601461542f565b8451612490919061533c565b61249861269560016002615447565b6126a160146002615447565b6126ac90600161542f565b6126b6919061542f565b6126c190601461542f565b6126cd60016002615447565b6126d960146002615447565b6126e490600161542f565b61266f919061542f565b6000816014815181106127035761270361520d565b016020015160f81c92915050565b60006001600160a01b03841615612728578361272a565b305b935060008061273884613e14565b91509150600080836001600160a01b031663128acb0889856127598d613bd9565b6001600160a01b038c161561276e578b612792565b8761278d5773fff6fbe64b68d618d47c209fe40b0d8ee6e23c91612792565b620f18825b8b6040516020016127a391906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016127d29594939291906153d1565b6040805180830381600087803b1580156127eb57600080fd5b505af11580156127ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612823919061540b565b91509150826128325781612834565b805b61283d90615353565b9998505050505050505050565b60008060006128588661374c565b5091509150612893828661288d7f000000000000000000000000000000000000000000000000000000000000000086866135ef565b8a6129ed565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152600091908316906370a082319060240160206040518083038186803b1580156128f157600080fd5b505afa158015612905573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292991906151d2565b9050612936838387613e5b565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301528291908416906370a082319060240160206040518083038186803b15801561299357600080fd5b505afa1580156129a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129cb91906151d2565b6129d5919061533c565b98975050505050505050565b600061249882826140d4565b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b0316846001600160a01b0316148015612a2e5750804710155b15612b1e57612a5d7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab182613383565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152602482018390527f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1169063a9059cbb90604401602060405180830381600087803b158015612ae057600080fd5b505af1158015612af4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1891906151eb565b50611ca7565b6001600160a01b038316301415612b3f57612b3a848383613ac8565b611ca7565b611ca7848484846141a4565b600080600080612b5a876141f5565b6001600160a01b038084166000908152600260209081526040808320855190941683529290522054929550909350915060ff16612c825780516040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260001960248201529084169063095ea7b390604401602060405180830381600087803b158015612bf857600080fd5b505af1158015612c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3091906151eb565b506001600160a01b038084166000908152600260209081526040808320855190941683529290522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790555b60ff861660051415612d2b578051604080830151606084015191517f3df02124000000000000000000000000000000000000000000000000000000008152600091820b600482015291810b6024830152604482018b905260648201526001600160a01b0390911690633df02124906084015b600060405180830381600087803b158015612d0e57600080fd5b505af1158015612d22573d6000803e3d6000fd5b505050506132ca565b60ff861660061415612da1578051604080830151606084015191517fa6417ed6000000000000000000000000000000000000000000000000000000008152600091820b600482015291810b6024830152604482018b905260648201526001600160a01b039091169063a6417ed690608401612cf4565b60ff861660071415612e18578051604080830151606084015191517f5b41b90800000000000000000000000000000000000000000000000000000000815260ff918216600482015291166024820152604481018a9052600060648201526001600160a01b0390911690635b41b90890608401612cf4565b60ff861660081415612e8f578051604080830151606084015191517f65b2489b00000000000000000000000000000000000000000000000000000000815260ff918216600482015291166024820152604481018a9052600060648201526001600160a01b03909116906365b2489b90608401612cf4565b60ff8616600b1415612f4557612ea3614b4f565b8881836040015160ff1660028110612ebd57612ebd61520d565b602002015281516040517f0b4c7e4d0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690630b4c7e4d90612f0d908490600090600401615466565b600060405180830381600087803b158015612f2757600080fd5b505af1158015612f3b573d6000803e3d6000fd5b50505050506132ca565b60ff8616600c1415612fc357612f59614b6d565b8881836040015160ff1660038110612f7357612f7361520d565b602002015281516040517f4515cef30000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690634515cef390612f0d9084906000906004016154c1565b60ff8616600d141561304457612fd7614b6d565b8881836040015160ff1660038110612ff157612ff161520d565b602002015281516040517f2b6e993a0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690632b6e993a90612f0d9084906000906001906004016154dc565b60ff8616600e14156130b057805160608201516040517f1a4d01d2000000000000000000000000000000000000000000000000000000008152600481018b9052600091820b602482015260448101919091526001600160a01b0390911690631a4d01d290606401612cf4565b60ff8616600f141561317657805160608201516040517f517a55a3000000000000000000000000000000000000000000000000000000008152600481018b9052600091820b60248201526044810191909152600160648201526001600160a01b039091169063517a55a390608401602060405180830381600087803b15801561313857600080fd5b505af115801561314c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317091906151d2565b506132ca565b60ff8616600914156131f85780516020820151604080840151606085015191517f7981c43e0000000000000000000000000000000000000000000000000000000081526001600160a01b039384166004820152600091820b602482015291810b6044830152606482018c90526084820152911690637981c43e9060a401612cf4565b60ff8616600a14156132825780516020820151604080840151606085015191517f2bf78c610000000000000000000000000000000000000000000000000000000081526001600160a01b03938416600482015260ff918216602482015291166044820152606481018b905260006084820181905260a4820152911690632bf78c619060c401612cf4565b60405162461bcd60e51b815260206004820152600660248201527f4352515f49500000000000000000000000000000000000000000000000000000604482015260640161021e565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a082319060240160206040518083038186803b15801561332257600080fd5b505afa158015613336573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061335a91906151d2565b93506001600160a01b038516301461337857613378823087876129ed565b505050949350505050565b804710156133d35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e6365000000604482015260640161021e565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613420576040519150601f19603f3d011682016040523d82523d6000602084013e613425565b606091505b5050905080610e035760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d61792068617665207265766572746564000000000000606482015260840161021e565b60606002825110156134ad57600080fd5b815167ffffffffffffffff8111156134c7576134c7614c4c565b6040519080825280602002602001820160405280156134f0578160200160208202803683370190505b509050828160018351613503919061533c565b815181106135135761351361520d565b60200260200101818152505060006001835161352f919061533c565b90505b80156135e757600080613582878661354b60018761533c565b8151811061355b5761355b61520d565b60200260200101518786815181106135755761357561520d565b6020026020010151614252565b915091506135aa84848151811061359b5761359b61520d565b60200260200101518383614339565b846135b660018661533c565b815181106135c6576135c661520d565b602002602001018181525050505080806135df90615500565b915050613532565b509392505050565b60008060006135fe85856143ec565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606084811b8216602084015283901b166034820152919350915061368390604801604051602081830303815290604052805190602001207f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f60001b88614450565b9695505050505050565b60005b6001835103811015610e03576000808483815181106136b1576136b161520d565b60200260200101518584600101815181106136ce576136ce61520d565b6020026020010151915091506000600286510384106136ed5784613734565b6137347f0000000000000000000000000000000000000000000000000000000000000000838887600201815181106137275761372761520d565b60200260200101516135ef565b9050613741838383613e5b565b505050600101613690565b6000808061375a84826140d4565b925061377261376b6001601461542f565b859061447a565b90506137956137836003600161542f565b61378e90601461542f565b85906140d4565b91509193909250565b6040805160608101825260008082526020820181905291810191909152826001600160a01b0316846001600160a01b031611156137d9579192915b6040518060600160405280856001600160a01b03168152602001846001600160a01b031681526020018360020b81525090505b9392505050565b3361381e838361453a565b6001600160a01b0316146138745760405162461bcd60e51b815260206004820152600560248201527f43565f4943000000000000000000000000000000000000000000000000000000604482015260640161021e565b5050565b60006001600160a01b0384161561388f5783613891565b305b935060008061389f846145de565b91509150600080836001600160a01b031663128acb0889856138c08d613bd9565b6138c990615353565b6001600160a01b038c16156138de578b613902565b876138fd5773fff6fbe64b68d618d47c209fe40b0d8ee6e23c91613902565b620f18825b8b60405160200161391391906153be565b6040516020818303038152906040526040518663ffffffff1660e01b81526004016139429594939291906153d1565b6040805180830381600087803b15801561395b57600080fd5b505af115801561396f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613993919061540b565b915091506000836139ad57816139a884615353565b6139b7565b826139b783615353565b90965090506001600160a01b038816613a1957898114613a195760405162461bcd60e51b815260206004820152600760248201527f53525f49414f5200000000000000000000000000000000000000000000000000604482015260640161021e565b5050505050949350505050565b6060600080306001600160a01b031684604051613a439190615517565b600060405180830381855af49150503d8060008114613a7e576040519150601f19603f3d011682016040523d82523d6000602084013e613a83565b606091505b509150915061262b82826040518060400160405280600681526020017f4d5f4c4443460000000000000000000000000000000000000000000000000000815250614625565b6040516001600160a01b038316602482015260448101829052610e039084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261463e565b611ca784613b9e858585614723565b61478e565b600061262b7f0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f984613bd4868686614723565b614795565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821115613c715760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e206160448201527f6e20696e74323536000000000000000000000000000000000000000000000000606482015260840161021e565b5090565b606081613c8381601f61542f565b1015613cd15760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161021e565b82613cdc838261542f565b1015613d2a5760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161021e565b613d34828461542f565b84511015613d845760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015260640161021e565b606082158015613da35760405191506000825260208201604052613e0b565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015613ddc578051835260209283019201613dc4565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6000806000806000613e29866000015161374c565b925092509250613e3a83838361483a565b826001600160a01b0316846001600160a01b03161094509450505050915091565b6000613e6784846143ec565b5090506000613e977f000000000000000000000000000000000000000000000000000000000000000086866135ef565b9050600080600080846001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613ed857600080fd5b505afa158015613eec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f109190615551565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff169150600080876001600160a01b03168b6001600160a01b031614613f58578284613f5b565b83835b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015292945090925083918d16906370a082319060240160206040518083038186803b158015613fbd57600080fd5b505afa158015613fd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff591906151d2565b613fff919061533c565b955061400c868383614870565b945050505050600080856001600160a01b0316896001600160a01b0316146140365782600061403a565b6000835b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915291935091506001600160a01b0386169063022c0d9f9061409790859085908c9060248101615596565b600060405180830381600087803b1580156140b157600080fd5b505af11580156140c5573d6000803e3d6000fd5b50505050505050505050505050565b6000816140e281601461542f565b10156141305760405162461bcd60e51b815260206004820152601260248201527f746f416464726573735f6f766572666c6f770000000000000000000000000000604482015260640161021e565b61413b82601461542f565b8351101561418b5760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e64730000000000000000000000604482015260640161021e565b5001602001516c01000000000000000000000000900490565b6040516001600160a01b0380851660248301528316604482015260648101829052611ca79085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613b0d565b6040805160808101825260008082526020820181905291810182905260608101829052819061422384614928565b60ff90811660608801521660408601526001600160a01b03908116602086015216835290959094509092509050565b600080600061426185856143ec565b5090506000806142728888886135ef565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156142aa57600080fd5b505afa1580156142be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142e29190615551565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff169150826001600160a01b0316876001600160a01b03161461432757808261432a565b81815b90999098509650505050505050565b600080841161438a5760405162461bcd60e51b815260206004820152600860248201527f5556324c5f494f41000000000000000000000000000000000000000000000000604482015260640161021e565b60008311801561439a5750600082115b6143a357600080fd5b60006143af8585615447565b6143bb906103e8615447565b905060006143c9868561533c565b6143d5906103e5615447565b90506143e181836155c4565b61368390600161542f565b600080826001600160a01b0316846001600160a01b0316141561440e57600080fd5b826001600160a01b0316846001600160a01b03161061442e578284614431565b83835b90925090506001600160a01b03821661444957600080fd5b9250929050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b60008161448881600361542f565b10156144d65760405162461bcd60e51b815260206004820152601160248201527f746f55696e7432345f6f766572666c6f77000000000000000000000000000000604482015260640161021e565b6144e182600361542f565b835110156145315760405162461bcd60e51b815260206004820152601460248201527f746f55696e7432345f6f75744f66426f756e6473000000000000000000000000604482015260640161021e565b50016003015190565b600081602001516001600160a01b031682600001516001600160a01b03161061456257600080fd5b815160208084015160408086015181516001600160a01b0395861694810194909452939091169082015260029190910b606082015261380c90608001604051602081830303815290604052805190602001207f884a6891a166f885bf6f0a3b330a25e41d1761a5aa091110a229d9a0e34b2c3660001b85614450565b60008060008060006145f3866000015161374c565b92509250925061460482848361483a565b836001600160a01b0316836001600160a01b03161094509450505050915091565b6060831561463457508161380c565b61380c8383614a3a565b6000614693826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614a649092919063ffffffff16565b805190915015610e0357808060200190518101906146b191906151eb565b610e035760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161021e565b6040805160608101825260008082526020820181905291810191909152826001600160a01b0316846001600160a01b0316111561475e579192915b50604080516060810182526001600160a01b03948516815292909316602083015262ffffff169181019190915290565b3361381e83835b600081602001516001600160a01b031682600001516001600160a01b0316106147bd57600080fd5b815160208084015160408086015181516001600160a01b0395861694810194909452939091169082015262ffffff909116606082015261380c90608001604051602081830303815290604052805190602001207fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460001b85614450565b600061262b7f00000000000000000000000032d1f0dce675902f89d72251db4ab1d728efa19c61486b86868661379e565b61453a565b60008084116148c15760405162461bcd60e51b815260206004820152600860248201527f5556324c5f494941000000000000000000000000000000000000000000000000604482015260640161021e565b6000831180156148d15750600082115b6148da57600080fd5b60006148e8856103e5615447565b905060006148f68483615447565b9050600082614907876103e8615447565b614911919061542f565b905061491d81836155c4565b979650505050505050565b6000808080808061493987826140d4565b955061495161494a6001601461542f565b88906140d4565b935061496d601461496360018261542f565b61494a919061542f565b92508660148061497e60018261542f565b614988919061542f565b614992919061542f565b815181106149a2576149a261520d565b016020015160f81c91508660016014806149bc838261542f565b6149c6919061542f565b6149d0919061542f565b6149da919061542f565b815181106149ea576149ea61520d565b016020015160f81c9050614a2f614a0360016002615447565b614a0f60146002615447565b614a1a90600161542f565b614a24919061542f565b61494a90601461542f565b945091939550919395565b815115614a4a5781518083602001fd5b8060405162461bcd60e51b815260040161021e91906155ff565b606061262b848460008585600080866001600160a01b03168587604051614a8b9190615517565b60006040518083038185875af1925050503d8060008114614ac8576040519150601f19603f3d011682016040523d82523d6000602084013e614acd565b606091505b509150915061491d8783838760608315614b45578251614b3e576001600160a01b0385163b614b3e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161021e565b508161262b565b61262b8383614a3a565b60405180604001604052806002906020820280368337509192915050565b60405180606001604052806003906020820280368337509192915050565b600060a08284031215610c5557600080fd5b600060208284031215614baf57600080fd5b813567ffffffffffffffff811115614bc657600080fd5b61262b84828501614b8b565b6001600160a01b0381168114614be757600080fd5b50565b60008060008060008060c08789031215614c0357600080fd5b8635614c0e81614bd2565b95506020870135945060408701359350606087013560ff81168114614c3257600080fd5b9598949750929560808101359460a0909101359350915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112614c8c57600080fd5b813567ffffffffffffffff80821115614ca757614ca7614c4c565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715614ced57614ced614c4c565b81604052838152866020858801011115614d0657600080fd5b836020870160208301376000602085830101528094505050505092915050565b600060a08284031215614d3857600080fd5b60405160a0810167ffffffffffffffff8282108183111715614d5c57614d5c614c4c565b816040528293508435915080821115614d7457600080fd5b50614d8185828601614c7b565b8252506020830135614d9281614bd2565b806020830152506040830135604082015260608301356060820152608083013560808201525092915050565b600060208284031215614dd057600080fd5b813567ffffffffffffffff811115614de757600080fd5b61262b84828501614d26565b60006101008284031215610c5557600080fd5b60006101008284031215614e1957600080fd5b61380c8383614df3565b60008060408385031215614e3657600080fd5b823591506020830135614e4881614bd2565b809150509250929050565b60008060408385031215614e6657600080fd5b8235614e7181614bd2565b91506020830135614e4881614bd2565b60008083601f840112614e9357600080fd5b50813567ffffffffffffffff811115614eab57600080fd5b6020830191508360208260051b850101111561444957600080fd5b600080600080600060808688031215614ede57600080fd5b8535945060208601359350604086013567ffffffffffffffff811115614f0357600080fd5b614f0f88828901614e81565b9094509250506060860135614f2381614bd2565b809150509295509295909350565b60008060008060608587031215614f4757600080fd5b8435935060208501359250604085013567ffffffffffffffff80821115614f6d57600080fd5b818701915087601f830112614f8157600080fd5b813581811115614f9057600080fd5b886020828501011115614fa257600080fd5b95989497505060200194505050565b60008060208385031215614fc457600080fd5b823567ffffffffffffffff811115614fdb57600080fd5b614fe785828601614e81565b90969095509350505050565b60005b8381101561500e578181015183820152602001614ff6565b83811115611ca75750506000910152565b60008151808452615037816020860160208601614ff3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156150dc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526150ca85835161501f565b94509285019290850190600101615090565b5092979650505050505050565b6000806000606084860312156150fe57600080fd5b833561510981614bd2565b925060208401359150604084013561512081614bd2565b809150509250925092565b60006020828403121561513d57600080fd5b813561380c81614bd2565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261517d57600080fd5b83018035915067ffffffffffffffff82111561519857600080fd5b60200191503681900382131561444957600080fd5b6000602082840312156151bf57600080fd5b813562ffffff8116811461380c57600080fd5b6000602082840312156151e457600080fd5b5051919050565b6000602082840312156151fd57600080fd5b8151801515811461380c57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006040828403121561524e57600080fd5b6040516040810167ffffffffffffffff828210818311171561527257615272614c4c565b81604052829350843591508082111561528a57600080fd5b5061529785828601614c7b565b82525060208301356152a881614bd2565b6020919091015292915050565b6000602082840312156152c757600080fd5b813567ffffffffffffffff8111156152de57600080fd5b61262b8482850161523c565b6000602082840312156152fc57600080fd5b81358060020b811461380c57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561534e5761534e61530d565b500390565b60007f80000000000000000000000000000000000000000000000000000000000000008214156153855761538561530d565b5060000390565b60008151604084526153a1604085018261501f565b6020938401516001600160a01b0316949093019390935250919050565b60208152600061380c602083018461538c565b60006001600160a01b038088168352861515602084015285604084015280851660608401525060a0608083015261491d60a083018461501f565b6000806040838503121561541e57600080fd5b505080516020909101519092909150565b600082198211156154425761544261530d565b500190565b60008160001904831182151516156154615761546161530d565b500290565b60608101818460005b600281101561548e57815183526020928301929091019060010161546f565b5050508260408301529392505050565b8060005b6003811015611ca75781518452602093840193909101906001016154a2565b608081016154cf828561549e565b8260608301529392505050565b60a081016154ea828661549e565b8360608301528215156080830152949350505050565b60008161550f5761550f61530d565b506000190190565b60008251615529818460208701614ff3565b9190910192915050565b80516dffffffffffffffffffffffffffff8116811461244d57600080fd5b60008060006060848603121561556657600080fd5b61556f84615533565b925061557d60208501615533565b9150604084015163ffffffff8116811461512057600080fd5b8481528360208201526001600160a01b0383166040820152608060608201526000613683608083018461501f565b6000826155fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60208152600061380c602083018461501f56fea2646970667358221220db31f1b9509a9ae1aa16f9965cbbd2c30d8d00524e839300031e477739a3940a64736f6c63430008090033

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

00000000000000000000000032d1f0dce675902f89d72251db4ab1d728efa19c0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f984000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1

-----Decoded View---------------
Arg [0] : _gridexGridFactory (address): 0x32d1F0Dce675902f89D72251DB4AB1d728efa19c
Arg [1] : _uniswapV3PoolFactory (address): 0x1F98431c8aD98523631AE4a59f267346ea31F984
Arg [2] : _uniswapV2PoolFactory (address): 0x0000000000000000000000000000000000000000
Arg [3] : _weth9 (address): 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 00000000000000000000000032d1f0dce675902f89d72251db4ab1d728efa19c
Arg [1] : 0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f984
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1


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  ]
[ 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.