ETH Price: $1,743.47 (+10.45%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Order Disabl...124316202021-05-14 9:13:281439 days ago1620983608IN
0x22812EA9...4e486606b
0 ETH0.00602999124
Set Order Disabl...124316162021-05-14 9:12:231439 days ago1620983543IN
0x22812EA9...4e486606b
0 ETH0.00602491124
Set Order Disabl...124316122021-05-14 9:11:511439 days ago1620983511IN
0x22812EA9...4e486606b
0 ETH0.00601846124
Set Order Disabl...124316022021-05-14 9:09:151439 days ago1620983355IN
0x22812EA9...4e486606b
0 ETH0.00601201124
Set Order Disabl...124315972021-05-14 9:07:471439 days ago1620983267IN
0x22812EA9...4e486606b
0 ETH0.00611704126
Set Order Disabl...124315882021-05-14 9:06:141439 days ago1620983174IN
0x22812EA9...4e486606b
0 ETH0.00620748128
Set Order Disabl...124315862021-05-14 9:05:411439 days ago1620983141IN
0x22812EA9...4e486606b
0 ETH0.00626269129
Set Order Disabl...124315802021-05-14 9:04:361439 days ago1620983076IN
0x22812EA9...4e486606b
0 ETH0.00620748128
Set Order Disabl...124315762021-05-14 9:03:111439 days ago1620982991IN
0x22812EA9...4e486606b
0 ETH0.00627468129
Set Order Disabl...124315732021-05-14 9:02:471439 days ago1620982967IN
0x22812EA9...4e486606b
0 ETH0.0064638133
Set Order Disabl...124315702021-05-14 9:02:281439 days ago1620982948IN
0x22812EA9...4e486606b
0 ETH0.00655398135
Set Order Disabl...124315652021-05-14 9:01:281439 days ago1620982888IN
0x22812EA9...4e486606b
0 ETH0.00630448130
Set Order Disabl...124315582021-05-14 8:59:381439 days ago1620982778IN
0x22812EA9...4e486606b
0 ETH0.00655398135
Set Order Disabl...124315482021-05-14 8:57:081439 days ago1620982628IN
0x22812EA9...4e486606b
0 ETH0.01066912220
Execute124311682021-05-14 7:30:201439 days ago1620977420IN
0x22812EA9...4e486606b
0 ETH0.0248843100.00000145
Withdraw124311402021-05-14 7:25:061439 days ago1620977106IN
0x22812EA9...4e486606b
0.06978428 ETH0.0198944693
Execute124311002021-05-14 7:15:421439 days ago1620976542IN
0x22812EA9...4e486606b
0 ETH0.0263135299.1
Withdraw124310722021-05-14 7:10:251439 days ago1620976225IN
0x22812EA9...4e486606b
0.07020058 ETH0.0205999696
Execute124310602021-05-14 7:08:251439 days ago1620976105IN
0x22812EA9...4e486606b
0 ETH0.02468062106.5
Withdraw124310362021-05-14 7:03:131439 days ago1620975793IN
0x22812EA9...4e486606b
0.07053089 ETH0.02652744124
Execute124307852021-05-14 6:06:201439 days ago1620972380IN
0x22812EA9...4e486606b
0 ETH0.02757116117
Withdraw124307712021-05-14 6:01:141439 days ago1620972074IN
0x22812EA9...4e486606b
0.07080387 ETH0.03218745150
Execute124305942021-05-14 5:17:301439 days ago1620969450IN
0x22812EA9...4e486606b
0 ETH0.03636622145.09231
Deposit124305782021-05-14 5:12:181439 days ago1620969138IN
0x22812EA9...4e486606b
0.73846526 ETH0.025518881
Execute124305242021-05-14 5:01:041439 days ago1620968464IN
0x22812EA9...4e486606b
0 ETH0.0197867192
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
-124311682021-05-14 7:30:201439 days ago1620977420
0x22812EA9...4e486606b
0.02331475 ETH
-124311682021-05-14 7:30:201439 days ago1620977420
0x22812EA9...4e486606b
0.04314646 ETH
-124311402021-05-14 7:25:061439 days ago1620977106
0x22812EA9...4e486606b
0.00332306 ETH
-124311002021-05-14 7:15:421439 days ago1620976542
0x22812EA9...4e486606b
0.02079831 ETH
-124311002021-05-14 7:15:421439 days ago1620976542
0x22812EA9...4e486606b
0.04605938 ETH
-124310722021-05-14 7:10:251439 days ago1620976225
0x22812EA9...4e486606b
0.00334288 ETH
-124310602021-05-14 7:08:251439 days ago1620976105
0x22812EA9...4e486606b
0.02629906 ETH
-124310602021-05-14 7:08:251439 days ago1620976105
0x22812EA9...4e486606b
0.04087321 ETH
-124310362021-05-14 7:03:131439 days ago1620975793
0x22812EA9...4e486606b
0.00335861 ETH
-124307852021-05-14 6:06:201439 days ago1620972380
0x22812EA9...4e486606b
0.02577341 ETH
-124307852021-05-14 6:06:201439 days ago1620972380
0x22812EA9...4e486606b
0.04165884 ETH
-124307712021-05-14 6:01:141439 days ago1620972074
0x22812EA9...4e486606b
0.00337161 ETH
-124305942021-05-14 5:17:301439 days ago1620969450
0x22812EA9...4e486606b
0.0643718 ETH
-124305942021-05-14 5:17:301439 days ago1620969450
0x22812EA9...4e486606b
0.04335347 ETH
-124305782021-05-14 5:12:181439 days ago1620969138
0x22812EA9...4e486606b
0.62535372 ETH
-124305782021-05-14 5:12:181439 days ago1620969138
0x22812EA9...4e486606b
0.00538626 ETH
-124305242021-05-14 5:01:041439 days ago1620968464
0x22812EA9...4e486606b
0.02928554 ETH
-124305242021-05-14 5:01:041439 days ago1620968464
0x22812EA9...4e486606b
0.03862971 ETH
-124304962021-05-14 4:55:451439 days ago1620968145
0x22812EA9...4e486606b
0.00339576 ETH
-124304192021-05-14 4:39:501439 days ago1620967190
0x22812EA9...4e486606b
0.02667377 ETH
-124304192021-05-14 4:39:501439 days ago1620967190
0x22812EA9...4e486606b
0.04163418 ETH
-124303952021-05-14 4:34:101439 days ago1620966850
0x22812EA9...4e486606b
0.00341539 ETH
-124301592021-05-14 3:40:361439 days ago1620963636
0x22812EA9...4e486606b
0.02398271 ETH
-124301592021-05-14 3:40:361439 days ago1620963636
0x22812EA9...4e486606b
0.0445827 ETH
-124301362021-05-14 3:34:581439 days ago1620963298
0x22812EA9...4e486606b
0.00342827 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
IntegralDelay

Compiler Version
v0.7.5+commit.eb77ed08

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license
File 1 of 17 : IntegralDelay.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import 'IIntegralPair.sol';
import 'IIntegralDelay.sol';
import 'IIntegralOracle.sol';
import 'IWETH.sol';
import 'SafeMath.sol';
import 'Normalizer.sol';
import 'Orders.sol';
import 'TokenShares.sol';
import 'AddLiquidity.sol';
import 'BuyHelper.sol';

contract IntegralDelay is IIntegralDelay {
    using SafeMath for uint256;
    using Normalizer for uint256;
    using Orders for Orders.Data;
    using TokenShares for TokenShares.Data;
    Orders.Data internal orders;
    TokenShares.Data internal tokenShares;

    uint256 public constant ORDER_CANCEL_TIME = 24 hours;
    uint256 private constant ORDER_EXECUTED_COST = 3700;

    address public override owner;
    mapping(address => bool) public override isBot;
    uint256 public override botExecuteTime;

    constructor(
        address _factory,
        address _weth,
        address _bot
    ) {
        orders.factory = _factory;
        owner = msg.sender;
        isBot[_bot] = true;
        orders.gasPrice = tx.gasprice - (tx.gasprice % 1e6);
        tokenShares.setWeth(_weth);
        orders.delay = 5 minutes;
        botExecuteTime = 4 * orders.delay;
        orders.maxGasLimit = 5000000;
        orders.gasPriceInertia = 20000000;
        orders.maxGasPriceImpact = 1000000;
    }

    function getTransferGasCost(address token) public view override returns (uint256 gasCost) {
        return orders.transferGasCosts[token];
    }

    function getDepositOrder(uint256 orderId) public view override returns (Orders.DepositOrder memory order) {
        return orders.getDepositOrder(orderId);
    }

    function getWithdrawOrder(uint256 orderId) public view override returns (Orders.WithdrawOrder memory order) {
        return orders.getWithdrawOrder(orderId);
    }

    function getSellOrder(uint256 orderId) public view override returns (Orders.SellOrder memory order) {
        return orders.getSellOrder(orderId);
    }

    function getBuyOrder(uint256 orderId) public view override returns (Orders.BuyOrder memory order) {
        return orders.getBuyOrder(orderId);
    }

    function getDepositDisabled(address pair) public view override returns (bool) {
        return orders.depositDisabled[pair];
    }

    function getWithdrawDisabled(address pair) public view override returns (bool) {
        return orders.withdrawDisabled[pair];
    }

    function getBuyDisabled(address pair) public view override returns (bool) {
        return orders.buyDisabled[pair];
    }

    function getSellDisabled(address pair) public view override returns (bool) {
        return orders.sellDisabled[pair];
    }

    function getOrderStatus(uint256 orderId) public view override returns (Orders.OrderStatus) {
        return orders.getOrderStatus(orderId);
    }

    uint256 private unlocked = 1;
    modifier lock() {
        require(unlocked == 1, 'ID_LOCKED');
        unlocked = 0;
        _;
        unlocked = 1;
    }

    function factory() public view override returns (address) {
        return orders.factory;
    }

    function totalShares(address token) public view override returns (uint256) {
        return tokenShares.totalShares[token];
    }

    function weth() public view override returns (address) {
        return tokenShares.weth;
    }

    function delay() public view override returns (uint256) {
        return orders.delay;
    }

    function lastProcessedOrderId() public view returns (uint256) {
        return orders.lastProcessedOrderId;
    }

    function newestOrderId() public view returns (uint256) {
        return orders.newestOrderId;
    }

    function getOrder(uint256 orderId) public view returns (Orders.OrderType orderType, uint256 validAfterTimestamp) {
        return orders.getOrder(orderId);
    }

    function isOrderCanceled(uint256 orderId) public view returns (bool) {
        return orders.canceled[orderId];
    }

    function maxGasLimit() public view override returns (uint256) {
        return orders.maxGasLimit;
    }

    function maxGasPriceImpact() public view override returns (uint256) {
        return orders.maxGasPriceImpact;
    }

    function gasPriceInertia() public view override returns (uint256) {
        return orders.gasPriceInertia;
    }

    function gasPrice() public view override returns (uint256) {
        return orders.gasPrice;
    }

    function setOrderDisabled(
        address pair,
        Orders.OrderType orderType,
        bool disabled
    ) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        require(orderType != Orders.OrderType.Empty, 'ID_INVALID_ORDER_TYPE');
        if (orderType == Orders.OrderType.Deposit) {
            orders.depositDisabled[pair] = disabled;
        } else if (orderType == Orders.OrderType.Withdraw) {
            orders.withdrawDisabled[pair] = disabled;
        } else if (orderType == Orders.OrderType.Sell) {
            orders.sellDisabled[pair] = disabled;
        } else if (orderType == Orders.OrderType.Buy) {
            orders.buyDisabled[pair] = disabled;
        }
        emit OrderDisabled(pair, orderType, disabled);
    }

    function setOwner(address _owner) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        owner = _owner;
        emit OwnerSet(owner);
    }

    function setBot(address _bot, bool _isBot) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        isBot[_bot] = _isBot;
        emit BotSet(_bot, _isBot);
    }

    function setMaxGasLimit(uint256 _maxGasLimit) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        orders.setMaxGasLimit(_maxGasLimit);
    }

    function setDelay(uint256 _delay) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        orders.delay = _delay;
        botExecuteTime = 4 * _delay;
        emit DelaySet(_delay);
    }

    function setGasPriceInertia(uint256 _gasPriceInertia) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        orders.setGasPriceInertia(_gasPriceInertia);
    }

    function setMaxGasPriceImpact(uint256 _maxGasPriceImpact) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        orders.setMaxGasPriceImpact(_maxGasPriceImpact);
    }

    function setTransferGasCost(address token, uint256 gasCost) public override {
        require(msg.sender == owner, 'ID_FORBIDDEN');
        orders.setTransferGasCost(token, gasCost);
    }

    function deposit(Orders.DepositParams calldata depositParams)
        external
        payable
        override
        lock
        returns (uint256 orderId)
    {
        orders.deposit(depositParams, tokenShares);
        return orders.newestOrderId;
    }

    function withdraw(Orders.WithdrawParams calldata withdrawParams)
        external
        payable
        override
        lock
        returns (uint256 orderId)
    {
        orders.withdraw(withdrawParams);
        return orders.newestOrderId;
    }

    function sell(Orders.SellParams calldata sellParams) external payable override lock returns (uint256 orderId) {
        orders.sell(sellParams, tokenShares);
        return orders.newestOrderId;
    }

    function buy(Orders.BuyParams calldata buyParams) external payable override lock returns (uint256 orderId) {
        orders.buy(buyParams, tokenShares);
        return orders.newestOrderId;
    }

    function execute(uint256 n) public override lock {
        emit Execute(msg.sender, n);
        uint256 gasBefore = gasleft();
        bool orderExecuted = false;
        for (uint256 i = 0; i < n; i++) {
            if (isOrderCanceled(orders.lastProcessedOrderId + 1)) {
                orders.dequeueCanceledOrder();
                continue;
            }
            (Orders.OrderType orderType, uint256 validAfterTimestamp) = orders.getNextOrder();
            if (orderType == Orders.OrderType.Empty || validAfterTimestamp >= block.timestamp) {
                break;
            }
            require(
                block.timestamp >= validAfterTimestamp + botExecuteTime || isBot[msg.sender] || isBot[address(0)],
                'ID_FORBIDDEN'
            );
            orderExecuted = true;
            if (orderType == Orders.OrderType.Deposit) {
                executeDeposit();
            } else if (orderType == Orders.OrderType.Withdraw) {
                executeWithdraw();
            } else if (orderType == Orders.OrderType.Sell) {
                executeSell();
            } else if (orderType == Orders.OrderType.Buy) {
                executeBuy();
            }
        }
        if (orderExecuted) {
            orders.updateGasPrice(gasBefore.sub(gasleft()));
        }
    }

    function executeDeposit() internal {
        uint256 gasStart = gasleft();
        Orders.DepositOrder memory depositOrder = orders.dequeueDepositOrder();
        (, address token0, address token1) = orders.getPairInfo(depositOrder.pairId);
        (bool executionSuccess, bytes memory data) = address(this).call{
            gas: depositOrder.gasLimit.sub(
                Orders.ORDER_BASE_COST.add(orders.transferGasCosts[token0]).add(orders.transferGasCosts[token1])
            )
        }(abi.encodeWithSelector(this._executeDeposit.selector, depositOrder));
        bool refundSuccess = true;
        if (!executionSuccess) {
            refundSuccess = refundTokens(
                depositOrder.to,
                token0,
                depositOrder.share0,
                token1,
                depositOrder.share1,
                depositOrder.unwrap
            );
        }
        if (!refundSuccess) {
            orders.markRefundFailed();
        } else {
            orders.forgetLastProcessedOrder();
        }
        (uint256 gasUsed, uint256 ethRefund) = refund(
            depositOrder.gasLimit,
            depositOrder.gasPrice,
            gasStart,
            depositOrder.to
        );
        emit OrderExecuted(orders.lastProcessedOrderId, executionSuccess, data, gasUsed, ethRefund);
    }

    function executeWithdraw() internal {
        uint256 gasStart = gasleft();
        Orders.WithdrawOrder memory withdrawOrder = orders.dequeueWithdrawOrder();
        (address pair, , ) = orders.getPairInfo(withdrawOrder.pairId);
        (bool executionSuccess, bytes memory data) = address(this).call{
            gas: withdrawOrder.gasLimit.sub(Orders.ORDER_BASE_COST.add(Orders.PAIR_TRANSFER_COST))
        }(abi.encodeWithSelector(this._executeWithdraw.selector, withdrawOrder));
        bool refundSuccess = true;
        if (!executionSuccess) {
            refundSuccess = refundLiquidity(pair, withdrawOrder.to, withdrawOrder.liquidity);
        }
        if (!refundSuccess) {
            orders.markRefundFailed();
        } else {
            orders.forgetLastProcessedOrder();
        }
        (uint256 gasUsed, uint256 ethRefund) = refund(
            withdrawOrder.gasLimit,
            withdrawOrder.gasPrice,
            gasStart,
            withdrawOrder.to
        );
        emit OrderExecuted(orders.lastProcessedOrderId, executionSuccess, data, gasUsed, ethRefund);
    }

    function executeSell() internal {
        uint256 gasStart = gasleft();
        Orders.SellOrder memory sellOrder = orders.dequeueSellOrder();
        (, address token0, address token1) = orders.getPairInfo(sellOrder.pairId);
        (bool executionSuccess, bytes memory data) = address(this).call{
            gas: sellOrder.gasLimit.sub(
                Orders.ORDER_BASE_COST.add(orders.transferGasCosts[sellOrder.inverse ? token1 : token0])
            )
        }(abi.encodeWithSelector(this._executeSell.selector, sellOrder));
        bool refundSuccess = true;
        if (!executionSuccess) {
            refundSuccess = refundToken(
                sellOrder.inverse ? token1 : token0,
                sellOrder.to,
                sellOrder.shareIn,
                sellOrder.unwrap
            );
        }
        if (!refundSuccess) {
            orders.markRefundFailed();
        } else {
            orders.forgetLastProcessedOrder();
        }
        (uint256 gasUsed, uint256 ethRefund) = refund(sellOrder.gasLimit, sellOrder.gasPrice, gasStart, sellOrder.to);
        emit OrderExecuted(orders.lastProcessedOrderId, executionSuccess, data, gasUsed, ethRefund);
    }

    function executeBuy() internal {
        uint256 gasStart = gasleft();
        Orders.BuyOrder memory buyOrder = orders.dequeueBuyOrder();
        (, address token0, address token1) = orders.getPairInfo(buyOrder.pairId);
        (bool executionSuccess, bytes memory data) = address(this).call{
            gas: buyOrder.gasLimit.sub(
                Orders.ORDER_BASE_COST.add(orders.transferGasCosts[buyOrder.inverse ? token1 : token0])
            )
        }(abi.encodeWithSelector(this._executeBuy.selector, buyOrder));
        bool refundSuccess = true;
        if (!executionSuccess) {
            refundSuccess = refundToken(
                buyOrder.inverse ? token1 : token0,
                buyOrder.to,
                buyOrder.shareInMax,
                buyOrder.unwrap
            );
        }
        if (!refundSuccess) {
            orders.markRefundFailed();
        } else {
            orders.forgetLastProcessedOrder();
        }
        (uint256 gasUsed, uint256 ethRefund) = refund(buyOrder.gasLimit, buyOrder.gasPrice, gasStart, buyOrder.to);
        emit OrderExecuted(orders.lastProcessedOrderId, executionSuccess, data, gasUsed, ethRefund);
    }

    function refund(
        uint256 gasLimit,
        uint256 gasPriceInOrder,
        uint256 gasStart,
        address to
    ) private returns (uint256 gasUsed, uint256 leftOver) {
        uint256 feeCollected = gasLimit.mul(gasPriceInOrder);
        gasUsed = gasStart.sub(gasleft()).add(Orders.REFUND_END_COST).add(ORDER_EXECUTED_COST);
        uint256 actualRefund = Math.min(feeCollected, gasUsed.mul(orders.gasPrice));
        leftOver = feeCollected.sub(actualRefund);
        require(refundEth(msg.sender, actualRefund), 'ID_ETH_REFUND_FAILED');
        refundEth(payable(to), leftOver);
    }

    function refundEth(address payable to, uint256 value) internal returns (bool success) {
        if (value == 0) {
            return true;
        }
        success = to.send(value);
        emit EthRefund(to, success, value);
    }

    function refundToken(
        address token,
        address to,
        uint256 share,
        bool unwrap
    ) private returns (bool) {
        if (share == 0) {
            return true;
        }
        (bool success, bytes memory data) = address(this).call{ gas: orders.transferGasCosts[token] }(
            abi.encodeWithSelector(this._refundToken.selector, token, to, share, unwrap)
        );
        if (!success) {
            emit RefundFailed(to, token, share, data);
        }
        return success;
    }

    function refundTokens(
        address to,
        address token0,
        uint256 share0,
        address token1,
        uint256 share1,
        bool unwrap
    ) private returns (bool) {
        (bool success, bytes memory data) = address(this).call{
            gas: orders.transferGasCosts[token0].add(orders.transferGasCosts[token1])
        }(abi.encodeWithSelector(this._refundTokens.selector, to, token0, share0, token1, share1, unwrap));
        if (!success) {
            emit RefundFailed(to, token0, share0, data);
            emit RefundFailed(to, token1, share1, data);
        }
        return success;
    }

    function _refundTokens(
        address to,
        address token0,
        uint256 share0,
        address token1,
        uint256 share1,
        bool unwrap
    ) external {
        // no need to check sender, because it is checked in _refundToken
        _refundToken(token0, to, share0, unwrap);
        _refundToken(token1, to, share1, unwrap);
    }

    function _refundToken(
        address token,
        address to,
        uint256 share,
        bool unwrap
    ) public {
        require(msg.sender == address(this), 'ID_FORBIDDEN');
        if (token == tokenShares.weth && unwrap) {
            uint256 amount = tokenShares.sharesToAmount(token, share);
            IWETH(tokenShares.weth).withdraw(amount);
            payable(to).transfer(amount);
        } else {
            return TransferHelper.safeTransfer(token, to, tokenShares.sharesToAmount(token, share));
        }
    }

    function refundLiquidity(
        address pair,
        address to,
        uint256 liquidity
    ) private returns (bool) {
        if (liquidity == 0) {
            return true;
        }
        (bool success, bytes memory data) = address(this).call{ gas: Orders.PAIR_TRANSFER_COST }(
            abi.encodeWithSelector(this._refundLiquidity.selector, pair, to, liquidity, false)
        );
        if (!success) {
            emit RefundFailed(to, pair, liquidity, data);
        }
        return success;
    }

    function _refundLiquidity(
        address pair,
        address to,
        uint256 liquidity
    ) public {
        require(msg.sender == address(this), 'ID_FORBIDDEN');
        return TransferHelper.safeTransfer(pair, to, liquidity);
    }

    function _executeDeposit(Orders.DepositOrder memory depositOrder) public {
        require(msg.sender == address(this), 'ID_FORBIDDEN');
        require(depositOrder.deadline >= block.timestamp, 'ID_EXPIRED');

        (address pair, address token0, address token1, uint256 amount0Left, uint256 amount1Left) = _initialDeposit(
            depositOrder
        );
        if (
            (amount0Left != 0 || amount1Left != 0) &&
            AddLiquidity.canSwap(
                depositOrder.initialRatio,
                depositOrder.minRatioChangeToSwap,
                orders.pairs[depositOrder.pairId].pair
            )
        ) {
            if (amount0Left != 0) {
                (amount0Left, amount1Left) = AddLiquidity.swapDeposit0(
                    pair,
                    token0,
                    amount0Left,
                    depositOrder.minSwapPrice
                );
            } else if (amount1Left != 0) {
                (amount0Left, amount1Left) = AddLiquidity.swapDeposit1(
                    pair,
                    token1,
                    amount1Left,
                    depositOrder.maxSwapPrice
                );
            }
        }
        if (amount0Left != 0 && amount1Left != 0) {
            (amount0Left, amount1Left) = _addLiquidityAndMint(
                pair,
                depositOrder.to,
                token0,
                token1,
                amount0Left,
                amount1Left
            );
        }

        _refundDeposit(depositOrder.to, token0, token1, amount0Left, amount1Left);
    }

    function _initialDeposit(Orders.DepositOrder memory depositOrder)
        private
        returns (
            address pair,
            address token0,
            address token1,
            uint256 amount0Left,
            uint256 amount1Left
        )
    {
        (pair, token0, token1) = orders.getPairInfo(depositOrder.pairId);
        uint256 amount0Desired = tokenShares.sharesToAmount(token0, depositOrder.share0);
        uint256 amount1Desired = tokenShares.sharesToAmount(token1, depositOrder.share1);
        IIntegralPair(pair).fullSync();
        (amount0Left, amount1Left) = _addLiquidityAndMint(
            pair,
            depositOrder.to,
            token0,
            token1,
            amount0Desired,
            amount1Desired
        );
    }

    function _addLiquidityAndMint(
        address pair,
        address to,
        address token0,
        address token1,
        uint256 amount0Desired,
        uint256 amount1Desired
    ) private returns (uint256 amount0Left, uint256 amount1Left) {
        (uint256 amount0, uint256 amount1) = AddLiquidity.addLiquidity(pair, amount0Desired, amount1Desired);
        if (amount0 == 0 || amount1 == 0) {
            return (amount0Desired, amount1Desired);
        }
        TransferHelper.safeTransfer(token0, pair, amount0);
        TransferHelper.safeTransfer(token1, pair, amount1);
        IIntegralPair(pair).mint(to);

        amount0Left = amount0Desired.sub(amount0);
        amount1Left = amount1Desired.sub(amount1);
    }

    function _refundDeposit(
        address to,
        address token0,
        address token1,
        uint256 amount0,
        uint256 amount1
    ) private {
        if (amount0 > 0) {
            TransferHelper.safeTransfer(token0, to, amount0);
        }
        if (amount1 > 0) {
            TransferHelper.safeTransfer(token1, to, amount1);
        }
    }

    function _executeWithdraw(Orders.WithdrawOrder memory withdrawOrder) public {
        require(msg.sender == address(this), 'ID_FORBIDDEN');
        require(withdrawOrder.deadline >= block.timestamp, 'ID_EXPIRED');

        (address pair, , ) = orders.getPairInfo(withdrawOrder.pairId);
        IIntegralPair(pair).fullSync();
        TransferHelper.safeTransfer(pair, pair, withdrawOrder.liquidity);
        (uint256 amount0, uint256 amount1) = IIntegralPair(pair).burn(withdrawOrder.to);

        require(amount0 >= withdrawOrder.amount0Min && amount1 >= withdrawOrder.amount1Min, 'ID_INSUFFICIENT_AMOUNT');
    }

    function _executeBuy(Orders.BuyOrder memory buyOrder) public {
        require(msg.sender == address(this), 'ID_FORBIDDEN');
        require(buyOrder.deadline >= block.timestamp, 'ID_EXPIRED');

        (address pairAddress, address token0, address token1) = orders.getPairInfo(buyOrder.pairId);
        (address tokenIn, address tokenOut) = buyOrder.inverse ? (token1, token0) : (token0, token1);
        uint256 amountInMax = tokenShares.sharesToAmount(tokenIn, buyOrder.shareInMax);
        IIntegralPair pair = IIntegralPair(pairAddress);
        pair.fullSync();
        uint256 amountIn = buyOrder.inverse
            ? BuyHelper.getSwapAmount1In(pairAddress, buyOrder.amountOut)
            : BuyHelper.getSwapAmount0In(pairAddress, buyOrder.amountOut);
        require(amountInMax >= amountIn, 'ID_INSUFFICIENT_INPUT_AMOUNT');
        (uint256 amount0Out, uint256 amount1Out) = buyOrder.inverse
            ? (buyOrder.amountOut, uint256(0))
            : (uint256(0), buyOrder.amountOut);
        TransferHelper.safeTransfer(tokenIn, pairAddress, amountIn);
        if (tokenOut == tokenShares.weth && buyOrder.unwrap) {
            pair.swap(amount0Out, amount1Out, address(this));
            IWETH(tokenShares.weth).withdraw(buyOrder.amountOut);
            (bool success, ) = buyOrder.to.call{ value: buyOrder.amountOut, gas: Orders.ETHER_TRANSFER_CALL_COST }('');
            if (!success) {
                tokenShares.onUnwrapFailed(buyOrder.to, buyOrder.amountOut);
            }
        } else {
            pair.swap(amount0Out, amount1Out, buyOrder.to);
        }
    }

    function _executeSell(Orders.SellOrder memory sellOrder) public {
        require(msg.sender == address(this), 'ID_FORBIDDEN');
        require(sellOrder.deadline >= block.timestamp, 'ID_EXPIRED');

        (address pairAddress, address token0, address token1) = orders.getPairInfo(sellOrder.pairId);
        (address tokenIn, address tokenOut) = sellOrder.inverse ? (token1, token0) : (token0, token1);
        uint256 amountIn = tokenShares.sharesToAmount(tokenIn, sellOrder.shareIn);
        IIntegralPair pair = IIntegralPair(pairAddress);
        pair.fullSync();
        TransferHelper.safeTransfer(tokenIn, pairAddress, amountIn);
        uint256 amountOut = sellOrder.inverse ? pair.getSwapAmount0Out(amountIn) : pair.getSwapAmount1Out(amountIn);
        require(amountOut >= sellOrder.amountOutMin, 'ID_INSUFFICIENT_OUTPUT_AMOUNT');
        (uint256 amount0Out, uint256 amount1Out) = sellOrder.inverse
            ? (amountOut, uint256(0))
            : (uint256(0), amountOut);
        if (tokenOut == tokenShares.weth && sellOrder.unwrap) {
            pair.swap(amount0Out, amount1Out, address(this));
            IWETH(tokenShares.weth).withdraw(amountOut);
            (bool success, ) = sellOrder.to.call{ value: amountOut, gas: Orders.ETHER_TRANSFER_CALL_COST }('');
            if (!success) {
                tokenShares.onUnwrapFailed(sellOrder.to, amountOut);
            }
        } else {
            pair.swap(amount0Out, amount1Out, sellOrder.to);
        }
    }

    function performRefund(
        Orders.OrderType orderType,
        uint256 validAfterTimestamp,
        uint256 orderId,
        bool shouldRefundEth
    ) internal {
        bool canOwnerRefund = validAfterTimestamp.add(365 days) < block.timestamp;
        if (orderType == Orders.OrderType.Deposit) {
            Orders.DepositOrder memory depositOrder = orders.getDepositOrder(orderId);
            (, address token0, address token1) = orders.getPairInfo(depositOrder.pairId);
            address to = canOwnerRefund ? owner : depositOrder.to;
            require(
                refundTokens(to, token0, depositOrder.share0, token1, depositOrder.share1, depositOrder.unwrap),
                'ID_REFUND_FAILED'
            );
            if (shouldRefundEth) {
                uint256 value = depositOrder.gasPrice.mul(depositOrder.gasLimit);
                require(refundEth(payable(to), value), 'ID_ETH_REFUND_FAILED');
            }
        } else if (orderType == Orders.OrderType.Withdraw) {
            Orders.WithdrawOrder memory withdrawOrder = orders.getWithdrawOrder(orderId);
            (address pair, , ) = orders.getPairInfo(withdrawOrder.pairId);
            address to = canOwnerRefund ? owner : withdrawOrder.to;
            require(refundLiquidity(pair, to, withdrawOrder.liquidity), 'ID_REFUND_FAILED');
            if (shouldRefundEth) {
                uint256 value = withdrawOrder.gasPrice.mul(withdrawOrder.gasLimit);
                require(refundEth(payable(to), value), 'ID_ETH_REFUND_FAILED');
            }
        } else if (orderType == Orders.OrderType.Sell) {
            Orders.SellOrder memory sellOrder = orders.getSellOrder(orderId);
            (, address token0, address token1) = orders.getPairInfo(sellOrder.pairId);
            address to = canOwnerRefund ? owner : sellOrder.to;
            require(
                refundToken(sellOrder.inverse ? token1 : token0, to, sellOrder.shareIn, sellOrder.unwrap),
                'ID_REFUND_FAILED'
            );
            if (shouldRefundEth) {
                uint256 value = sellOrder.gasPrice.mul(sellOrder.gasLimit);
                require(refundEth(payable(to), value), 'ID_ETH_REFUND_FAILED');
            }
        } else if (orderType == Orders.OrderType.Buy) {
            Orders.BuyOrder memory buyOrder = orders.getBuyOrder(orderId);
            (, address token0, address token1) = orders.getPairInfo(buyOrder.pairId);
            address to = canOwnerRefund ? owner : buyOrder.to;
            require(
                refundToken(buyOrder.inverse ? token1 : token0, to, buyOrder.shareInMax, buyOrder.unwrap),
                'ID_REFUND_FAILED'
            );
            if (shouldRefundEth) {
                uint256 value = buyOrder.gasPrice.mul(buyOrder.gasLimit);
                require(refundEth(payable(to), value), 'ID_ETH_REFUND_FAILED');
            }
        }
        orders.forgetOrder(orderId);
    }

    function retryRefund(uint256 orderId) public lock {
        (Orders.OrderType orderType, uint256 validAfterTimestamp) = orders.getFailedOrderType(orderId);
        performRefund(orderType, validAfterTimestamp, orderId, false);
    }

    function cancelOrder(uint256 orderId) public lock {
        (Orders.OrderType orderType, uint256 validAfterTimestamp) = orders.getOrder(orderId);
        require(validAfterTimestamp.sub(delay()).add(ORDER_CANCEL_TIME) < block.timestamp, 'ID_ORDER_NOT_EXCEEDED');
        performRefund(orderType, validAfterTimestamp, orderId, true);
        orders.canceled[orderId] = true;
    }

    receive() external payable {}
}

File 2 of 17 : IERC20.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

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

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

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

    function decimals() external view returns (uint8);

    function totalSupply() external view returns (uint256);

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

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

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

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

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

File 3 of 17 : IIntegralERC20.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

import 'IERC20.sol';

interface IIntegralERC20 is IERC20 {
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    function PERMIT_TYPEHASH() external pure returns (bytes32);

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

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

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

    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
}

File 4 of 17 : IReserves.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

interface IReserves {
    event Sync(uint112 reserve0, uint112 reserve1);
    event Fees(uint256 fee0, uint256 fee1);

    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 lastTimestamp
        );

    function getReferences()
        external
        view
        returns (
            uint112 reference0,
            uint112 reference1,
            uint32 epoch
        );

    function getFees() external view returns (uint256 fee0, uint256 fee1);
}

File 5 of 17 : IIntegralPair.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

import 'IIntegralERC20.sol';
import 'IReserves.sol';

interface IIntegralPair is IIntegralERC20, IReserves {
    event Mint(address indexed sender, address indexed to);
    event Burn(address indexed sender, address indexed to);
    event Swap(address indexed sender, address indexed to);
    event SetMintFee(uint256 fee);
    event SetBurnFee(uint256 fee);
    event SetSwapFee(uint256 fee);
    event SetOracle(address account);
    event SetTrader(address trader);
    event SetToken0AbsoluteLimit(uint256 limit);
    event SetToken1AbsoluteLimit(uint256 limit);
    event SetToken0RelativeLimit(uint256 limit);
    event SetToken1RelativeLimit(uint256 limit);
    event SetPriceDeviationLimit(uint256 limit);

    function MINIMUM_LIQUIDITY() external pure returns (uint256);

    function factory() external view returns (address);

    function token0() external view returns (address);

    function token1() external view returns (address);

    function oracle() external view returns (address);

    function trader() external view returns (address);

    function mintFee() external view returns (uint256);

    function setMintFee(uint256 fee) external;

    function mint(address to) external returns (uint256 liquidity);

    function burnFee() external view returns (uint256);

    function setBurnFee(uint256 fee) external;

    function burn(address to) external returns (uint256 amount0, uint256 amount1);

    function swapFee() external view returns (uint256);

    function setSwapFee(uint256 fee) external;

    function setOracle(address account) external;

    function setTrader(address account) external;

    function token0AbsoluteLimit() external view returns (uint256);

    function setToken0AbsoluteLimit(uint256 limit) external;

    function token1AbsoluteLimit() external view returns (uint256);

    function setToken1AbsoluteLimit(uint256 limit) external;

    function token0RelativeLimit() external view returns (uint256);

    function setToken0RelativeLimit(uint256 limit) external;

    function token1RelativeLimit() external view returns (uint256);

    function setToken1RelativeLimit(uint256 limit) external;

    function priceDeviationLimit() external view returns (uint256);

    function setPriceDeviationLimit(uint256 limit) external;

    function collect(address to) external;

    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to
    ) external;

    function sync() external;

    function initialize(
        address _token0,
        address _token1,
        address _oracle,
        address _trader
    ) external;

    function syncWithOracle() external;

    function fullSync() external;

    function getSpotPrice() external view returns (uint256 spotPrice);

    function getSwapAmount0In(uint256 amount1Out) external view returns (uint256 swapAmount0In);

    function getSwapAmount1In(uint256 amount0Out) external view returns (uint256 swapAmount1In);

    function getSwapAmount0Out(uint256 amount1In) external view returns (uint256 swapAmount0Out);

    function getSwapAmount1Out(uint256 amount0In) external view returns (uint256 swapAmount1Out);

    function getDepositAmount0In(uint256 amount0) external view returns (uint256 depositAmount0In);

    function getDepositAmount1In(uint256 amount1) external view returns (uint256 depositAmount1In);
}

File 6 of 17 : SafeMath.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)

library SafeMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, 'SM_ADD_OVERFLOW');
    }

    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = sub(x, y, 'SM_SUB_UNDERFLOW');
    }

    function sub(
        uint256 x,
        uint256 y,
        string memory message
    ) internal pure returns (uint256 z) {
        require((z = x - y) <= x, message);
    }

    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(y == 0 || (z = x * y) / y == x, 'SM_MUL_OVERFLOW');
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, 'SM_DIV_BY_ZERO');
        uint256 c = a / b;
        return c;
    }

    function ceil_div(uint256 a, uint256 b) internal pure returns (uint256 c) {
        c = div(a, b);
        if (c == mul(a, b)) {
            return c;
        } else {
            return add(c, 1);
        }
    }
}

File 7 of 17 : Math.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

// a library for performing various math operations

library Math {
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x < y ? x : y;
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x > y ? x : y;
    }

    // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }
}

File 8 of 17 : IIntegralFactory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

interface IIntegralFactory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
    event OwnerSet(address owner);

    function owner() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);

    function allPairs(uint256) external view returns (address pair);

    function allPairsLength() external view returns (uint256);

    function createPair(
        address tokenA,
        address tokenB,
        address oracle,
        address trader
    ) external returns (address pair);

    function setOwner(address) external;

    function setMintFee(
        address tokenA,
        address tokenB,
        uint256 fee
    ) external;

    function setBurnFee(
        address tokenA,
        address tokenB,
        uint256 fee
    ) external;

    function setSwapFee(
        address tokenA,
        address tokenB,
        uint256 fee
    ) external;

    function setOracle(
        address tokenA,
        address tokenB,
        address oracle
    ) external;

    function setTrader(
        address tokenA,
        address tokenB,
        address trader
    ) external;

    function collect(
        address tokenA,
        address tokenB,
        address to
    ) external;

    function withdraw(
        address tokenA,
        address tokenB,
        uint256 amount,
        address to
    ) external;
}

File 9 of 17 : IWETH.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity =0.7.5;

interface IWETH {
    function deposit() external payable;

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

    function withdraw(uint256) external;
}

File 10 of 17 : TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TH_APPROVE_FAILED');
    }

    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TH_TRANSFER_FAILED');
    }

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TH_TRANSFER_FROM_FAILED');
    }

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

File 11 of 17 : TokenShares.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

import 'IERC20.sol';
import 'IWETH.sol';
import 'SafeMath.sol';
import 'TransferHelper.sol';

library TokenShares {
    using SafeMath for uint256;
    using TransferHelper for address;

    event UnwrapFailed(address to, uint256 amount);

    struct Data {
        mapping(address => uint256) totalShares;
        address weth;
    }

    function setWeth(Data storage data, address _weth) internal {
        data.weth = _weth;
    }

    function sharesToAmount(
        Data storage data,
        address token,
        uint256 share
    ) external returns (uint256) {
        if (share == 0) {
            return 0;
        }
        if (token == data.weth) {
            return share;
        }
        require(data.totalShares[token] >= share, 'TS_INSUFFICIENT_BALANCE');
        uint256 balance = IERC20(token).balanceOf(address(this));
        uint256 value = balance.mul(share).div(data.totalShares[token]);
        data.totalShares[token] = data.totalShares[token].sub(share);
        return value;
    }

    function amountToShares(
        Data storage data,
        address token,
        uint256 amount,
        bool wrap
    ) external returns (uint256) {
        if (amount == 0) {
            return 0;
        }
        if (token == data.weth) {
            if (wrap) {
                require(msg.value >= amount, 'TS_INSUFFICIENT_AMOUNT');
                IWETH(token).deposit{ value: amount }();
            } else {
                token.safeTransferFrom(msg.sender, address(this), amount);
            }
            return amount;
        } else {
            uint256 balanceBefore = IERC20(token).balanceOf(address(this));
            require(balanceBefore > 0 || data.totalShares[token] == 0, 'TS_INVALID_SHARES');
            if (data.totalShares[token] == 0) {
                data.totalShares[token] = balanceBefore;
            }
            token.safeTransferFrom(msg.sender, address(this), amount);
            uint256 balanceAfter = IERC20(token).balanceOf(address(this));
            require(balanceAfter > balanceBefore, 'TS_INVALID_TRANSFER');
            if (balanceBefore > 0) {
                uint256 lastShares = data.totalShares[token];
                data.totalShares[token] = lastShares.mul(balanceAfter).div(balanceBefore);
                return data.totalShares[token] - lastShares;
            } else {
                data.totalShares[token] = balanceAfter;
                data.totalShares[token] = balanceAfter;
                return balanceAfter;
            }
        }
    }

    function onUnwrapFailed(
        Data storage data,
        address to,
        uint256 amount
    ) external {
        emit UnwrapFailed(to, amount);
        IWETH(data.weth).deposit{ value: amount }();
        TransferHelper.safeTransfer(data.weth, to, amount);
    }
}

File 12 of 17 : Orders.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import 'SafeMath.sol';
import 'Math.sol';
import 'IIntegralFactory.sol';
import 'IIntegralPair.sol';
import 'TokenShares.sol';

library Orders {
    using SafeMath for uint256;
    using TokenShares for TokenShares.Data;
    using TransferHelper for address;

    enum OrderType { Empty, Deposit, Withdraw, Sell, Buy }
    enum OrderStatus { NonExistent, EnqueuedWaiting, EnqueuedReady, ExecutedSucceeded, ExecutedFailed, Canceled }

    event MaxGasLimitSet(uint256 maxGasLimit);
    event GasPriceInertiaSet(uint256 gasPriceInertia);
    event MaxGasPriceImpactSet(uint256 maxGasPriceImpact);
    event TransferGasCostSet(address token, uint256 gasCost);

    event DepositEnqueued(uint256 indexed orderId, uint128 validAfterTimestamp, uint256 gasPrice);
    event WithdrawEnqueued(uint256 indexed orderId, uint128 validAfterTimestamp, uint256 gasPrice);
    event SellEnqueued(uint256 indexed orderId, uint128 validAfterTimestamp, uint256 gasPrice);
    event BuyEnqueued(uint256 indexed orderId, uint128 validAfterTimestamp, uint256 gasPrice);

    uint8 private constant DEPOSIT_TYPE = 1;
    uint8 private constant WITHDRAW_TYPE = 2;
    uint8 private constant BUY_TYPE = 3;
    uint8 private constant BUY_INVERTED_TYPE = 4;
    uint8 private constant SELL_TYPE = 5;
    uint8 private constant SELL_INVERTED_TYPE = 6;

    uint8 private constant UNWRAP_NOT_FAILED = 0;
    uint8 private constant KEEP_NOT_FAILED = 1;
    uint8 private constant UNWRAP_FAILED = 2;
    uint8 private constant KEEP_FAILED = 3;

    uint256 private constant ETHER_TRANSFER_COST = 2300;
    uint256 private constant BUFFER_COST = 10000;
    uint256 private constant EXECUTE_PREPARATION_COST = 55000; // dequeue + getPair in execute

    uint256 public constant ETHER_TRANSFER_CALL_COST = 10000;
    uint256 public constant PAIR_TRANSFER_COST = 55000;
    uint256 public constant REFUND_END_COST = 2 * ETHER_TRANSFER_COST + BUFFER_COST;
    uint256 public constant ORDER_BASE_COST = EXECUTE_PREPARATION_COST + REFUND_END_COST;

    uint256 private constant TIMESTAMP_OFFSET = 1609455600; // 2021 Jan 1

    struct PairInfo {
        address pair;
        address token0;
        address token1;
    }

    struct Data {
        uint256 delay;
        uint256 newestOrderId;
        uint256 lastProcessedOrderId;
        mapping(uint256 => StoredOrder) orderQueue;
        address factory;
        uint256 maxGasLimit;
        uint256 gasPrice;
        uint256 gasPriceInertia;
        uint256 maxGasPriceImpact;
        mapping(uint32 => PairInfo) pairs;
        mapping(address => uint256) transferGasCosts;
        mapping(uint256 => bool) canceled;
        mapping(address => bool) depositDisabled;
        mapping(address => bool) withdrawDisabled;
        mapping(address => bool) buyDisabled;
        mapping(address => bool) sellDisabled;
    }

    struct StoredOrder {
        // slot 1
        uint8 orderType;
        uint32 validAfterTimestamp;
        uint8 unwrapAndFailure;
        uint32 deadline;
        uint32 gasLimit;
        uint32 gasPrice;
        uint112 liquidityOrRatio;
        // slot 1
        uint112 value0;
        uint112 value1;
        uint32 pairId;
        // slot2
        address to;
        uint32 minRatioChangeToSwap;
        uint32 minSwapPrice;
        uint32 maxSwapPrice;
    }

    struct DepositOrder {
        uint32 pairId;
        uint256 share0;
        uint256 share1;
        uint256 initialRatio;
        uint256 minRatioChangeToSwap;
        uint256 minSwapPrice;
        uint256 maxSwapPrice;
        bool unwrap;
        address to;
        uint256 gasPrice;
        uint256 gasLimit;
        uint256 deadline;
    }

    struct WithdrawOrder {
        uint32 pairId;
        uint256 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        bool unwrap;
        address to;
        uint256 gasPrice;
        uint256 gasLimit;
        uint256 deadline;
    }

    struct SellOrder {
        uint32 pairId;
        bool inverse;
        uint256 shareIn;
        uint256 amountOutMin;
        bool unwrap;
        address to;
        uint256 gasPrice;
        uint256 gasLimit;
        uint256 deadline;
    }

    struct BuyOrder {
        uint32 pairId;
        bool inverse;
        uint256 shareInMax;
        uint256 amountOut;
        bool unwrap;
        address to;
        uint256 gasPrice;
        uint256 gasLimit;
        uint256 deadline;
    }

    function decodeType(uint256 internalType) internal pure returns (OrderType orderType) {
        if (internalType == DEPOSIT_TYPE) {
            orderType = OrderType.Deposit;
        } else if (internalType == WITHDRAW_TYPE) {
            orderType = OrderType.Withdraw;
        } else if (internalType == BUY_TYPE) {
            orderType = OrderType.Buy;
        } else if (internalType == BUY_INVERTED_TYPE) {
            orderType = OrderType.Buy;
        } else if (internalType == SELL_TYPE) {
            orderType = OrderType.Sell;
        } else if (internalType == SELL_INVERTED_TYPE) {
            orderType = OrderType.Sell;
        } else {
            orderType = OrderType.Empty;
        }
    }

    function getOrder(Data storage data, uint256 orderId)
        public
        view
        returns (OrderType orderType, uint256 validAfterTimestamp)
    {
        StoredOrder storage order = data.orderQueue[orderId];
        uint8 internalType = order.orderType;
        validAfterTimestamp = uint32ToTimestamp(order.validAfterTimestamp);
        orderType = decodeType(internalType);
    }

    function getOrderStatus(Data storage data, uint256 orderId) external view returns (OrderStatus orderStatus) {
        if (orderId > data.newestOrderId) {
            return OrderStatus.NonExistent;
        }
        if (data.canceled[orderId]) {
            return OrderStatus.Canceled;
        }
        if (isRefundFailed(data, orderId)) {
            return OrderStatus.ExecutedFailed;
        }
        (OrderType orderType, uint256 validAfterTimestamp) = getOrder(data, orderId);
        if (orderType == OrderType.Empty) {
            return OrderStatus.ExecutedSucceeded;
        }
        if (validAfterTimestamp >= block.timestamp) {
            return OrderStatus.EnqueuedWaiting;
        }
        return OrderStatus.EnqueuedReady;
    }

    function getPair(
        Data storage data,
        address tokenA,
        address tokenB
    )
        internal
        returns (
            address pair,
            uint32 pairId,
            bool inverted
        )
    {
        inverted = tokenA > tokenB;
        (address token0, address token1) = inverted ? (tokenB, tokenA) : (tokenA, tokenB);
        pair = IIntegralFactory(data.factory).getPair(token0, token1);
        pairId = uint32(bytes4(keccak256(abi.encodePacked((pair)))));
        require(pair != address(0), 'OS_PAIR_NONEXISTENT');
        if (data.pairs[pairId].pair == address(0)) {
            data.pairs[pairId] = PairInfo(pair, token0, token1);
        }
    }

    function getPairInfo(Data storage data, uint32 pairId)
        external
        view
        returns (
            address pair,
            address token0,
            address token1
        )
    {
        PairInfo storage info = data.pairs[pairId];
        pair = info.pair;
        token0 = info.token0;
        token1 = info.token1;
    }

    function getDepositOrder(Data storage data, uint256 index) public view returns (DepositOrder memory order) {
        StoredOrder memory stored = data.orderQueue[index];
        require(stored.orderType == DEPOSIT_TYPE, 'OS_INVALID_ORDER_TYPE');
        order.pairId = stored.pairId;
        order.share0 = stored.value0;
        order.share1 = stored.value1;
        order.initialRatio = stored.liquidityOrRatio;
        order.minRatioChangeToSwap = stored.minRatioChangeToSwap;
        order.minSwapPrice = float32ToUint(stored.minSwapPrice);
        order.maxSwapPrice = float32ToUint(stored.maxSwapPrice);
        order.unwrap = getUnwrap(stored.unwrapAndFailure);
        order.to = stored.to;
        order.gasPrice = uint32ToGasPrice(stored.gasPrice);
        order.gasLimit = stored.gasLimit;
        order.deadline = uint32ToTimestamp(stored.deadline);
    }

    function getWithdrawOrder(Data storage data, uint256 index) public view returns (WithdrawOrder memory order) {
        StoredOrder memory stored = data.orderQueue[index];
        require(stored.orderType == WITHDRAW_TYPE, 'OS_INVALID_ORDER_TYPE');
        order.pairId = stored.pairId;
        order.liquidity = stored.liquidityOrRatio;
        order.amount0Min = stored.value0;
        order.amount1Min = stored.value1;
        order.unwrap = getUnwrap(stored.unwrapAndFailure);
        order.to = stored.to;
        order.gasPrice = uint32ToGasPrice(stored.gasPrice);
        order.gasLimit = stored.gasLimit;
        order.deadline = uint32ToTimestamp(stored.deadline);
    }

    function getSellOrder(Data storage data, uint256 index) public view returns (SellOrder memory order) {
        StoredOrder memory stored = data.orderQueue[index];
        require(stored.orderType == SELL_TYPE || stored.orderType == SELL_INVERTED_TYPE, 'OS_INVALID_ORDER_TYPE');
        order.pairId = stored.pairId;
        order.inverse = stored.orderType == SELL_INVERTED_TYPE;
        order.shareIn = stored.value0;
        order.amountOutMin = stored.value1;
        order.unwrap = getUnwrap(stored.unwrapAndFailure);
        order.to = stored.to;
        order.gasPrice = uint32ToGasPrice(stored.gasPrice);
        order.gasLimit = stored.gasLimit;
        order.deadline = uint32ToTimestamp(stored.deadline);
    }

    function getBuyOrder(Data storage data, uint256 index) public view returns (BuyOrder memory order) {
        StoredOrder memory stored = data.orderQueue[index];
        require(stored.orderType == BUY_TYPE || stored.orderType == BUY_INVERTED_TYPE, 'OS_INVALID_ORDER_TYPE');
        order.pairId = stored.pairId;
        order.inverse = stored.orderType == BUY_INVERTED_TYPE;
        order.shareInMax = stored.value0;
        order.amountOut = stored.value1;
        order.unwrap = getUnwrap(stored.unwrapAndFailure);
        order.to = stored.to;
        order.gasPrice = uint32ToGasPrice(stored.gasPrice);
        order.gasLimit = stored.gasLimit;
        order.deadline = uint32ToTimestamp(stored.deadline);
    }

    function getFailedOrderType(Data storage data, uint256 orderId)
        external
        view
        returns (OrderType orderType, uint256 validAfterTimestamp)
    {
        require(isRefundFailed(data, orderId), 'OS_NO_POSSIBLE_REFUND');
        (orderType, validAfterTimestamp) = getOrder(data, orderId);
    }

    function getUnwrap(uint8 unwrapAndFailure) private pure returns (bool) {
        return unwrapAndFailure == UNWRAP_FAILED || unwrapAndFailure == UNWRAP_NOT_FAILED;
    }

    function getUnwrapAndFailure(bool unwrap) private pure returns (uint8) {
        return unwrap ? UNWRAP_NOT_FAILED : KEEP_NOT_FAILED;
    }

    function timestampToUint32(uint256 timestamp) private pure returns (uint32 timestamp32) {
        if (timestamp == uint256(-1)) {
            return uint32(-1);
        }
        timestamp32 = uintToUint32(timestamp.sub(TIMESTAMP_OFFSET));
    }

    function uint32ToTimestamp(uint32 timestamp32) private pure returns (uint256 timestamp) {
        if (timestamp32 == uint32(-1)) {
            return uint256(-1);
        }
        if (timestamp32 == 0) {
            return 0;
        }
        timestamp = uint256(timestamp32) + TIMESTAMP_OFFSET;
    }

    function gasPriceToUint32(uint256 gasPrice) private pure returns (uint32 gasPrice32) {
        require((gasPrice / 1e6) * 1e6 == gasPrice, 'OS_GAS_PRICE_PRECISION');
        gasPrice32 = uintToUint32(gasPrice / 1e6);
    }

    function uint32ToGasPrice(uint32 gasPrice32) public pure returns (uint256 gasPrice) {
        gasPrice = uint256(gasPrice32) * 1e6;
    }

    function uintToUint32(uint256 number) private pure returns (uint32 number32) {
        number32 = uint32(number);
        require(uint256(number32) == number, 'OS_OVERFLOW_32');
    }

    function uintToUint112(uint256 number) private pure returns (uint112 number112) {
        number112 = uint112(number);
        require(uint256(number112) == number, 'OS_OVERFLOW_112');
    }

    function uintToFloat32(uint256 number) internal pure returns (uint32 float32) {
        // Number is encoded on 4 bytes. 3 bytes for mantissa and 1 for exponent.
        // If the number fits in the mantissa we set the exponent to zero and return.
        if (number < 2 << 24) {
            return uint32(number << 8);
        }
        // We find the exponent by counting the number of trailing zeroes.
        // Simultaneously we remove those zeroes from the number.
        uint32 exponent;
        for (exponent = 0; exponent < 256 - 24; exponent++) {
            // Last bit is one.
            if (number & 1 == 1) {
                break;
            }
            number = number >> 1;
        }
        // The number must fit in the mantissa.
        require(number < 2 << 24, 'OS_OVERFLOW_FLOAT_ENCODE');
        // Set the first three bytes to the number and the fourth to the exponent.
        float32 = uint32(number << 8) | exponent;
    }

    function float32ToUint(uint32 float32) internal pure returns (uint256 number) {
        // Number is encoded on 4 bytes. 3 bytes for mantissa and 1 for exponent.
        // We get the exponent by extracting the last byte.
        uint256 exponent = float32 & 0xFF;
        // Sanity check. Only triggered for values not encoded with uintToFloat32.
        require(exponent <= 256 - 24, 'OS_OVERFLOW_FLOAT_DECODE');
        // We get the mantissa by extracting the first three bytes and removing the fourth.
        uint256 mantissa = (float32 & 0xFFFFFF00) >> 8;
        // We add exponent number zeroes after the mantissa.
        number = mantissa << exponent;
    }

    function enqueueDepositOrder(Data storage data, DepositOrder memory depositOrder) internal {
        data.newestOrderId++;
        uint128 validAfterTimestamp = uint128(block.timestamp + data.delay);
        emit DepositEnqueued(data.newestOrderId, validAfterTimestamp, depositOrder.gasPrice);
        data.orderQueue[data.newestOrderId] = StoredOrder(
            DEPOSIT_TYPE,
            timestampToUint32(validAfterTimestamp),
            getUnwrapAndFailure(depositOrder.unwrap),
            timestampToUint32(depositOrder.deadline),
            uintToUint32(depositOrder.gasLimit),
            gasPriceToUint32(depositOrder.gasPrice),
            uintToUint112(depositOrder.initialRatio),
            uintToUint112(depositOrder.share0),
            uintToUint112(depositOrder.share1),
            depositOrder.pairId,
            depositOrder.to,
            uint32(depositOrder.minRatioChangeToSwap),
            uintToFloat32(depositOrder.minSwapPrice),
            uintToFloat32(depositOrder.maxSwapPrice)
        );
    }

    function enqueueWithdrawOrder(Data storage data, WithdrawOrder memory withdrawOrder) internal {
        data.newestOrderId++;
        uint128 validAfterTimestamp = uint128(block.timestamp + data.delay);
        emit WithdrawEnqueued(data.newestOrderId, validAfterTimestamp, withdrawOrder.gasPrice);
        data.orderQueue[data.newestOrderId] = StoredOrder(
            WITHDRAW_TYPE,
            timestampToUint32(validAfterTimestamp),
            getUnwrapAndFailure(withdrawOrder.unwrap),
            timestampToUint32(withdrawOrder.deadline),
            uintToUint32(withdrawOrder.gasLimit),
            gasPriceToUint32(withdrawOrder.gasPrice),
            uintToUint112(withdrawOrder.liquidity),
            uintToUint112(withdrawOrder.amount0Min),
            uintToUint112(withdrawOrder.amount1Min),
            withdrawOrder.pairId,
            withdrawOrder.to,
            0, // maxRatioChange
            0, // minSwapPrice
            0 // maxSwapPrice
        );
    }

    function enqueueSellOrder(Data storage data, SellOrder memory sellOrder) internal {
        data.newestOrderId++;
        uint128 validAfterTimestamp = uint128(block.timestamp + data.delay);
        emit SellEnqueued(data.newestOrderId, validAfterTimestamp, sellOrder.gasPrice);
        data.orderQueue[data.newestOrderId] = StoredOrder(
            sellOrder.inverse ? SELL_INVERTED_TYPE : SELL_TYPE,
            timestampToUint32(validAfterTimestamp),
            getUnwrapAndFailure(sellOrder.unwrap),
            timestampToUint32(sellOrder.deadline),
            uintToUint32(sellOrder.gasLimit),
            gasPriceToUint32(sellOrder.gasPrice),
            0, // liquidityOrRatio
            uintToUint112(sellOrder.shareIn),
            uintToUint112(sellOrder.amountOutMin),
            sellOrder.pairId,
            sellOrder.to,
            0, // maxRatioChange
            0, // minSwapPrice
            0 // maxSwapPrice
        );
    }

    function enqueueBuyOrder(Data storage data, BuyOrder memory buyOrder) internal {
        data.newestOrderId++;
        uint128 validAfterTimestamp = uint128(block.timestamp + data.delay);
        emit BuyEnqueued(data.newestOrderId, validAfterTimestamp, buyOrder.gasPrice);
        data.orderQueue[data.newestOrderId] = StoredOrder(
            buyOrder.inverse ? BUY_INVERTED_TYPE : BUY_TYPE,
            timestampToUint32(validAfterTimestamp),
            getUnwrapAndFailure(buyOrder.unwrap),
            timestampToUint32(buyOrder.deadline),
            uintToUint32(buyOrder.gasLimit),
            gasPriceToUint32(buyOrder.gasPrice),
            0, // liquidityOrRatio
            uintToUint112(buyOrder.shareInMax),
            uintToUint112(buyOrder.amountOut),
            buyOrder.pairId,
            buyOrder.to,
            0, // maxRatioChange
            0, // minSwapPrice
            0 // maxSwapPrice
        );
    }

    function isRefundFailed(Data storage data, uint256 index) internal view returns (bool) {
        uint8 unwrapAndFailure = data.orderQueue[index].unwrapAndFailure;
        return unwrapAndFailure == UNWRAP_FAILED || unwrapAndFailure == KEEP_FAILED;
    }

    function markRefundFailed(Data storage data) internal {
        StoredOrder storage stored = data.orderQueue[data.lastProcessedOrderId];
        stored.unwrapAndFailure = stored.unwrapAndFailure == UNWRAP_NOT_FAILED ? UNWRAP_FAILED : KEEP_FAILED;
    }

    function getNextOrder(Data storage data) internal view returns (OrderType orderType, uint256 validAfterTimestamp) {
        return getOrder(data, data.lastProcessedOrderId + 1);
    }

    function dequeueCanceledOrder(Data storage data) external {
        data.lastProcessedOrderId++;
    }

    function dequeueDepositOrder(Data storage data) external returns (DepositOrder memory order) {
        data.lastProcessedOrderId++;
        order = getDepositOrder(data, data.lastProcessedOrderId);
    }

    function dequeueWithdrawOrder(Data storage data) external returns (WithdrawOrder memory order) {
        data.lastProcessedOrderId++;
        order = getWithdrawOrder(data, data.lastProcessedOrderId);
    }

    function dequeueSellOrder(Data storage data) external returns (SellOrder memory order) {
        data.lastProcessedOrderId++;
        order = getSellOrder(data, data.lastProcessedOrderId);
    }

    function dequeueBuyOrder(Data storage data) external returns (BuyOrder memory order) {
        data.lastProcessedOrderId++;
        order = getBuyOrder(data, data.lastProcessedOrderId);
    }

    function forgetOrder(Data storage data, uint256 orderId) internal {
        delete data.orderQueue[orderId];
    }

    function forgetLastProcessedOrder(Data storage data) internal {
        delete data.orderQueue[data.lastProcessedOrderId];
    }

    struct DepositParams {
        address token0;
        address token1;
        uint256 amount0;
        uint256 amount1;
        uint256 initialRatio;
        uint256 minRatioChangeToSwap;
        uint256 minSwapPrice;
        uint256 maxSwapPrice;
        bool wrap;
        address to;
        uint256 gasLimit;
        uint256 submitDeadline;
        uint256 executionDeadline;
    }

    function deposit(
        Data storage data,
        DepositParams calldata depositParams,
        TokenShares.Data storage tokenShares
    ) external {
        require(
            data.transferGasCosts[depositParams.token0] != 0 && data.transferGasCosts[depositParams.token1] != 0,
            'OS_TOKEN_TRANSFER_GAS_COST_UNSET'
        );
        checkOrderParams(
            data,
            depositParams.to,
            depositParams.gasLimit,
            depositParams.submitDeadline,
            depositParams.executionDeadline,
            ORDER_BASE_COST.add(data.transferGasCosts[depositParams.token0]).add(
                data.transferGasCosts[depositParams.token1]
            )
        );
        require(depositParams.amount0 != 0 || depositParams.amount1 != 0, 'OS_NO_AMOUNT');
        (address pair, uint32 pairId, bool inverted) = getPair(data, depositParams.token0, depositParams.token1);
        require(!data.depositDisabled[pair], 'OS_DEPOSIT_DISABLED');

        uint256 value = msg.value;

        // allocate gas refund
        if (depositParams.token0 == tokenShares.weth && depositParams.wrap) {
            value = value.sub(depositParams.amount0, 'OS_NOT_ENOUGH_FUNDS');
        } else if (depositParams.token1 == tokenShares.weth && depositParams.wrap) {
            value = value.sub(depositParams.amount1, 'OS_NOT_ENOUGH_FUNDS');
        }
        allocateGasRefund(data, value, depositParams.gasLimit);

        uint256 shares0 = tokenShares.amountToShares(depositParams.token0, depositParams.amount0, depositParams.wrap);
        uint256 shares1 = tokenShares.amountToShares(depositParams.token1, depositParams.amount1, depositParams.wrap);

        IIntegralPair(pair).syncWithOracle();
        enqueueDepositOrder(
            data,
            DepositOrder(
                pairId,
                inverted ? shares1 : shares0,
                inverted ? shares0 : shares1,
                depositParams.initialRatio,
                depositParams.minRatioChangeToSwap,
                depositParams.minSwapPrice,
                depositParams.maxSwapPrice,
                depositParams.wrap,
                depositParams.to,
                data.gasPrice,
                depositParams.gasLimit,
                depositParams.executionDeadline
            )
        );
    }

    struct WithdrawParams {
        address token0;
        address token1;
        uint256 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        bool unwrap;
        address to;
        uint256 gasLimit;
        uint256 submitDeadline;
        uint256 executionDeadline;
    }

    function withdraw(Data storage data, WithdrawParams calldata withdrawParams) external {
        (address pair, uint32 pairId, bool inverted) = getPair(data, withdrawParams.token0, withdrawParams.token1);
        require(!data.withdrawDisabled[pair], 'OS_WITHDRAW_DISABLED');
        checkOrderParams(
            data,
            withdrawParams.to,
            withdrawParams.gasLimit,
            withdrawParams.submitDeadline,
            withdrawParams.executionDeadline,
            ORDER_BASE_COST.add(PAIR_TRANSFER_COST)
        );
        require(withdrawParams.liquidity != 0, 'OS_NO_LIQUIDITY');

        allocateGasRefund(data, msg.value, withdrawParams.gasLimit);
        pair.safeTransferFrom(msg.sender, address(this), withdrawParams.liquidity);

        IIntegralPair(pair).syncWithOracle();
        enqueueWithdrawOrder(
            data,
            WithdrawOrder(
                pairId,
                withdrawParams.liquidity,
                inverted ? withdrawParams.amount1Min : withdrawParams.amount0Min,
                inverted ? withdrawParams.amount0Min : withdrawParams.amount1Min,
                withdrawParams.unwrap,
                withdrawParams.to,
                data.gasPrice,
                withdrawParams.gasLimit,
                withdrawParams.executionDeadline
            )
        );
    }

    struct SellParams {
        address tokenIn;
        address tokenOut;
        uint256 amountIn;
        uint256 amountOutMin;
        bool wrapUnwrap;
        address to;
        uint256 gasLimit;
        uint256 submitDeadline;
        uint256 executionDeadline;
    }

    function sell(
        Data storage data,
        SellParams calldata sellParams,
        TokenShares.Data storage tokenShares
    ) external {
        require(data.transferGasCosts[sellParams.tokenIn] != 0, 'OS_TOKEN_TRANSFER_GAS_COST_UNSET');
        checkOrderParams(
            data,
            sellParams.to,
            sellParams.gasLimit,
            sellParams.submitDeadline,
            sellParams.executionDeadline,
            ORDER_BASE_COST.add(data.transferGasCosts[sellParams.tokenIn])
        );
        require(sellParams.amountIn != 0, 'OS_NO_AMOUNT_IN');
        (address pair, uint32 pairId, bool inverted) = getPair(data, sellParams.tokenIn, sellParams.tokenOut);
        require(!data.sellDisabled[pair], 'OS_SELL_DISABLED');
        uint256 value = msg.value;

        // allocate gas refund
        if (sellParams.tokenIn == tokenShares.weth && sellParams.wrapUnwrap) {
            value = value.sub(sellParams.amountIn, 'OS_NOT_ENOUGH_FUNDS');
        }
        allocateGasRefund(data, value, sellParams.gasLimit);

        uint256 shares = tokenShares.amountToShares(sellParams.tokenIn, sellParams.amountIn, sellParams.wrapUnwrap);

        IIntegralPair(pair).syncWithOracle();
        enqueueSellOrder(
            data,
            SellOrder(
                pairId,
                inverted,
                shares,
                sellParams.amountOutMin,
                sellParams.wrapUnwrap,
                sellParams.to,
                data.gasPrice,
                sellParams.gasLimit,
                sellParams.executionDeadline
            )
        );
    }

    struct BuyParams {
        address tokenIn;
        address tokenOut;
        uint256 amountInMax;
        uint256 amountOut;
        bool wrapUnwrap;
        address to;
        uint256 gasLimit;
        uint256 submitDeadline;
        uint256 executionDeadline;
    }

    function buy(
        Data storage data,
        BuyParams calldata buyParams,
        TokenShares.Data storage tokenShares
    ) external {
        require(data.transferGasCosts[buyParams.tokenIn] != 0, 'OS_TOKEN_TRANSFER_GAS_COST_UNSET');
        checkOrderParams(
            data,
            buyParams.to,
            buyParams.gasLimit,
            buyParams.submitDeadline,
            buyParams.executionDeadline,
            ORDER_BASE_COST.add(data.transferGasCosts[buyParams.tokenIn])
        );
        require(buyParams.amountOut != 0, 'OS_NO_AMOUNT_OUT');
        (address pair, uint32 pairId, bool inverted) = getPair(data, buyParams.tokenIn, buyParams.tokenOut);
        require(!data.buyDisabled[pair], 'OS_BUY_DISABLED');

        uint256 value = msg.value;

        // allocate gas refund
        if (buyParams.tokenIn == tokenShares.weth && buyParams.wrapUnwrap) {
            value = value.sub(buyParams.amountInMax, 'OS_NOT_ENOUGH_FUNDS');
        }
        allocateGasRefund(data, value, buyParams.gasLimit);

        uint256 shares = tokenShares.amountToShares(buyParams.tokenIn, buyParams.amountInMax, buyParams.wrapUnwrap);

        IIntegralPair(pair).syncWithOracle();
        enqueueBuyOrder(
            data,
            BuyOrder(
                pairId,
                inverted,
                shares,
                buyParams.amountOut,
                buyParams.wrapUnwrap,
                buyParams.to,
                data.gasPrice,
                buyParams.gasLimit,
                buyParams.executionDeadline
            )
        );
    }

    function checkOrderParams(
        Data storage data,
        address to,
        uint256 gasLimit,
        uint256 submitDeadline,
        uint256 executionDeadline,
        uint256 minGasLimit
    ) private view {
        require(submitDeadline >= block.timestamp, 'OS_EXPIRED');
        require(executionDeadline > block.timestamp.add(data.delay), 'OS_INVALID_DEADLINE');
        require(gasLimit <= data.maxGasLimit, 'OS_GAS_LIMIT_TOO_HIGH');
        require(gasLimit >= minGasLimit, 'OS_GAS_LIMIT_TOO_LOW');
        require(to != address(0), 'OS_NO_ADDRESS');
    }

    function allocateGasRefund(
        Data storage data,
        uint256 value,
        uint256 gasLimit
    ) private returns (uint256 futureFee) {
        futureFee = data.gasPrice.mul(gasLimit);
        require(value >= futureFee, 'OS_NOT_ENOUGH_FUNDS');
        if (value > futureFee) {
            msg.sender.transfer(value.sub(futureFee));
        }
    }

    function updateGasPrice(Data storage data, uint256 gasUsed) external {
        uint256 scale = Math.min(gasUsed, data.maxGasPriceImpact);
        uint256 updated = data.gasPrice.mul(data.gasPriceInertia.sub(scale)).add(tx.gasprice.mul(scale)).div(
            data.gasPriceInertia
        );
        // we lower the precision for gas savings in order queue
        data.gasPrice = updated - (updated % 1e6);
    }

    function setMaxGasLimit(Data storage data, uint256 _maxGasLimit) external {
        require(_maxGasLimit <= 10000000, 'OS_MAX_GAS_LIMIT_TOO_HIGH');
        data.maxGasLimit = _maxGasLimit;
        emit MaxGasLimitSet(_maxGasLimit);
    }

    function setGasPriceInertia(Data storage data, uint256 _gasPriceInertia) external {
        require(_gasPriceInertia >= 1, 'OS_INVALID_INERTIA');
        data.gasPriceInertia = _gasPriceInertia;
        emit GasPriceInertiaSet(_gasPriceInertia);
    }

    function setMaxGasPriceImpact(Data storage data, uint256 _maxGasPriceImpact) external {
        require(_maxGasPriceImpact <= data.gasPriceInertia, 'OS_INVALID_MAX_GAS_PRICE_IMPACT');
        data.maxGasPriceImpact = _maxGasPriceImpact;
        emit MaxGasPriceImpactSet(_maxGasPriceImpact);
    }

    function setTransferGasCost(
        Data storage data,
        address token,
        uint256 gasCost
    ) external {
        data.transferGasCosts[token] = gasCost;
        emit TransferGasCostSet(token, gasCost);
    }
}

File 13 of 17 : IIntegralDelay.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;
pragma experimental ABIEncoderV2;

import 'Orders.sol';

interface IIntegralDelay {
    event OrderExecuted(uint256 indexed id, bool indexed success, bytes data, uint256 gasSpent, uint256 ethRefunded);
    event RefundFailed(address indexed to, address indexed token, uint256 amount, bytes data);
    event EthRefund(address indexed to, bool indexed success, uint256 value);
    event OwnerSet(address owner);
    event BotSet(address bot, bool isBot);
    event DelaySet(uint256 delay);
    event MaxGasLimitSet(uint256 maxGasLimit);
    event GasPriceInertiaSet(uint256 gasPriceInertia);
    event MaxGasPriceImpactSet(uint256 maxGasPriceImpact);
    event TransferGasCostSet(address token, uint256 gasCost);
    event OrderDisabled(address pair, Orders.OrderType orderType, bool disabled);
    event UnwrapFailed(address to, uint256 amount);
    event Execute(address sender, uint256 n);

    function factory() external returns (address);

    function owner() external returns (address);

    function isBot(address bot) external returns (bool);

    function botExecuteTime() external returns (uint256);

    function gasPriceInertia() external returns (uint256);

    function gasPrice() external returns (uint256);

    function maxGasPriceImpact() external returns (uint256);

    function maxGasLimit() external returns (uint256);

    function delay() external returns (uint256);

    function totalShares(address token) external returns (uint256);

    function weth() external returns (address);

    function getTransferGasCost(address token) external returns (uint256);

    function getDepositOrder(uint256 orderId) external returns (Orders.DepositOrder memory order);

    function getWithdrawOrder(uint256 orderId) external returns (Orders.WithdrawOrder memory order);

    function getSellOrder(uint256 orderId) external returns (Orders.SellOrder memory order);

    function getBuyOrder(uint256 orderId) external returns (Orders.BuyOrder memory order);

    function getDepositDisabled(address pair) external returns (bool);

    function getWithdrawDisabled(address pair) external returns (bool);

    function getBuyDisabled(address pair) external returns (bool);

    function getSellDisabled(address pair) external returns (bool);

    function getOrderStatus(uint256 orderId) external returns (Orders.OrderStatus);

    function setOrderDisabled(
        address pair,
        Orders.OrderType orderType,
        bool disabled
    ) external;

    function setOwner(address _owner) external;

    function setBot(address _bot, bool _isBot) external;

    function setMaxGasLimit(uint256 _maxGasLimit) external;

    function setDelay(uint256 _delay) external;

    function setGasPriceInertia(uint256 _gasPriceInertia) external;

    function setMaxGasPriceImpact(uint256 _maxGasPriceImpact) external;

    function setTransferGasCost(address token, uint256 gasCost) external;

    function deposit(Orders.DepositParams memory depositParams) external payable returns (uint256 orderId);

    function withdraw(Orders.WithdrawParams memory withdrawParams) external payable returns (uint256 orderId);

    function sell(Orders.SellParams memory sellParams) external payable returns (uint256 orderId);

    function buy(Orders.BuyParams memory buyParams) external payable returns (uint256 orderId);

    function execute(uint256 n) external;
}

File 14 of 17 : IIntegralOracle.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

interface IIntegralOracle {
    event OwnerSet(address owner);
    event UniswapPairSet(address uniswapPair);
    event PriceUpdateIntervalSet(uint32 interval);
    event ParametersSet(uint32 epoch, int256[] bidExponents, int256[] bidQs, int256[] askExponents, int256[] askQs);

    function owner() external view returns (address);

    function setOwner(address) external;

    function epoch() external view returns (uint32);

    function xDecimals() external view returns (uint8);

    function yDecimals() external view returns (uint8);

    function getParameters()
        external
        view
        returns (
            int256[] memory bidExponents,
            int256[] memory bidQs,
            int256[] memory askExponents,
            int256[] memory askQs
        );

    function setParameters(
        int256[] calldata bidExponents,
        int256[] calldata bidQs,
        int256[] calldata askExponents,
        int256[] calldata askQs
    ) external;

    function price() external view returns (int256);

    function priceUpdateInterval() external view returns (uint32);

    function updatePrice() external returns (uint32 _epoch);

    function setPriceUpdateInterval(uint32 interval) external;

    function price0CumulativeLast() external view returns (uint256);

    function blockTimestampLast() external view returns (uint32);

    function tradeX(
        uint256 xAfter,
        uint256 xBefore,
        uint256 yBefore
    ) external view returns (uint256 yAfter);

    function tradeY(
        uint256 yAfter,
        uint256 xBefore,
        uint256 yBefore
    ) external view returns (uint256 xAfter);

    function getSpotPrice(uint256 xCurrent, uint256 xBefore) external view returns (uint256 spotPrice);
}

File 15 of 17 : Normalizer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

import 'SafeMath.sol';

library Normalizer {
    using SafeMath for uint256;

    function normalize(uint256 amount, uint8 decimals) internal pure returns (uint256) {
        if (decimals == 18) {
            return amount;
        } else if (decimals > 18) {
            return amount.div(10**(decimals - 18));
        } else {
            return amount.mul(10**(18 - decimals));
        }
    }

    function denormalize(uint256 amount, uint8 decimals) internal pure returns (uint256) {
        if (decimals == 18) {
            return amount;
        } else if (decimals > 18) {
            return amount.mul(10**(decimals - 18));
        } else {
            return amount.div(10**(18 - decimals));
        }
    }
}

File 16 of 17 : AddLiquidity.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;

import 'TransferHelper.sol';
import 'SafeMath.sol';
import 'Math.sol';
import 'IIntegralPair.sol';

library AddLiquidity {
    using SafeMath for uint256;

    function _quote(
        uint256 amount0,
        uint256 reserve0,
        uint256 reserve1
    ) private pure returns (uint256 amountB) {
        require(amount0 > 0, 'AL_INSUFFICIENT_AMOUNT');
        require(reserve0 > 0 && reserve1 > 0, 'AL_INSUFFICIENT_LIQUIDITY');
        amountB = amount0.mul(reserve1) / reserve0;
    }

    function addLiquidity(
        address pair,
        uint256 amount0Desired,
        uint256 amount1Desired
    ) external view returns (uint256 amount0, uint256 amount1) {
        if (amount0Desired == 0 || amount1Desired == 0) {
            return (0, 0);
        }
        (uint256 reserve0, uint256 reserve1, ) = IIntegralPair(pair).getReserves();
        if (reserve0 == 0 && reserve1 == 0) {
            (amount0, amount1) = (amount0Desired, amount1Desired);
        } else {
            uint256 amount1Optimal = _quote(amount0Desired, reserve0, reserve1);
            if (amount1Optimal <= amount1Desired) {
                (amount0, amount1) = (amount0Desired, amount1Optimal);
            } else {
                uint256 amount0Optimal = _quote(amount1Desired, reserve1, reserve0);
                assert(amount0Optimal <= amount0Desired);
                (amount0, amount1) = (amount0Optimal, amount1Desired);
            }
        }
    }

    function swapDeposit0(
        address pair,
        address token0,
        uint256 amount0,
        uint256 minSwapPrice
    ) external returns (uint256 amount0Left, uint256 amount1Left) {
        uint256 amount0In = IIntegralPair(pair).getDepositAmount0In(amount0);
        amount1Left = IIntegralPair(pair).getSwapAmount1Out(amount0In);
        if (amount1Left == 0) {
            return (amount0, amount1Left);
        }
        uint256 price = amount1Left.mul(1e18).div(amount0In);
        require(minSwapPrice == 0 || price >= minSwapPrice, 'AL_PRICE_TOO_LOW');
        TransferHelper.safeTransfer(token0, pair, amount0In);
        IIntegralPair(pair).swap(0, amount1Left, address(this));
        amount0Left = amount0.sub(amount0In);
    }

    function swapDeposit1(
        address pair,
        address token1,
        uint256 amount1,
        uint256 maxSwapPrice
    ) external returns (uint256 amount0Left, uint256 amount1Left) {
        uint256 amount1In = IIntegralPair(pair).getDepositAmount1In(amount1);
        amount0Left = IIntegralPair(pair).getSwapAmount0Out(amount1In);
        if (amount0Left == 0) {
            return (amount0Left, amount1);
        }
        uint256 price = amount1In.mul(1e18).div(amount0Left);
        require(maxSwapPrice == 0 || price <= maxSwapPrice, 'AL_PRICE_TOO_HIGH');
        TransferHelper.safeTransfer(token1, pair, amount1In);
        IIntegralPair(pair).swap(amount0Left, 0, address(this));
        amount1Left = amount1.sub(amount1In);
    }

    function canSwap(
        uint256 initialRatio, // setting it to 0 disables swap
        uint256 minRatioChangeToSwap,
        address pairAddress
    ) external view returns (bool) {
        (uint256 reserve0, uint256 reserve1, ) = IIntegralPair(pairAddress).getReserves();
        if (reserve0 == 0 || reserve1 == 0 || initialRatio == 0) {
            return false;
        }
        uint256 ratio = reserve0.mul(1e18).div(reserve1);
        // ratioChange(before, after) = MAX(before, after) / MIN(before, after) - 1
        uint256 change = Math.max(initialRatio, ratio).mul(1e3).div(Math.min(initialRatio, ratio)).sub(1e3);
        return change >= minRatioChangeToSwap;
    }
}

File 17 of 17 : BuyHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// Deployed with donations via Gitcoin GR9

pragma solidity 0.7.5;
// pragma abicoder v2;

import 'IIntegralOracle.sol';
import 'IIntegralPair.sol';
import 'SafeMath.sol';

library BuyHelper {
    using SafeMath for uint256;
    uint256 public constant PRECISION = 10**18;

    function getSwapAmount0In(address pair, uint256 amount1Out) external view returns (uint256 swapAmount0In) {
        (uint112 reserve0, uint112 reserve1, ) = IIntegralPair(pair).getReserves();
        (uint112 reference0, uint112 reference1, ) = IIntegralPair(pair).getReferences();
        uint256 balance1After = uint256(reserve1).sub(amount1Out);
        uint256 balance0After = IIntegralOracle(IIntegralPair(pair).oracle()).tradeY(
            balance1After,
            reference0,
            reference1
        );
        uint256 swapFee = IIntegralPair(pair).swapFee();
        return balance0After.sub(uint256(reserve0)).mul(PRECISION).ceil_div(PRECISION.sub(swapFee)).add(1);
    }

    function getSwapAmount1In(address pair, uint256 amount0Out) external view returns (uint256 swapAmount1In) {
        (uint112 reserve0, uint112 reserve1, ) = IIntegralPair(pair).getReserves();
        (uint112 reference0, uint112 reference1, ) = IIntegralPair(pair).getReferences();
        uint256 balance0After = uint256(reserve0).sub(amount0Out);
        uint256 balance1After = IIntegralOracle(IIntegralPair(pair).oracle()).tradeX(
            balance0After,
            reference0,
            reference1
        );
        uint256 swapFee = IIntegralPair(pair).swapFee();
        return balance1After.sub(uint256(reserve1)).mul(PRECISION).ceil_div(PRECISION.sub(swapFee)).add(1);
    }
}

Settings
{
  "libraries": {
    "IERC20.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IIntegralERC20.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IReserves.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IIntegralPair.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "SafeMath.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "Math.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IIntegralFactory.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IWETH.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "TransferHelper.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "TokenShares.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "Orders.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IIntegralDelay.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IIntegralOracle.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "Normalizer.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "AddLiquidity.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "BuyHelper.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    },
    "IntegralDelay.sol": {
      "TokenShares": "0xc82938b53e0e190459ba4e3502bf26f194760183",
      "Orders": "0x7218D567b671E36c4e6B3257b8919196CF68ff5E",
      "AddLiquidity": "0x3A09FA6c7Cb82FC94c377087cF59eC9529094e61",
      "BuyHelper": "0x5d0434d41C77E4D9a858000f3939c0c4A05B0e26"
    }
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_bot","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"bot","type":"address"},{"indexed":false,"internalType":"bool","name":"isBot","type":"bool"}],"name":"BotSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"delay","type":"uint256"}],"name":"DelaySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"EthRefund","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"n","type":"uint256"}],"name":"Execute","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gasPriceInertia","type":"uint256"}],"name":"GasPriceInertiaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxGasLimit","type":"uint256"}],"name":"MaxGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxGasPriceImpact","type":"uint256"}],"name":"MaxGasPriceImpactSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"enum Orders.OrderType","name":"orderType","type":"uint8"},{"indexed":false,"internalType":"bool","name":"disabled","type":"bool"}],"name":"OrderDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"gasSpent","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethRefunded","type":"uint256"}],"name":"OrderExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"OwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"RefundFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"gasCost","type":"uint256"}],"name":"TransferGasCostSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnwrapFailed","type":"event"},{"inputs":[],"name":"ORDER_CANCEL_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"bool","name":"inverse","type":"bool"},{"internalType":"uint256","name":"shareInMax","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.BuyOrder","name":"buyOrder","type":"tuple"}],"name":"_executeBuy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"uint256","name":"share0","type":"uint256"},{"internalType":"uint256","name":"share1","type":"uint256"},{"internalType":"uint256","name":"initialRatio","type":"uint256"},{"internalType":"uint256","name":"minRatioChangeToSwap","type":"uint256"},{"internalType":"uint256","name":"minSwapPrice","type":"uint256"},{"internalType":"uint256","name":"maxSwapPrice","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.DepositOrder","name":"depositOrder","type":"tuple"}],"name":"_executeDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"bool","name":"inverse","type":"bool"},{"internalType":"uint256","name":"shareIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.SellOrder","name":"sellOrder","type":"tuple"}],"name":"_executeSell","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.WithdrawOrder","name":"withdrawOrder","type":"tuple"}],"name":"_executeWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"_refundLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"share","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"}],"name":"_refundToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"uint256","name":"share0","type":"uint256"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"share1","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"}],"name":"_refundTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"botExecuteTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bool","name":"wrapUnwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"submitDeadline","type":"uint256"},{"internalType":"uint256","name":"executionDeadline","type":"uint256"}],"internalType":"struct Orders.BuyParams","name":"buyParams","type":"tuple"}],"name":"buy","outputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"cancelOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"initialRatio","type":"uint256"},{"internalType":"uint256","name":"minRatioChangeToSwap","type":"uint256"},{"internalType":"uint256","name":"minSwapPrice","type":"uint256"},{"internalType":"uint256","name":"maxSwapPrice","type":"uint256"},{"internalType":"bool","name":"wrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"submitDeadline","type":"uint256"},{"internalType":"uint256","name":"executionDeadline","type":"uint256"}],"internalType":"struct Orders.DepositParams","name":"depositParams","type":"tuple"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"n","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasPriceInertia","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"getBuyDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"getBuyOrder","outputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"bool","name":"inverse","type":"bool"},{"internalType":"uint256","name":"shareInMax","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.BuyOrder","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"getDepositDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"getDepositOrder","outputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"uint256","name":"share0","type":"uint256"},{"internalType":"uint256","name":"share1","type":"uint256"},{"internalType":"uint256","name":"initialRatio","type":"uint256"},{"internalType":"uint256","name":"minRatioChangeToSwap","type":"uint256"},{"internalType":"uint256","name":"minSwapPrice","type":"uint256"},{"internalType":"uint256","name":"maxSwapPrice","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.DepositOrder","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"getOrder","outputs":[{"internalType":"enum Orders.OrderType","name":"orderType","type":"uint8"},{"internalType":"uint256","name":"validAfterTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"getOrderStatus","outputs":[{"internalType":"enum Orders.OrderStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"getSellDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"getSellOrder","outputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"bool","name":"inverse","type":"bool"},{"internalType":"uint256","name":"shareIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.SellOrder","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getTransferGasCost","outputs":[{"internalType":"uint256","name":"gasCost","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"getWithdrawDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"getWithdrawOrder","outputs":[{"components":[{"internalType":"uint32","name":"pairId","type":"uint32"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct Orders.WithdrawOrder","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"isOrderCanceled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastProcessedOrderId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxGasPriceImpact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newestOrderId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"name":"retryRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bool","name":"wrapUnwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"submitDeadline","type":"uint256"},{"internalType":"uint256","name":"executionDeadline","type":"uint256"}],"internalType":"struct Orders.SellParams","name":"sellParams","type":"tuple"}],"name":"sell","outputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_bot","type":"address"},{"internalType":"bool","name":"_isBot","type":"bool"}],"name":"setBot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasPriceInertia","type":"uint256"}],"name":"setGasPriceInertia","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxGasLimit","type":"uint256"}],"name":"setMaxGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxGasPriceImpact","type":"uint256"}],"name":"setMaxGasPriceImpact","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"},{"internalType":"enum Orders.OrderType","name":"orderType","type":"uint8"},{"internalType":"bool","name":"disabled","type":"bool"}],"name":"setOrderDisabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"gasCost","type":"uint256"}],"name":"setTransferGasCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"submitDeadline","type":"uint256"},{"internalType":"uint256","name":"executionDeadline","type":"uint256"}],"internalType":"struct Orders.WithdrawParams","name":"withdrawParams","type":"tuple"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"orderId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405260016015553480156200001657600080fd5b5060405162005a9d38038062005a9d83398101604081905262000039916200010f565b600480546001600160a01b038086166001600160a01b031992831617909255601280549091163317905581166000908152601360205260409020805460ff19166001179055620f42403a063a03600655620000a2601083620000cd602090811b6200298f17901c565b505061012c600055506104b0601455624c4b406005556301312d00600755620f424060085562000158565b60019190910180546001600160a01b0319166001600160a01b03909216919091179055565b80516001600160a01b03811681146200010a57600080fd5b919050565b60008060006060848603121562000124578283fd5b6200012f84620000f2565b92506200013f60208501620000f2565b91506200014f60408501620000f2565b90509250925092565b61593580620001686000396000f3fe6080604052600436106102975760003560e01c8063945317131161015a578063d09ef241116100c1578063e5e7988e1161007a578063e5e7988e146107c0578063e6a0cc94146107e0578063f0aa61a3146107f5578063f968bc2a1461080a578063fe0d94c11461082a578063fe173b971461084a5761029e565b8063d09ef2411461070a578063d22e924214610738578063e085272f14610758578063e177246e1461076b578063e30a49931461078b578063e5b1be65146107a05761029e565b8063b800522611610113578063b800522614610660578063be58130414610675578063bf6b874e14610695578063c35930c3146106b5578063c45a0155146106d5578063c9cd9760146106ea5761029e565b806394531713146105a05780639718f627146105c0578063af482b58146105e0578063b1af1bda14610600578063b32ac93614610620578063b3756220146106405761029e565b806345fa8aae116101fe5780636a42b8f8116101b75780636a42b8f8146105015780636de3c67c1461051657806377632ec21461052b5780637f6a1caf1461054b578063814494701461056b5780638da5cb5b1461058b5761029e565b806345fa8aae1461043d5780634c0160161461046a578063514fcac71461048a57806354df6d74146104aa57806357a62a4f146104d75780635e45da23146104ec5761029e565b8063342aa8b511610250578063342aa8b51461037b578063390ce0d31461039b5780633bbac579146103c85780633ed76f17146103e85780633fc8cef31461040857806343133c4b1461042a5761029e565b806307ddd3bc146102a357806313af4035146102cc5780631776834a146102ee57806317818a5c1461030e57806320a68fab1461033b5780632da7cbfd146103685761029e565b3661029e57005b600080fd5b6102b66102b1366004614fc8565b61085f565b6040516102c391906154fe565b60405180910390f35b3480156102d857600080fd5b506102ec6102e7366004614a65565b61090a565b005b3480156102fa57600080fd5b506102ec610309366004614fda565b610990565b34801561031a57600080fd5b5061032e610329366004614fda565b610a27565b6040516102c3919061579c565b34801561034757600080fd5b5061035b610356366004614a65565b610ac2565b6040516102c391906152cf565b6102b6610376366004614e90565b610ae0565b34801561038757600080fd5b506102ec610396366004614bce565b610b46565b3480156103a757600080fd5b506103bb6103b6366004614fda565b610bd2565b6040516102c391906154ef565b3480156103d457600080fd5b5061035b6103e3366004614a65565b610c65565b3480156103f457600080fd5b506102ec610403366004614b0d565b610c7a565b34801561041457600080fd5b5061041d610c9a565b6040516102c391906151ad565b6102b6610438366004614d10565b610ca9565b34801561044957600080fd5b5061045d610458366004614fda565b610d0f565b6040516102c391906152ff565b34801561047657600080fd5b5061035b610485366004614a65565b610d9b565b34801561049657600080fd5b506102ec6104a5366004614fda565b610db9565b3480156104b657600080fd5b506104ca6104c5366004614fda565b610edd565b6040516102c391906156fc565b3480156104e357600080fd5b506102b6610f70565b3480156104f857600080fd5b506102b6610f76565b34801561050d57600080fd5b506102b6610f7c565b34801561052257600080fd5b506102b6610f82565b34801561053757600080fd5b5061035b610546366004614fda565b610f88565b34801561055757600080fd5b506102ec610566366004614c06565b610f9d565b34801561057757600080fd5b506102ec610586366004614d2c565b611123565b34801561059757600080fd5b5061041d6113ca565b3480156105ac57600080fd5b506102ec6105bb366004614fda565b6113d9565b3480156105cc57600080fd5b506102b66105db366004614a65565b61143d565b3480156105ec57600080fd5b5061035b6105fb366004614a65565b611458565b34801561060c57600080fd5b506103bb61061b366004614fda565b611476565b34801561062c57600080fd5b506102ec61063b366004614fda565b6114b8565b34801561064c57600080fd5b506102ec61065b366004614ea2565b61151c565b34801561066c57600080fd5b506102b6611715565b34801561068157600080fd5b506102ec610690366004614fda565b61171c565b3480156106a157600080fd5b506102b66106b0366004614a65565b6117ea565b3480156106c157600080fd5b506102ec6106d0366004614cd8565b611805565b3480156106e157600080fd5b5061041d611dab565b3480156106f657600080fd5b506102ec610705366004614acd565b611dba565b34801561071657600080fd5b5061072a610725366004614fda565b611de9565b6040516102c3929190615313565b34801561074457600080fd5b506102ec610753366004614c45565b611e7f565b6102b6610766366004614d10565b611f11565b34801561077757600080fd5b506102ec610786366004614fda565b611f76565b34801561079757600080fd5b506102b6611fdc565b3480156107ac57600080fd5b506102ec6107bb366004614b7c565b611fe2565b3480156107cc57600080fd5b5061035b6107db366004614a65565b6121ed565b3480156107ec57600080fd5b506102b661220b565b34801561080157600080fd5b506102b6612211565b34801561081657600080fd5b506102ec610825366004614cd8565b612217565b34801561083657600080fd5b506102ec610845366004614fda565b6126e3565b34801561085657600080fd5b506102b6612989565b600060155460011461088c5760405162461bcd60e51b81526004016108839061532e565b60405180910390fd5b60006015819055604051636587992160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e916365879921916108cb9190869060040161562c565b60006040518083038186803b1580156108e357600080fd5b505af41580156108f7573d6000803e3d6000fd5b5050600154925050506001601555919050565b6012546001600160a01b031633146109345760405162461bcd60e51b815260040161088390615434565b601280546001600160a01b0319166001600160a01b0383811691909117918290556040517f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe2926109859216906151ad565b60405180910390a150565b6012546001600160a01b031633146109ba5760405162461bcd60e51b815260040161088390615434565b604051636702eca360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e90636702eca3906109f49060009085906004016156da565b60006040518083038186803b158015610a0c57600080fd5b505af4158015610a20573d6000803e3d6000fd5b5050505050565b610a2f6147af565b604051634ce4436960e11b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906399c886d290610a699060009086906004016156da565b6101206040518083038186803b158015610a8257600080fd5b505af4158015610a96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614f35565b90505b919050565b6001600160a01b03166000908152600d602052604090205460ff1690565b6000601554600114610b045760405162461bcd60e51b81526004016108839061532e565b60006015819055604051638f0e6bef60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e91638f0e6bef916108cb9190869060109060040161554a565b6012546001600160a01b03163314610b705760405162461bcd60e51b815260040161088390615434565b6001600160a01b03821660009081526013602052604090819020805460ff1916831515179055517f70af441dbb427737e6a5ef2cf5b664321011765ce1d19ce4a69cd024e69d4f2f90610bc69084908490615266565b60405180910390a15050565b610bda61480c565b60405163ac1ecdb360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063ac1ecdb390610c149060009086906004016156da565b6101206040518083038186803b158015610c2d57600080fd5b505af4158015610c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614cf4565b60136020526000908152604090205460ff1681565b610c8685878684611fe2565b610c9283878484611fe2565b505050505050565b6011546001600160a01b031690565b6000601554600114610ccd5760405162461bcd60e51b81526004016108839061532e565b6000601581905560405163758e99b360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9163758e99b3916108cb91908690601090600401615526565b604051634dc1ffe760e11b8152600090737218d567b671e36c4e6b3257b8919196cf68ff5e90639b83ffce90610d4b90849086906004016156da565b60206040518083038186803b158015610d6357600080fd5b505af4158015610d77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614c8c565b6001600160a01b03166000908152600f602052604090205460ff1690565b601554600114610ddb5760405162461bcd60e51b81526004016108839061532e565b6000601581905560405163317e626f60e21b81528190737218d567b671e36c4e6b3257b8919196cf68ff5e9063c5f989bc90610e1d90849087906004016156da565b604080518083038186803b158015610e3457600080fd5b505af4158015610e48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6c9190614cab565b9150915042610e9062015180610e8a610e83610f7c565b85906129b4565b906129f0565b10610ead5760405162461bcd60e51b815260040161088390615388565b610eba8282856001612a40565b50506000908152600b60205260409020805460ff19166001908117909155601555565b610ee561486b565b60405163117d7ab160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063117d7ab190610f1f9060009086906004016156da565b6101806040518083038186803b158015610f3857600080fd5b505af4158015610f4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614dde565b60085490565b60055490565b60005490565b60075490565b6000908152600b602052604090205460ff1690565b6012546001600160a01b03163314610fc75760405162461bcd60e51b815260040161088390615434565b6000826004811115610fd557fe5b1415610ff35760405162461bcd60e51b8152600401610883906153b7565b600182600481111561100157fe5b1415611030576001600160a01b0383166000908152600c60205260409020805460ff19168215151790556110e3565b600282600481111561103e57fe5b141561106d576001600160a01b0383166000908152600d60205260409020805460ff19168215151790556110e3565b600382600481111561107b57fe5b14156110aa576001600160a01b0383166000908152600f60205260409020805460ff19168215151790556110e3565b60048260048111156110b857fe5b14156110e3576001600160a01b0383166000908152600e60205260409020805460ff19168215151790555b7f3d5726ff7c14d1fb7a71ac46100040a8b7a9edb7624e96b8abde4a15649fdf4983838360405161111693929190615281565b60405180910390a1505050565b3330146111425760405162461bcd60e51b815260040161088390615434565b4281610160015110156111675760405162461bcd60e51b8152600401610883906153e6565b6000806000806000611178866131c3565b9450945094509450945081600014158061119157508015155b801561124d575060608601516080870151875163ffffffff1660009081526009602052604090819020549051635c1941c160e11b8152733a09fa6c7cb82fc94c377087cf59ec9529094e619363b8328382936111fd93919290916001600160a01b03169060040161582b565b60206040518083038186803b15801561121557600080fd5b505af4158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d9190614c70565b1561138c5781156112f15760a0860151604051634f57f14d60e11b8152733a09fa6c7cb82fc94c377087cf59ec9529094e6191639eafe29a916112989189918991889160040161523d565b604080518083038186803b1580156112af57600080fd5b505af41580156112c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e7919061500a565b909250905061138c565b801561138c5760c086015160405163aceb17df60e01b8152733a09fa6c7cb82fc94c377087cf59ec9529094e619163aceb17df916113379189918891879160040161523d565b604080518083038186803b15801561134e57600080fd5b505af4158015611362573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611386919061500a565b90925090505b811580159061139a57508015155b156113b8576113b28587610100015186868686613401565b90925090505b610c928661010001518585858561356f565b6012546001600160a01b031681565b6012546001600160a01b031633146114035760405162461bcd60e51b815260040161088390615434565b604051630fd1437d60e11b8152737218d567b671e36c4e6b3257b8919196cf68ff5e90631fa286fa906109f49060009085906004016156da565b6001600160a01b03166000908152600a602052604090205490565b6001600160a01b03166000908152600c602052604090205460ff1690565b61147e61480c565b6040516311c8197f60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906311c8197f90610c149060009086906004016156da565b6012546001600160a01b031633146114e25760405162461bcd60e51b815260040161088390615434565b60405163153cc2fd60e31b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063a9e617e8906109f49060009085906004016156da565b33301461153b5760405162461bcd60e51b815260040161088390615434565b4281610100015110156115605760405162461bcd60e51b8152600401610883906153e6565b8051604051630270f5c360e51b8152600091737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb8609161159d918591906004016156e8565b60606040518083038186803b1580156115b557600080fd5b505af41580156115c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ed9190614a81565b50509050806001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561162c57600080fd5b505af1158015611640573d6000803e3d6000fd5b505050506116538182846020015161358d565b600080826001600160a01b03166389afcb448560a001516040518263ffffffff1660e01b815260040161168691906151ad565b6040805180830381600087803b15801561169f57600080fd5b505af11580156116b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d7919061500a565b91509150836040015182101580156116f3575083606001518110155b61170f5760405162461bcd60e51b81526004016108839061545a565b50505050565b6201518081565b60155460011461173e5760405162461bcd60e51b81526004016108839061532e565b60006015819055604051631ae4f06f60e01b81528190737218d567b671e36c4e6b3257b8919196cf68ff5e90631ae4f06f9061178090849087906004016156da565b604080518083038186803b15801561179757600080fd5b505af41580156117ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117cf9190614cab565b915091506117e08282856000612a40565b5050600160155550565b6001600160a01b031660009081526010602052604090205490565b3330146118245760405162461bcd60e51b815260040161088390615434565b4281610100015110156118495760405162461bcd60e51b8152600401610883906153e6565b8051604051630270f5c360e51b815260009182918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb8609161188a918591906004016156e8565b60606040518083038186803b1580156118a257600080fd5b505af41580156118b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118da9190614a81565b92509250925060008085602001516118f35783836118f6565b82845b915091506000601073c82938b53e0e190459ba4e3502bf26f194760183636c5d9cf09091858a604001516040518463ffffffff1660e01b815260040161193e93929190615507565b60206040518083038186803b15801561195657600080fd5b505af415801561196a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061198e9190614ff2565b90506000869050806001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156119d057600080fd5b505af11580156119e4573d6000803e3d6000fd5b5050505060008860200151611a8457606089015160405163049c271760e41b8152735d0434d41c77e4d9a858000f3939c0c4a05b0e26916349c2717091611a2f918c916004016151c1565b60206040518083038186803b158015611a4757600080fd5b505af4158015611a5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7f9190614ff2565b611b10565b6060890151604051635ee8722f60e01b8152735d0434d41c77e4d9a858000f3939c0c4a05b0e2691635ee8722f91611ac0918c916004016151c1565b60206040518083038186803b158015611ad857600080fd5b505af4158015611aec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b109190614ff2565b905080831015611b325760405162461bcd60e51b8152600401610883906154b8565b6000808a60200151611b4a5760008b60600151611b52565b8a6060015160005b91509150611b61878b8561358d565b6011546001600160a01b038781169116148015611b7f57508a608001515b15611d37576040516336cd320560e11b81526001600160a01b03851690636d9a640a90611bb49085908590309060040161582b565b600060405180830381600087803b158015611bce57600080fd5b505af1158015611be2573d6000803e3d6000fd5b505060115460608e0151604051632e1a7d4d60e01b81526001600160a01b039092169350632e1a7d4d9250611c19916004016154fe565b600060405180830381600087803b158015611c3357600080fd5b505af1158015611c47573d6000803e3d6000fd5b5050505060008b60a001516001600160a01b03168c6060015161271090604051611c70906151aa565b600060405180830381858888f193505050503d8060008114611cae576040519150601f19603f3d011682016040523d82523d6000602084013e611cb3565b606091505b5050905080611d315760a08c015160608d015160405163032b310f60e51b815273c82938b53e0e190459ba4e3502bf26f1947601839263656621e092611d00926010929190600401615507565b60006040518083038186803b158015611d1857600080fd5b505af4158015611d2c573d6000803e3d6000fd5b505050505b50611d9e565b60a08b01516040516336cd320560e11b81526001600160a01b03861691636d9a640a91611d6b91869186919060040161582b565b600060405180830381600087803b158015611d8557600080fd5b505af1158015611d99573d6000803e3d6000fd5b505050505b5050505050505050505050565b6004546001600160a01b031690565b333014611dd95760405162461bcd60e51b815260040161088390615434565b611de483838361358d565b505050565b60405163317e626f60e21b81526000908190737218d567b671e36c4e6b3257b8919196cf68ff5e9063c5f989bc90611e2790849087906004016156da565b604080518083038186803b158015611e3e57600080fd5b505af4158015611e52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e769190614cab565b91509150915091565b6012546001600160a01b03163314611ea95760405162461bcd60e51b815260040161088390615434565b60405163b2456a0760e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063b2456a0790611ee59060009086908690600401615507565b60006040518083038186803b158015611efd57600080fd5b505af4158015610c92573d6000803e3d6000fd5b6000601554600114611f355760405162461bcd60e51b81526004016108839061532e565b6000601581905560405162c44ff160e31b8152737218d567b671e36c4e6b3257b8919196cf68ff5e916306227f88916108cb91908690601090600401615526565b6012546001600160a01b03163314611fa05760405162461bcd60e51b815260040161088390615434565b6000819055600481026014556040517f63e09f16584208fba1fc7ff64c62b00f07bec177c0d97ca6689891b1e77a35c7906109859083906154fe565b60015490565b3330146120015760405162461bcd60e51b815260040161088390615434565b6011546001600160a01b03858116911614801561201b5750805b1561214f576040516306c5d9cf60e41b815260009073c82938b53e0e190459ba4e3502bf26f19476018390636c5d9cf09061205f9060109089908890600401615507565b60206040518083038186803b15801561207757600080fd5b505af415801561208b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120af9190614ff2565b601154604051632e1a7d4d60e01b81529192506001600160a01b031690632e1a7d4d906120e09084906004016154fe565b600060405180830381600087803b1580156120fa57600080fd5b505af115801561210e573d6000803e3d6000fd5b50506040516001600160a01b038716925083156108fc02915083906000818181858888f19350505050158015612148573d6000803e3d6000fd5b505061170f565b6040516306c5d9cf60e41b81526121e8908590859073c82938b53e0e190459ba4e3502bf26f19476018390636c5d9cf0906121939060109086908a90600401615507565b60206040518083038186803b1580156121ab57600080fd5b505af41580156121bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e39190614ff2565b61358d565b61170f565b6001600160a01b03166000908152600e602052604090205460ff1690565b60025490565b60145481565b3330146122365760405162461bcd60e51b815260040161088390615434565b42816101000151101561225b5760405162461bcd60e51b8152600401610883906153e6565b8051604051630270f5c360e51b815260009182918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb8609161229c918591906004016156e8565b60606040518083038186803b1580156122b457600080fd5b505af41580156122c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ec9190614a81565b9250925092506000808560200151612305578383612308565b82845b915091506000601073c82938b53e0e190459ba4e3502bf26f194760183636c5d9cf09091858a604001516040518463ffffffff1660e01b815260040161235093929190615507565b60206040518083038186803b15801561236857600080fd5b505af415801561237c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a09190614ff2565b90506000869050806001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156123e257600080fd5b505af11580156123f6573d6000803e3d6000fd5b5050505061240584888461358d565b60008860200151612491576040516357db007f60e11b81526001600160a01b0383169063afb600fe9061243c9086906004016154fe565b60206040518083038186803b15801561245457600080fd5b505afa158015612468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248c9190614ff2565b61250d565b604051632e34fcc760e21b81526001600160a01b0383169063b8d3f31c906124bd9086906004016154fe565b60206040518083038186803b1580156124d557600080fd5b505afa1580156124e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061250d9190614ff2565b905088606001518110156125335760405162461bcd60e51b815260040161088390615351565b6000808a602001516125475760008361254b565b8260005b60115491935091506001600160a01b03878116911614801561256e57508a608001515b15611d37576040516336cd320560e11b81526001600160a01b03851690636d9a640a906125a39085908590309060040161582b565b600060405180830381600087803b1580156125bd57600080fd5b505af11580156125d1573d6000803e3d6000fd5b5050601154604051632e1a7d4d60e01b81526001600160a01b039091169250632e1a7d4d91506126059086906004016154fe565b600060405180830381600087803b15801561261f57600080fd5b505af1158015612633573d6000803e3d6000fd5b5050505060008b60a001516001600160a01b03168461271090604051612658906151aa565b600060405180830381858888f193505050503d8060008114612696576040519150601f19603f3d011682016040523d82523d6000602084013e61269b565b606091505b5050905080611d315760a08c015160405163032b310f60e51b815273c82938b53e0e190459ba4e3502bf26f1947601839163656621e091611d00916010918990600401615507565b6015546001146127055760405162461bcd60e51b81526004016108839061532e565b60006015556040517f892cd8f5b436bd5fb7dac1f11aafb73345d892ba3e9fe09cd94d95ba84928e739061273c90339084906151c1565b60405180910390a160005a90506000805b838110156129045760025461276490600101610f88565b156127d6576040516305d4bd1160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906305d4bd11906127a1906000906004016154fe565b60006040518083038186803b1580156127b957600080fd5b505af41580156127cd573d6000803e3d6000fd5b505050506128fc565b6000806127e360006136e5565b909250905060008260048111156127f657fe5b14806128025750428110155b1561280e575050612904565b60145481014210158061283057503360009081526013602052604090205460ff165b8061286557506000805260136020527f8fa6efc3be94b5b348b21fea823fe8d100408cee9b7f90524494500445d8ff6c5460ff165b6128815760405162461bcd60e51b815260040161088390615434565b600193508382600481111561289257fe5b14156128a5576128a06136f9565b6128f9565b60028260048111156128b357fe5b14156128c1576128a06139ce565b60038260048111156128cf57fe5b14156128dd576128a0613c64565b60048260048111156128eb57fe5b14156128f9576128f9613ef8565b50505b60010161274d565b5080156117e057737218d567b671e36c4e6b3257b8919196cf68ff5e639db74df160006129325a86906129b4565b6040518363ffffffff1660e01b815260040161294f9291906156da565b60006040518083038186803b15801561296757600080fd5b505af415801561297b573d6000803e3d6000fd5b505050505050600160155550565b60065490565b60019190910180546001600160a01b0319166001600160a01b03909216919091179055565b60006129e983836040518060400160405280601081526020016f534d5f5355425f554e444552464c4f5760801b815250614069565b9392505050565b80820182811015612a3a576040805162461bcd60e51b815260206004820152600f60248201526e534d5f4144445f4f564552464c4f5760881b604482015290519081900360640190fd5b92915050565b600042612a51856301e133806129f0565b1090506001856004811115612a6257fe5b1415612c4157612a7061486b565b60405163117d7ab160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063117d7ab190612aaa9060009088906004016156da565b6101806040518083038186803b158015612ac357600080fd5b505af4158015612ad7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612afb9190614dde565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091612b3d918591906004016156e8565b60606040518083038186803b158015612b5557600080fd5b505af4158015612b69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8d9190614a81565b9250925050600084612ba457836101000151612bb1565b6012546001600160a01b03165b9050612bcd818486602001518588604001518960e00151614101565b612be95760405162461bcd60e51b81526004016108839061540a565b8515612c38576000612c0e85610140015186610120015161426890919063ffffffff16565b9050612c1a82826142c6565b612c365760405162461bcd60e51b81526004016108839061548a565b505b505050506131b8565b6002856004811115612c4f57fe5b1415612e1957612c5d6147af565b604051634ce4436960e11b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906399c886d290612c979060009088906004016156da565b6101206040518083038186803b158015612cb057600080fd5b505af4158015612cc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce89190614f35565b8051604051630270f5c360e51b8152919250600091737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091612d279185916004016156e8565b60606040518083038186803b158015612d3f57600080fd5b505af4158015612d53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d779190614a81565b50509050600083612d8c578260a00151612d99565b6012546001600160a01b03165b9050612daa82828560200151614346565b612dc65760405162461bcd60e51b81526004016108839061540a565b8415612e11576000612de98460e001518560c0015161426890919063ffffffff16565b9050612df582826142c6565b612c385760405162461bcd60e51b81526004016108839061548a565b5050506131b8565b6003856004811115612e2757fe5b1415612fda57612e3561480c565b60405163ac1ecdb360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063ac1ecdb390612e6f9060009088906004016156da565b6101206040518083038186803b158015612e8857600080fd5b505af4158015612e9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ec09190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091612f02918591906004016156e8565b60606040518083038186803b158015612f1a57600080fd5b505af4158015612f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f529190614a81565b9250925050600084612f68578360a00151612f75565b6012546001600160a01b03165b9050612f9b8460200151612f895783612f8b565b825b8286604001518760800151614456565b612fb75760405162461bcd60e51b81526004016108839061540a565b8515612c38576000612c0e8560e001518660c0015161426890919063ffffffff16565b6004856004811115612fe857fe5b14156131b857612ff661480c565b6040516311c8197f60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906311c8197f906130309060009088906004016156da565b6101206040518083038186803b15801561304957600080fd5b505af415801561305d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130819190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb860916130c3918591906004016156e8565b60606040518083038186803b1580156130db57600080fd5b505af41580156130ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131139190614a81565b9250925050600084613129578360a00151613136565b6012546001600160a01b03165b905061314a8460200151612f895783612f8b565b6131665760405162461bcd60e51b81526004016108839061540a565b85156131b35760006131898560e001518660c0015161426890919063ffffffff16565b905061319582826142c6565b6131b15760405162461bcd60e51b81526004016108839061548a565b505b505050505b610a20600084614576565b8051604051630270f5c360e51b81526000918291829182918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613208918591906004016156e8565b60606040518083038186803b15801561322057600080fd5b505af4158015613234573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132589190614a81565b60208901516040516306c5d9cf60e41b8152939850919650945060009173c82938b53e0e190459ba4e3502bf26f19476018391636c5d9cf0916132a3916010918a9190600401615507565b60206040518083038186803b1580156132bb57600080fd5b505af41580156132cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f39190614ff2565b60408089015190516306c5d9cf60e41b815291925060009173c82938b53e0e190459ba4e3502bf26f19476018391636c5d9cf091613339916010918a9190600401615507565b60206040518083038186803b15801561335157600080fd5b505af4158015613365573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133899190614ff2565b9050866001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156133c657600080fd5b505af11580156133da573d6000803e3d6000fd5b505050506133f18789610100015188888686613401565b9799969850949694959350505050565b600080600080733a09fa6c7cb82fc94c377087cf59ec9529094e616355776b778b88886040518463ffffffff1660e01b8152600401613442939291906152ae565b604080518083038186803b15801561345957600080fd5b505af415801561346d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613491919061500a565b9150915081600014806134a2575080155b156134b4578585935093505050613564565b6134bf888b8461358d565b6134ca878b8361358d565b6040516335313c2160e11b81526001600160a01b038b1690636a627842906134f6908c906004016151ad565b602060405180830381600087803b15801561351057600080fd5b505af1158015613524573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135489190614ff2565b5061355386836129b4565b935061355f85826129b4565b925050505b965096945050505050565b81156135805761358084868461358d565b8015610a2057610a208386835b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b6020831061360a5780518252601f1990920191602091820191016135eb565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461366c576040519150601f19603f3d011682016040523d82523d6000602084013e613671565b606091505b509150915081801561369f57508051158061369f575080806020019051602081101561369c57600080fd5b50515b610a20576040805162461bcd60e51b8152602060048201526012602482015271151217d514905394d1915497d1905253115160721b604482015290519081900360640190fd5b600080611e76838460020154600101614597565b60005a905061370661486b565b60405163a81df10f60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063a81df10f9061373e906000906004016154fe565b6101806040518083038186803b15801561375757600080fd5b505af415801561376b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061378f9190614dde565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb860916137d1918591906004016156e8565b60606040518083038186803b1580156137e957600080fd5b505af41580156137fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138219190614a81565b6001600160a01b038082166000908152600a60205260408082205492851682528120549396509194509092506060913091613875916138699190610e8a9062010fe0906129f0565b610140880151906129b4565b604051630814494760e41b9061388f9089906024016156fc565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516138cd919061518e565b60006040518083038160008787f1925050503d806000811461390b576040519150601f19603f3d011682016040523d82523d6000602084013e613910565b606091505b50909250905060018261393f5761393c866101000151868860200151878a604001518b60e00151614101565b90505b806139535761394e60006145de565b61395d565b61395d6000614631565b60008061397b8861014001518961012001518b8b6101000151614656565b915091508415156000600201547ff0cc99aeb224e65869630a14e23683d20b9c535c00427b50024ce8b6b21d35c38685856040516139bb939291906152da565b60405180910390a3505050505050505050565b60005a90506139db6147af565b6040516313ed2f6160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906313ed2f6190613a13906000906004016154fe565b6101206040518083038186803b158015613a2c57600080fd5b505af4158015613a40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a649190614f35565b8051604051630270f5c360e51b8152919250600091737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613aa39185916004016156e8565b60606040518083038186803b158015613abb57600080fd5b505af4158015613acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af39190614a81565b5090915060009050606030613b1b613b1062010fe061d6d86129f0565b60e0870151906129b4565b60405163059bab1160e51b90613b3590889060240161579c565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051613b73919061518e565b60006040518083038160008787f1925050503d8060008114613bb1576040519150601f19603f3d011682016040523d82523d6000602084013e613bb6565b606091505b509092509050600182613bd957613bd6848660a001518760200151614346565b90505b80613bed57613be860006145de565b613bf7565b613bf76000614631565b600080613c128760e001518860c001518a8a60a00151614656565b915091508415156000600201547ff0cc99aeb224e65869630a14e23683d20b9c535c00427b50024ce8b6b21d35c3868585604051613c52939291906152da565b60405180910390a35050505050505050565b60005a9050613c7161480c565b604051632e9d30e160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e90632e9d30e190613ca9906000906004016154fe565b6101206040518083038186803b158015613cc257600080fd5b505af4158015613cd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cfa9190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613d3c918591906004016156e8565b60606040518083038186803b158015613d5457600080fd5b505af4158015613d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d8c9190614a81565b925092505060006060306001600160a01b0316613dec613de16000600a0160008960200151613dbb5788613dbd565b875b6001600160a01b0316815260208101919091526040016000205462010fe0906129f0565b60e0880151906129b4565b604051637cb45e1560e11b90613e069089906024016154ef565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051613e44919061518e565b60006040518083038160008787f1925050503d8060008114613e82576040519150601f19603f3d011682016040523d82523d6000602084013e613e87565b606091505b509092509050600182613ebf57613ebc8660200151613ea65785613ea8565b845b8760a0015188604001518960800151614456565b90505b80613ed357613ece60006145de565b613edd565b613edd6000614631565b60008061397b8860e001518960c001518b8b60a00151614656565b60005a9050613f0561480c565b60405163a1ba139560e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063a1ba139590613f3d906000906004016154fe565b6101206040518083038186803b158015613f5657600080fd5b505af4158015613f6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8e9190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613fd0918591906004016156e8565b60606040518083038186803b158015613fe857600080fd5b505af4158015613ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140209190614a81565b925092505060006060306001600160a01b031661404f613de16000600a0160008960200151613dbb5788613dbd565b60405163c35930c360e01b90613e069089906024016154ef565b81830381848211156140f95760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156140be5781810151838201526020016140a6565b50505050905090810190601f1680156140eb5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b509392505050565b6001600160a01b038381166000908152600a6020526040808220549288168252812054909182916060913091614136916129f0565b604051633ed76f1760e01b9061415a908d908d908d908d908d908d906024016151da565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051614198919061518e565b60006040518083038160008787f1925050503d80600081146141d6576040519150601f19603f3d011682016040523d82523d6000602084013e6141db565b606091505b50915091508161425c57876001600160a01b0316896001600160a01b03166000805160206158e08339815191528984604051614218929190615812565b60405180910390a3856001600160a01b0316896001600160a01b03166000805160206158e08339815191528784604051614253929190615812565b60405180910390a35b50979650505050505050565b60008115806142835750508082028282828161428057fe5b04145b612a3a576040805162461bcd60e51b815260206004820152600f60248201526e534d5f4d554c5f4f564552464c4f5760881b604482015290519081900360640190fd5b6000816142d557506001612a3a565b6040516001600160a01b0384169083156108fc029084906000818181858888f193505050509050801515836001600160a01b03167fdbef2fc26e7694e7a1c5a4801b1ad144136d149cf76f310a780689b4087f0ffe8460405161433891906154fe565b60405180910390a392915050565b600081614355575060016129e9565b60006060306001600160a01b031661d6d863c9cd976060e01b88888860006040516024016143869493929190615214565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516143c4919061518e565b60006040518083038160008787f1925050503d8060008114614402576040519150601f19603f3d011682016040523d82523d6000602084013e614407565b606091505b50915091508161444d57856001600160a01b0316856001600160a01b03166000805160206158e08339815191528684604051614444929190615812565b60405180910390a35b50949350505050565b6000826144655750600161456e565b6001600160a01b0385166000908152600a6020526040808220549051606091309163e5b1be6560e01b906144a3908b908b908b908b90602401615214565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516144e1919061518e565b60006040518083038160008787f1925050503d806000811461451f576040519150601f19603f3d011682016040523d82523d6000602084013e614524565b606091505b50915091508161456a57866001600160a01b0316866001600160a01b03166000805160206158e08339815191528784604051614561929190615812565b60405180910390a35b5090505b949350505050565b60009081526003909101602052604081208181556001810182905560020155565b60008181526003830160205260408120805482919060ff8116906145c590610100900463ffffffff166146ea565b92506145d38160ff16614729565b935050509250929050565b600281015460009081526003820160205260409020805465010000000000900460ff161561460d576003614610565b60025b815460ff91909116650100000000000265ff00000000001990911617905550565b6002808201546000908152600390920160205260408220828155600181018390550155565b600080806146648787614268565b905061467b610e74610e8a613908815a8a906129b4565b925060006146a08261469b6000600601548761426890919063ffffffff16565b614799565b90506146ac82826129b4565b92506146b833826142c6565b6146d45760405162461bcd60e51b81526004016108839061548a565b6146de85846142c6565b50505094509492505050565b600063ffffffff82811614156147035750600019610abd565b63ffffffff821661471657506000610abd565b5063ffffffff8116635fee57f001919050565b6000600182141561473c57506001610abd565b600282141561474d57506002610abd565b600382141561475e57506004610abd565b600482141561476f57506004610abd565b600582141561478057506003610abd565b600682141561479157506003610abd565b506000919050565b60008183106147a857816129e9565b5090919050565b604051806101200160405280600063ffffffff16815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b604051806101200160405280600063ffffffff168152602001600015158152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b604051806101800160405280600063ffffffff16815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b8035610abd8161589a565b8051610abd8161589a565b8035610abd816158b2565b8051610abd816158b2565b600061012080838503121561491c578182fd5b6149258161584a565b91505061493182614a4f565b815261493f602083016148f3565b60208201526040820135604082015260608201356060820152614964608083016148f3565b608082015261497560a083016148dd565b60a082015260c082013560c082015260e082013560e082015261010080830135818301525092915050565b60006101208083850312156149b3578182fd5b6149bc8161584a565b9150506149c882614a5a565b81526149d6602083016148fe565b602082015260408201516040820152606082015160608201526149fb608083016148fe565b6080820152614a0c60a083016148e8565b60a082015260c082015160c082015260e082015160e082015261010080830151818301525092915050565b60006101208284031215614a49578081fd5b50919050565b8035610abd816158cd565b8051610abd816158cd565b600060208284031215614a76578081fd5b81356129e98161589a565b600080600060608486031215614a95578182fd5b8351614aa08161589a565b6020850151909350614ab18161589a565b6040850151909250614ac28161589a565b809150509250925092565b600080600060608486031215614ae1578081fd5b8335614aec8161589a565b92506020840135614afc8161589a565b929592945050506040919091013590565b60008060008060008060c08789031215614b25578384fd5b8635614b308161589a565b95506020870135614b408161589a565b9450604087013593506060870135614b578161589a565b92506080870135915060a0870135614b6e816158b2565b809150509295509295509295565b60008060008060808587031215614b91578182fd5b8435614b9c8161589a565b93506020850135614bac8161589a565b9250604085013591506060850135614bc3816158b2565b939692955090935050565b60008060408385031215614be0578182fd5b8235614beb8161589a565b91506020830135614bfb816158b2565b809150509250929050565b600080600060608486031215614c1a578081fd5b8335614c258161589a565b92506020840135614c35816158c0565b91506040840135614ac2816158b2565b60008060408385031215614c57578182fd5b8235614c628161589a565b946020939093013593505050565b600060208284031215614c81578081fd5b81516129e9816158b2565b600060208284031215614c9d578081fd5b8151600681106129e9578182fd5b60008060408385031215614cbd578182fd5b8251614cc8816158c0565b6020939093015192949293505050565b60006101208284031215614cea578081fd5b6129e98383614909565b60006101208284031215614d06578081fd5b6129e983836149a0565b60006101208284031215614d22578081fd5b6129e98383614a37565b6000610180808385031215614d3f578182fd5b614d488161584a565b9050614d5383614a4f565b81526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c0820152614d9d60e084016148f3565b60e0820152610100614db08185016148dd565b9082015261012083810135908201526101408084013590820152610160928301359281019290925250919050565b6000610180808385031215614df1578182fd5b614dfa8161584a565b9050614e0583614a5a565b81526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c0820152614e4f60e084016148fe565b60e0820152610100614e628185016148e8565b9082015261012083810151908201526101408084015190820152610160928301519281019290925250919050565b60006101a08284031215614a49578081fd5b6000610120808385031215614eb5578182fd5b614ebe8161584a565b9050614ec983614a4f565b8152602083013560208201526040830135604082015260608301356060820152614ef5608084016148f3565b6080820152614f0660a084016148dd565b60a082015260c083013560c082015260e083013560e08201526101008084013581830152508091505092915050565b6000610120808385031215614f48578182fd5b614f518161584a565b9050614f5c83614a5a565b8152602083015160208201526040830151604082015260608301516060820152614f88608084016148fe565b6080820152614f9960a084016148e8565b60a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b60006101408284031215614a49578081fd5b600060208284031215614feb578081fd5b5035919050565b600060208284031215615003578081fd5b5051919050565b6000806040838503121561501c578182fd5b505080516020909101519092909150565b6001600160a01b03169052565b15159052565b6000815180845261505881602086016020860161586e565b601f01601f19169290920160200192915050565b6005811061507657fe5b9052565b63ffffffff8151168252602081015115156020830152604081015160408301526060810151606083015260808101516150b6608084018261503a565b5060a08101516150c960a084018261502d565b5060c0818101519083015260e0808201519083015261010090810151910152565b80356150f58161589a565b6001600160a01b03168252602081013561510e8161589a565b61511b602084018261502d565b50604081013560408301526060810135606083015261513c608082016148f3565b615149608084018261503a565b5061515660a082016148dd565b61516360a084018261502d565b5060c0818101359083015260e0808201359083015261010090810135910152565b63ffffffff169052565b600082516151a081846020870161586e565b9190910192915050565b90565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0396871681529486166020860152604085019390935293166060830152608082019290925290151560a082015260c00190565b6001600160a01b0394851681529290931660208301526040820152901515606082015260800190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b03841681526060810161529e602083018561506c565b8215156040830152949350505050565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b6000606082526152ed6060830186615040565b60208301949094525060400152919050565b602081016006831061530d57fe5b91905290565b60408101615321828561506c565b8260208301529392505050565b602080825260099082015268125117d313d0d2d15160ba1b604082015260600190565b6020808252601d908201527f49445f494e53554646494349454e545f4f55545055545f414d4f554e54000000604082015260600190565b602080825260159082015274125117d3d491115497d393d517d15610d151511151605a1b604082015260600190565b60208082526015908201527449445f494e56414c49445f4f524445525f5459504560581b604082015260600190565b6020808252600a9082015269125117d156141254915160b21b604082015260600190565b60208082526010908201526f125117d4915195539117d1905253115160821b604082015260600190565b6020808252600c908201526b24a22fa327a92124a22222a760a11b604082015260600190565b602080825260169082015275125117d25394d551919250d251539517d05353d5539560521b604082015260600190565b602080825260149082015273125117d1551217d4915195539117d1905253115160621b604082015260600190565b6020808252601c908201527f49445f494e53554646494349454e545f494e5055545f414d4f554e5400000000604082015260600190565b6101208101612a3a828461507a565b90815260200190565b9283526001600160a01b03919091166020830152604082015260600190565b838152610160810161553b60208301856150ea565b82610140830152949350505050565b8381526101e0810161556760208301615562866148dd565b61502d565b615573602085016148dd565b615580604084018261502d565b506040840135606083015260608401356080830152608084013560a083015260a084013560c083015260c084013560e083015261010060e0850135818401526155ca8186016148f3565b90506101206155db8185018361503a565b6155e68187016148dd565b9150506101406155f88185018361502d565b61016091508086013582850152506101808186013581850152808601356101a08501525050826101c0830152949350505050565b828152610160810161564460208301615562856148dd565b615650602084016148dd565b61565d604084018261502d565b506040830135606083015260608301356080830152608083013560a083015261568860a084016148f3565b61569560c084018261503a565b506156a260c084016148dd565b6156af60e084018261502d565b5061010060e08401358184015261012081850135818501528085013561014085015250509392505050565b918252602082015260400190565b91825263ffffffff16602082015260400190565b600061018082019050615710828451615184565b6020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015161575e60e084018261503a565b50610100808401516157728285018261502d565b50506101208381015190830152610140808401519083015261016092830151929091019190915290565b60006101208201905063ffffffff835116825260208301516020830152604083015160408301526060830151606083015260808301511515608083015260a08301516157eb60a084018261502d565b5060c083015160c083015260e083015160e083015261010080840151818401525092915050565b60008382526040602083015261456e6040830184615040565b92835260208301919091526001600160a01b0316604082015260600190565b60405181810167ffffffffffffffff8111828210171561586657fe5b604052919050565b60005b83811015615889578181015183820152602001615871565b8381111561170f5750506000910152565b6001600160a01b03811681146158af57600080fd5b50565b80151581146158af57600080fd5b600581106158af57600080fd5b63ffffffff811681146158af57600080fdfe786212e89f390a4c768f3b935b85e0ec2561a24b6f48cda3c4b48a996b211592a264697066735822122050c5be39f6e719197a07033de942e62df6a5b8c83eeee1e0ed943ade09a5d8b764736f6c63430007050033000000000000000000000000673662e97b05e001816c380ba5a628d2e29f55d1000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000008971dc0105a22e54d81593af02167fc82af935e3

Deployed Bytecode

0x6080604052600436106102975760003560e01c8063945317131161015a578063d09ef241116100c1578063e5e7988e1161007a578063e5e7988e146107c0578063e6a0cc94146107e0578063f0aa61a3146107f5578063f968bc2a1461080a578063fe0d94c11461082a578063fe173b971461084a5761029e565b8063d09ef2411461070a578063d22e924214610738578063e085272f14610758578063e177246e1461076b578063e30a49931461078b578063e5b1be65146107a05761029e565b8063b800522611610113578063b800522614610660578063be58130414610675578063bf6b874e14610695578063c35930c3146106b5578063c45a0155146106d5578063c9cd9760146106ea5761029e565b806394531713146105a05780639718f627146105c0578063af482b58146105e0578063b1af1bda14610600578063b32ac93614610620578063b3756220146106405761029e565b806345fa8aae116101fe5780636a42b8f8116101b75780636a42b8f8146105015780636de3c67c1461051657806377632ec21461052b5780637f6a1caf1461054b578063814494701461056b5780638da5cb5b1461058b5761029e565b806345fa8aae1461043d5780634c0160161461046a578063514fcac71461048a57806354df6d74146104aa57806357a62a4f146104d75780635e45da23146104ec5761029e565b8063342aa8b511610250578063342aa8b51461037b578063390ce0d31461039b5780633bbac579146103c85780633ed76f17146103e85780633fc8cef31461040857806343133c4b1461042a5761029e565b806307ddd3bc146102a357806313af4035146102cc5780631776834a146102ee57806317818a5c1461030e57806320a68fab1461033b5780632da7cbfd146103685761029e565b3661029e57005b600080fd5b6102b66102b1366004614fc8565b61085f565b6040516102c391906154fe565b60405180910390f35b3480156102d857600080fd5b506102ec6102e7366004614a65565b61090a565b005b3480156102fa57600080fd5b506102ec610309366004614fda565b610990565b34801561031a57600080fd5b5061032e610329366004614fda565b610a27565b6040516102c3919061579c565b34801561034757600080fd5b5061035b610356366004614a65565b610ac2565b6040516102c391906152cf565b6102b6610376366004614e90565b610ae0565b34801561038757600080fd5b506102ec610396366004614bce565b610b46565b3480156103a757600080fd5b506103bb6103b6366004614fda565b610bd2565b6040516102c391906154ef565b3480156103d457600080fd5b5061035b6103e3366004614a65565b610c65565b3480156103f457600080fd5b506102ec610403366004614b0d565b610c7a565b34801561041457600080fd5b5061041d610c9a565b6040516102c391906151ad565b6102b6610438366004614d10565b610ca9565b34801561044957600080fd5b5061045d610458366004614fda565b610d0f565b6040516102c391906152ff565b34801561047657600080fd5b5061035b610485366004614a65565b610d9b565b34801561049657600080fd5b506102ec6104a5366004614fda565b610db9565b3480156104b657600080fd5b506104ca6104c5366004614fda565b610edd565b6040516102c391906156fc565b3480156104e357600080fd5b506102b6610f70565b3480156104f857600080fd5b506102b6610f76565b34801561050d57600080fd5b506102b6610f7c565b34801561052257600080fd5b506102b6610f82565b34801561053757600080fd5b5061035b610546366004614fda565b610f88565b34801561055757600080fd5b506102ec610566366004614c06565b610f9d565b34801561057757600080fd5b506102ec610586366004614d2c565b611123565b34801561059757600080fd5b5061041d6113ca565b3480156105ac57600080fd5b506102ec6105bb366004614fda565b6113d9565b3480156105cc57600080fd5b506102b66105db366004614a65565b61143d565b3480156105ec57600080fd5b5061035b6105fb366004614a65565b611458565b34801561060c57600080fd5b506103bb61061b366004614fda565b611476565b34801561062c57600080fd5b506102ec61063b366004614fda565b6114b8565b34801561064c57600080fd5b506102ec61065b366004614ea2565b61151c565b34801561066c57600080fd5b506102b6611715565b34801561068157600080fd5b506102ec610690366004614fda565b61171c565b3480156106a157600080fd5b506102b66106b0366004614a65565b6117ea565b3480156106c157600080fd5b506102ec6106d0366004614cd8565b611805565b3480156106e157600080fd5b5061041d611dab565b3480156106f657600080fd5b506102ec610705366004614acd565b611dba565b34801561071657600080fd5b5061072a610725366004614fda565b611de9565b6040516102c3929190615313565b34801561074457600080fd5b506102ec610753366004614c45565b611e7f565b6102b6610766366004614d10565b611f11565b34801561077757600080fd5b506102ec610786366004614fda565b611f76565b34801561079757600080fd5b506102b6611fdc565b3480156107ac57600080fd5b506102ec6107bb366004614b7c565b611fe2565b3480156107cc57600080fd5b5061035b6107db366004614a65565b6121ed565b3480156107ec57600080fd5b506102b661220b565b34801561080157600080fd5b506102b6612211565b34801561081657600080fd5b506102ec610825366004614cd8565b612217565b34801561083657600080fd5b506102ec610845366004614fda565b6126e3565b34801561085657600080fd5b506102b6612989565b600060155460011461088c5760405162461bcd60e51b81526004016108839061532e565b60405180910390fd5b60006015819055604051636587992160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e916365879921916108cb9190869060040161562c565b60006040518083038186803b1580156108e357600080fd5b505af41580156108f7573d6000803e3d6000fd5b5050600154925050506001601555919050565b6012546001600160a01b031633146109345760405162461bcd60e51b815260040161088390615434565b601280546001600160a01b0319166001600160a01b0383811691909117918290556040517f50146d0e3c60aa1d17a70635b05494f864e86144a2201275021014fbf08bafe2926109859216906151ad565b60405180910390a150565b6012546001600160a01b031633146109ba5760405162461bcd60e51b815260040161088390615434565b604051636702eca360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e90636702eca3906109f49060009085906004016156da565b60006040518083038186803b158015610a0c57600080fd5b505af4158015610a20573d6000803e3d6000fd5b5050505050565b610a2f6147af565b604051634ce4436960e11b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906399c886d290610a699060009086906004016156da565b6101206040518083038186803b158015610a8257600080fd5b505af4158015610a96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614f35565b90505b919050565b6001600160a01b03166000908152600d602052604090205460ff1690565b6000601554600114610b045760405162461bcd60e51b81526004016108839061532e565b60006015819055604051638f0e6bef60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e91638f0e6bef916108cb9190869060109060040161554a565b6012546001600160a01b03163314610b705760405162461bcd60e51b815260040161088390615434565b6001600160a01b03821660009081526013602052604090819020805460ff1916831515179055517f70af441dbb427737e6a5ef2cf5b664321011765ce1d19ce4a69cd024e69d4f2f90610bc69084908490615266565b60405180910390a15050565b610bda61480c565b60405163ac1ecdb360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063ac1ecdb390610c149060009086906004016156da565b6101206040518083038186803b158015610c2d57600080fd5b505af4158015610c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614cf4565b60136020526000908152604090205460ff1681565b610c8685878684611fe2565b610c9283878484611fe2565b505050505050565b6011546001600160a01b031690565b6000601554600114610ccd5760405162461bcd60e51b81526004016108839061532e565b6000601581905560405163758e99b360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9163758e99b3916108cb91908690601090600401615526565b604051634dc1ffe760e11b8152600090737218d567b671e36c4e6b3257b8919196cf68ff5e90639b83ffce90610d4b90849086906004016156da565b60206040518083038186803b158015610d6357600080fd5b505af4158015610d77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614c8c565b6001600160a01b03166000908152600f602052604090205460ff1690565b601554600114610ddb5760405162461bcd60e51b81526004016108839061532e565b6000601581905560405163317e626f60e21b81528190737218d567b671e36c4e6b3257b8919196cf68ff5e9063c5f989bc90610e1d90849087906004016156da565b604080518083038186803b158015610e3457600080fd5b505af4158015610e48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6c9190614cab565b9150915042610e9062015180610e8a610e83610f7c565b85906129b4565b906129f0565b10610ead5760405162461bcd60e51b815260040161088390615388565b610eba8282856001612a40565b50506000908152600b60205260409020805460ff19166001908117909155601555565b610ee561486b565b60405163117d7ab160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063117d7ab190610f1f9060009086906004016156da565b6101806040518083038186803b158015610f3857600080fd5b505af4158015610f4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aba9190614dde565b60085490565b60055490565b60005490565b60075490565b6000908152600b602052604090205460ff1690565b6012546001600160a01b03163314610fc75760405162461bcd60e51b815260040161088390615434565b6000826004811115610fd557fe5b1415610ff35760405162461bcd60e51b8152600401610883906153b7565b600182600481111561100157fe5b1415611030576001600160a01b0383166000908152600c60205260409020805460ff19168215151790556110e3565b600282600481111561103e57fe5b141561106d576001600160a01b0383166000908152600d60205260409020805460ff19168215151790556110e3565b600382600481111561107b57fe5b14156110aa576001600160a01b0383166000908152600f60205260409020805460ff19168215151790556110e3565b60048260048111156110b857fe5b14156110e3576001600160a01b0383166000908152600e60205260409020805460ff19168215151790555b7f3d5726ff7c14d1fb7a71ac46100040a8b7a9edb7624e96b8abde4a15649fdf4983838360405161111693929190615281565b60405180910390a1505050565b3330146111425760405162461bcd60e51b815260040161088390615434565b4281610160015110156111675760405162461bcd60e51b8152600401610883906153e6565b6000806000806000611178866131c3565b9450945094509450945081600014158061119157508015155b801561124d575060608601516080870151875163ffffffff1660009081526009602052604090819020549051635c1941c160e11b8152733a09fa6c7cb82fc94c377087cf59ec9529094e619363b8328382936111fd93919290916001600160a01b03169060040161582b565b60206040518083038186803b15801561121557600080fd5b505af4158015611229573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061124d9190614c70565b1561138c5781156112f15760a0860151604051634f57f14d60e11b8152733a09fa6c7cb82fc94c377087cf59ec9529094e6191639eafe29a916112989189918991889160040161523d565b604080518083038186803b1580156112af57600080fd5b505af41580156112c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e7919061500a565b909250905061138c565b801561138c5760c086015160405163aceb17df60e01b8152733a09fa6c7cb82fc94c377087cf59ec9529094e619163aceb17df916113379189918891879160040161523d565b604080518083038186803b15801561134e57600080fd5b505af4158015611362573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611386919061500a565b90925090505b811580159061139a57508015155b156113b8576113b28587610100015186868686613401565b90925090505b610c928661010001518585858561356f565b6012546001600160a01b031681565b6012546001600160a01b031633146114035760405162461bcd60e51b815260040161088390615434565b604051630fd1437d60e11b8152737218d567b671e36c4e6b3257b8919196cf68ff5e90631fa286fa906109f49060009085906004016156da565b6001600160a01b03166000908152600a602052604090205490565b6001600160a01b03166000908152600c602052604090205460ff1690565b61147e61480c565b6040516311c8197f60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906311c8197f90610c149060009086906004016156da565b6012546001600160a01b031633146114e25760405162461bcd60e51b815260040161088390615434565b60405163153cc2fd60e31b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063a9e617e8906109f49060009085906004016156da565b33301461153b5760405162461bcd60e51b815260040161088390615434565b4281610100015110156115605760405162461bcd60e51b8152600401610883906153e6565b8051604051630270f5c360e51b8152600091737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb8609161159d918591906004016156e8565b60606040518083038186803b1580156115b557600080fd5b505af41580156115c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ed9190614a81565b50509050806001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561162c57600080fd5b505af1158015611640573d6000803e3d6000fd5b505050506116538182846020015161358d565b600080826001600160a01b03166389afcb448560a001516040518263ffffffff1660e01b815260040161168691906151ad565b6040805180830381600087803b15801561169f57600080fd5b505af11580156116b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d7919061500a565b91509150836040015182101580156116f3575083606001518110155b61170f5760405162461bcd60e51b81526004016108839061545a565b50505050565b6201518081565b60155460011461173e5760405162461bcd60e51b81526004016108839061532e565b60006015819055604051631ae4f06f60e01b81528190737218d567b671e36c4e6b3257b8919196cf68ff5e90631ae4f06f9061178090849087906004016156da565b604080518083038186803b15801561179757600080fd5b505af41580156117ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117cf9190614cab565b915091506117e08282856000612a40565b5050600160155550565b6001600160a01b031660009081526010602052604090205490565b3330146118245760405162461bcd60e51b815260040161088390615434565b4281610100015110156118495760405162461bcd60e51b8152600401610883906153e6565b8051604051630270f5c360e51b815260009182918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb8609161188a918591906004016156e8565b60606040518083038186803b1580156118a257600080fd5b505af41580156118b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118da9190614a81565b92509250925060008085602001516118f35783836118f6565b82845b915091506000601073c82938b53e0e190459ba4e3502bf26f194760183636c5d9cf09091858a604001516040518463ffffffff1660e01b815260040161193e93929190615507565b60206040518083038186803b15801561195657600080fd5b505af415801561196a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061198e9190614ff2565b90506000869050806001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156119d057600080fd5b505af11580156119e4573d6000803e3d6000fd5b5050505060008860200151611a8457606089015160405163049c271760e41b8152735d0434d41c77e4d9a858000f3939c0c4a05b0e26916349c2717091611a2f918c916004016151c1565b60206040518083038186803b158015611a4757600080fd5b505af4158015611a5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a7f9190614ff2565b611b10565b6060890151604051635ee8722f60e01b8152735d0434d41c77e4d9a858000f3939c0c4a05b0e2691635ee8722f91611ac0918c916004016151c1565b60206040518083038186803b158015611ad857600080fd5b505af4158015611aec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b109190614ff2565b905080831015611b325760405162461bcd60e51b8152600401610883906154b8565b6000808a60200151611b4a5760008b60600151611b52565b8a6060015160005b91509150611b61878b8561358d565b6011546001600160a01b038781169116148015611b7f57508a608001515b15611d37576040516336cd320560e11b81526001600160a01b03851690636d9a640a90611bb49085908590309060040161582b565b600060405180830381600087803b158015611bce57600080fd5b505af1158015611be2573d6000803e3d6000fd5b505060115460608e0151604051632e1a7d4d60e01b81526001600160a01b039092169350632e1a7d4d9250611c19916004016154fe565b600060405180830381600087803b158015611c3357600080fd5b505af1158015611c47573d6000803e3d6000fd5b5050505060008b60a001516001600160a01b03168c6060015161271090604051611c70906151aa565b600060405180830381858888f193505050503d8060008114611cae576040519150601f19603f3d011682016040523d82523d6000602084013e611cb3565b606091505b5050905080611d315760a08c015160608d015160405163032b310f60e51b815273c82938b53e0e190459ba4e3502bf26f1947601839263656621e092611d00926010929190600401615507565b60006040518083038186803b158015611d1857600080fd5b505af4158015611d2c573d6000803e3d6000fd5b505050505b50611d9e565b60a08b01516040516336cd320560e11b81526001600160a01b03861691636d9a640a91611d6b91869186919060040161582b565b600060405180830381600087803b158015611d8557600080fd5b505af1158015611d99573d6000803e3d6000fd5b505050505b5050505050505050505050565b6004546001600160a01b031690565b333014611dd95760405162461bcd60e51b815260040161088390615434565b611de483838361358d565b505050565b60405163317e626f60e21b81526000908190737218d567b671e36c4e6b3257b8919196cf68ff5e9063c5f989bc90611e2790849087906004016156da565b604080518083038186803b158015611e3e57600080fd5b505af4158015611e52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e769190614cab565b91509150915091565b6012546001600160a01b03163314611ea95760405162461bcd60e51b815260040161088390615434565b60405163b2456a0760e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063b2456a0790611ee59060009086908690600401615507565b60006040518083038186803b158015611efd57600080fd5b505af4158015610c92573d6000803e3d6000fd5b6000601554600114611f355760405162461bcd60e51b81526004016108839061532e565b6000601581905560405162c44ff160e31b8152737218d567b671e36c4e6b3257b8919196cf68ff5e916306227f88916108cb91908690601090600401615526565b6012546001600160a01b03163314611fa05760405162461bcd60e51b815260040161088390615434565b6000819055600481026014556040517f63e09f16584208fba1fc7ff64c62b00f07bec177c0d97ca6689891b1e77a35c7906109859083906154fe565b60015490565b3330146120015760405162461bcd60e51b815260040161088390615434565b6011546001600160a01b03858116911614801561201b5750805b1561214f576040516306c5d9cf60e41b815260009073c82938b53e0e190459ba4e3502bf26f19476018390636c5d9cf09061205f9060109089908890600401615507565b60206040518083038186803b15801561207757600080fd5b505af415801561208b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120af9190614ff2565b601154604051632e1a7d4d60e01b81529192506001600160a01b031690632e1a7d4d906120e09084906004016154fe565b600060405180830381600087803b1580156120fa57600080fd5b505af115801561210e573d6000803e3d6000fd5b50506040516001600160a01b038716925083156108fc02915083906000818181858888f19350505050158015612148573d6000803e3d6000fd5b505061170f565b6040516306c5d9cf60e41b81526121e8908590859073c82938b53e0e190459ba4e3502bf26f19476018390636c5d9cf0906121939060109086908a90600401615507565b60206040518083038186803b1580156121ab57600080fd5b505af41580156121bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e39190614ff2565b61358d565b61170f565b6001600160a01b03166000908152600e602052604090205460ff1690565b60025490565b60145481565b3330146122365760405162461bcd60e51b815260040161088390615434565b42816101000151101561225b5760405162461bcd60e51b8152600401610883906153e6565b8051604051630270f5c360e51b815260009182918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb8609161229c918591906004016156e8565b60606040518083038186803b1580156122b457600080fd5b505af41580156122c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ec9190614a81565b9250925092506000808560200151612305578383612308565b82845b915091506000601073c82938b53e0e190459ba4e3502bf26f194760183636c5d9cf09091858a604001516040518463ffffffff1660e01b815260040161235093929190615507565b60206040518083038186803b15801561236857600080fd5b505af415801561237c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a09190614ff2565b90506000869050806001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156123e257600080fd5b505af11580156123f6573d6000803e3d6000fd5b5050505061240584888461358d565b60008860200151612491576040516357db007f60e11b81526001600160a01b0383169063afb600fe9061243c9086906004016154fe565b60206040518083038186803b15801561245457600080fd5b505afa158015612468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248c9190614ff2565b61250d565b604051632e34fcc760e21b81526001600160a01b0383169063b8d3f31c906124bd9086906004016154fe565b60206040518083038186803b1580156124d557600080fd5b505afa1580156124e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061250d9190614ff2565b905088606001518110156125335760405162461bcd60e51b815260040161088390615351565b6000808a602001516125475760008361254b565b8260005b60115491935091506001600160a01b03878116911614801561256e57508a608001515b15611d37576040516336cd320560e11b81526001600160a01b03851690636d9a640a906125a39085908590309060040161582b565b600060405180830381600087803b1580156125bd57600080fd5b505af11580156125d1573d6000803e3d6000fd5b5050601154604051632e1a7d4d60e01b81526001600160a01b039091169250632e1a7d4d91506126059086906004016154fe565b600060405180830381600087803b15801561261f57600080fd5b505af1158015612633573d6000803e3d6000fd5b5050505060008b60a001516001600160a01b03168461271090604051612658906151aa565b600060405180830381858888f193505050503d8060008114612696576040519150601f19603f3d011682016040523d82523d6000602084013e61269b565b606091505b5050905080611d315760a08c015160405163032b310f60e51b815273c82938b53e0e190459ba4e3502bf26f1947601839163656621e091611d00916010918990600401615507565b6015546001146127055760405162461bcd60e51b81526004016108839061532e565b60006015556040517f892cd8f5b436bd5fb7dac1f11aafb73345d892ba3e9fe09cd94d95ba84928e739061273c90339084906151c1565b60405180910390a160005a90506000805b838110156129045760025461276490600101610f88565b156127d6576040516305d4bd1160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906305d4bd11906127a1906000906004016154fe565b60006040518083038186803b1580156127b957600080fd5b505af41580156127cd573d6000803e3d6000fd5b505050506128fc565b6000806127e360006136e5565b909250905060008260048111156127f657fe5b14806128025750428110155b1561280e575050612904565b60145481014210158061283057503360009081526013602052604090205460ff165b8061286557506000805260136020527f8fa6efc3be94b5b348b21fea823fe8d100408cee9b7f90524494500445d8ff6c5460ff165b6128815760405162461bcd60e51b815260040161088390615434565b600193508382600481111561289257fe5b14156128a5576128a06136f9565b6128f9565b60028260048111156128b357fe5b14156128c1576128a06139ce565b60038260048111156128cf57fe5b14156128dd576128a0613c64565b60048260048111156128eb57fe5b14156128f9576128f9613ef8565b50505b60010161274d565b5080156117e057737218d567b671e36c4e6b3257b8919196cf68ff5e639db74df160006129325a86906129b4565b6040518363ffffffff1660e01b815260040161294f9291906156da565b60006040518083038186803b15801561296757600080fd5b505af415801561297b573d6000803e3d6000fd5b505050505050600160155550565b60065490565b60019190910180546001600160a01b0319166001600160a01b03909216919091179055565b60006129e983836040518060400160405280601081526020016f534d5f5355425f554e444552464c4f5760801b815250614069565b9392505050565b80820182811015612a3a576040805162461bcd60e51b815260206004820152600f60248201526e534d5f4144445f4f564552464c4f5760881b604482015290519081900360640190fd5b92915050565b600042612a51856301e133806129f0565b1090506001856004811115612a6257fe5b1415612c4157612a7061486b565b60405163117d7ab160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063117d7ab190612aaa9060009088906004016156da565b6101806040518083038186803b158015612ac357600080fd5b505af4158015612ad7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612afb9190614dde565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091612b3d918591906004016156e8565b60606040518083038186803b158015612b5557600080fd5b505af4158015612b69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8d9190614a81565b9250925050600084612ba457836101000151612bb1565b6012546001600160a01b03165b9050612bcd818486602001518588604001518960e00151614101565b612be95760405162461bcd60e51b81526004016108839061540a565b8515612c38576000612c0e85610140015186610120015161426890919063ffffffff16565b9050612c1a82826142c6565b612c365760405162461bcd60e51b81526004016108839061548a565b505b505050506131b8565b6002856004811115612c4f57fe5b1415612e1957612c5d6147af565b604051634ce4436960e11b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906399c886d290612c979060009088906004016156da565b6101206040518083038186803b158015612cb057600080fd5b505af4158015612cc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ce89190614f35565b8051604051630270f5c360e51b8152919250600091737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091612d279185916004016156e8565b60606040518083038186803b158015612d3f57600080fd5b505af4158015612d53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d779190614a81565b50509050600083612d8c578260a00151612d99565b6012546001600160a01b03165b9050612daa82828560200151614346565b612dc65760405162461bcd60e51b81526004016108839061540a565b8415612e11576000612de98460e001518560c0015161426890919063ffffffff16565b9050612df582826142c6565b612c385760405162461bcd60e51b81526004016108839061548a565b5050506131b8565b6003856004811115612e2757fe5b1415612fda57612e3561480c565b60405163ac1ecdb360e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063ac1ecdb390612e6f9060009088906004016156da565b6101206040518083038186803b158015612e8857600080fd5b505af4158015612e9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ec09190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091612f02918591906004016156e8565b60606040518083038186803b158015612f1a57600080fd5b505af4158015612f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f529190614a81565b9250925050600084612f68578360a00151612f75565b6012546001600160a01b03165b9050612f9b8460200151612f895783612f8b565b825b8286604001518760800151614456565b612fb75760405162461bcd60e51b81526004016108839061540a565b8515612c38576000612c0e8560e001518660c0015161426890919063ffffffff16565b6004856004811115612fe857fe5b14156131b857612ff661480c565b6040516311c8197f60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906311c8197f906130309060009088906004016156da565b6101206040518083038186803b15801561304957600080fd5b505af415801561305d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130819190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb860916130c3918591906004016156e8565b60606040518083038186803b1580156130db57600080fd5b505af41580156130ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131139190614a81565b9250925050600084613129578360a00151613136565b6012546001600160a01b03165b905061314a8460200151612f895783612f8b565b6131665760405162461bcd60e51b81526004016108839061540a565b85156131b35760006131898560e001518660c0015161426890919063ffffffff16565b905061319582826142c6565b6131b15760405162461bcd60e51b81526004016108839061548a565b505b505050505b610a20600084614576565b8051604051630270f5c360e51b81526000918291829182918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613208918591906004016156e8565b60606040518083038186803b15801561322057600080fd5b505af4158015613234573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132589190614a81565b60208901516040516306c5d9cf60e41b8152939850919650945060009173c82938b53e0e190459ba4e3502bf26f19476018391636c5d9cf0916132a3916010918a9190600401615507565b60206040518083038186803b1580156132bb57600080fd5b505af41580156132cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f39190614ff2565b60408089015190516306c5d9cf60e41b815291925060009173c82938b53e0e190459ba4e3502bf26f19476018391636c5d9cf091613339916010918a9190600401615507565b60206040518083038186803b15801561335157600080fd5b505af4158015613365573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133899190614ff2565b9050866001600160a01b031663caeba2176040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156133c657600080fd5b505af11580156133da573d6000803e3d6000fd5b505050506133f18789610100015188888686613401565b9799969850949694959350505050565b600080600080733a09fa6c7cb82fc94c377087cf59ec9529094e616355776b778b88886040518463ffffffff1660e01b8152600401613442939291906152ae565b604080518083038186803b15801561345957600080fd5b505af415801561346d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613491919061500a565b9150915081600014806134a2575080155b156134b4578585935093505050613564565b6134bf888b8461358d565b6134ca878b8361358d565b6040516335313c2160e11b81526001600160a01b038b1690636a627842906134f6908c906004016151ad565b602060405180830381600087803b15801561351057600080fd5b505af1158015613524573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135489190614ff2565b5061355386836129b4565b935061355f85826129b4565b925050505b965096945050505050565b81156135805761358084868461358d565b8015610a2057610a208386835b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b6020831061360a5780518252601f1990920191602091820191016135eb565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461366c576040519150601f19603f3d011682016040523d82523d6000602084013e613671565b606091505b509150915081801561369f57508051158061369f575080806020019051602081101561369c57600080fd5b50515b610a20576040805162461bcd60e51b8152602060048201526012602482015271151217d514905394d1915497d1905253115160721b604482015290519081900360640190fd5b600080611e76838460020154600101614597565b60005a905061370661486b565b60405163a81df10f60e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063a81df10f9061373e906000906004016154fe565b6101806040518083038186803b15801561375757600080fd5b505af415801561376b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061378f9190614dde565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb860916137d1918591906004016156e8565b60606040518083038186803b1580156137e957600080fd5b505af41580156137fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138219190614a81565b6001600160a01b038082166000908152600a60205260408082205492851682528120549396509194509092506060913091613875916138699190610e8a9062010fe0906129f0565b610140880151906129b4565b604051630814494760e41b9061388f9089906024016156fc565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516138cd919061518e565b60006040518083038160008787f1925050503d806000811461390b576040519150601f19603f3d011682016040523d82523d6000602084013e613910565b606091505b50909250905060018261393f5761393c866101000151868860200151878a604001518b60e00151614101565b90505b806139535761394e60006145de565b61395d565b61395d6000614631565b60008061397b8861014001518961012001518b8b6101000151614656565b915091508415156000600201547ff0cc99aeb224e65869630a14e23683d20b9c535c00427b50024ce8b6b21d35c38685856040516139bb939291906152da565b60405180910390a3505050505050505050565b60005a90506139db6147af565b6040516313ed2f6160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e906313ed2f6190613a13906000906004016154fe565b6101206040518083038186803b158015613a2c57600080fd5b505af4158015613a40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a649190614f35565b8051604051630270f5c360e51b8152919250600091737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613aa39185916004016156e8565b60606040518083038186803b158015613abb57600080fd5b505af4158015613acf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af39190614a81565b5090915060009050606030613b1b613b1062010fe061d6d86129f0565b60e0870151906129b4565b60405163059bab1160e51b90613b3590889060240161579c565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051613b73919061518e565b60006040518083038160008787f1925050503d8060008114613bb1576040519150601f19603f3d011682016040523d82523d6000602084013e613bb6565b606091505b509092509050600182613bd957613bd6848660a001518760200151614346565b90505b80613bed57613be860006145de565b613bf7565b613bf76000614631565b600080613c128760e001518860c001518a8a60a00151614656565b915091508415156000600201547ff0cc99aeb224e65869630a14e23683d20b9c535c00427b50024ce8b6b21d35c3868585604051613c52939291906152da565b60405180910390a35050505050505050565b60005a9050613c7161480c565b604051632e9d30e160e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e90632e9d30e190613ca9906000906004016154fe565b6101206040518083038186803b158015613cc257600080fd5b505af4158015613cd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cfa9190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613d3c918591906004016156e8565b60606040518083038186803b158015613d5457600080fd5b505af4158015613d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d8c9190614a81565b925092505060006060306001600160a01b0316613dec613de16000600a0160008960200151613dbb5788613dbd565b875b6001600160a01b0316815260208101919091526040016000205462010fe0906129f0565b60e0880151906129b4565b604051637cb45e1560e11b90613e069089906024016154ef565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051613e44919061518e565b60006040518083038160008787f1925050503d8060008114613e82576040519150601f19603f3d011682016040523d82523d6000602084013e613e87565b606091505b509092509050600182613ebf57613ebc8660200151613ea65785613ea8565b845b8760a0015188604001518960800151614456565b90505b80613ed357613ece60006145de565b613edd565b613edd6000614631565b60008061397b8860e001518960c001518b8b60a00151614656565b60005a9050613f0561480c565b60405163a1ba139560e01b8152737218d567b671e36c4e6b3257b8919196cf68ff5e9063a1ba139590613f3d906000906004016154fe565b6101206040518083038186803b158015613f5657600080fd5b505af4158015613f6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8e9190614cf4565b8051604051630270f5c360e51b81529192506000918291737218d567b671e36c4e6b3257b8919196cf68ff5e91634e1eb86091613fd0918591906004016156e8565b60606040518083038186803b158015613fe857600080fd5b505af4158015613ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140209190614a81565b925092505060006060306001600160a01b031661404f613de16000600a0160008960200151613dbb5788613dbd565b60405163c35930c360e01b90613e069089906024016154ef565b81830381848211156140f95760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156140be5781810151838201526020016140a6565b50505050905090810190601f1680156140eb5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b509392505050565b6001600160a01b038381166000908152600a6020526040808220549288168252812054909182916060913091614136916129f0565b604051633ed76f1760e01b9061415a908d908d908d908d908d908d906024016151da565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051614198919061518e565b60006040518083038160008787f1925050503d80600081146141d6576040519150601f19603f3d011682016040523d82523d6000602084013e6141db565b606091505b50915091508161425c57876001600160a01b0316896001600160a01b03166000805160206158e08339815191528984604051614218929190615812565b60405180910390a3856001600160a01b0316896001600160a01b03166000805160206158e08339815191528784604051614253929190615812565b60405180910390a35b50979650505050505050565b60008115806142835750508082028282828161428057fe5b04145b612a3a576040805162461bcd60e51b815260206004820152600f60248201526e534d5f4d554c5f4f564552464c4f5760881b604482015290519081900360640190fd5b6000816142d557506001612a3a565b6040516001600160a01b0384169083156108fc029084906000818181858888f193505050509050801515836001600160a01b03167fdbef2fc26e7694e7a1c5a4801b1ad144136d149cf76f310a780689b4087f0ffe8460405161433891906154fe565b60405180910390a392915050565b600081614355575060016129e9565b60006060306001600160a01b031661d6d863c9cd976060e01b88888860006040516024016143869493929190615214565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516143c4919061518e565b60006040518083038160008787f1925050503d8060008114614402576040519150601f19603f3d011682016040523d82523d6000602084013e614407565b606091505b50915091508161444d57856001600160a01b0316856001600160a01b03166000805160206158e08339815191528684604051614444929190615812565b60405180910390a35b50949350505050565b6000826144655750600161456e565b6001600160a01b0385166000908152600a6020526040808220549051606091309163e5b1be6560e01b906144a3908b908b908b908b90602401615214565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516144e1919061518e565b60006040518083038160008787f1925050503d806000811461451f576040519150601f19603f3d011682016040523d82523d6000602084013e614524565b606091505b50915091508161456a57866001600160a01b0316866001600160a01b03166000805160206158e08339815191528784604051614561929190615812565b60405180910390a35b5090505b949350505050565b60009081526003909101602052604081208181556001810182905560020155565b60008181526003830160205260408120805482919060ff8116906145c590610100900463ffffffff166146ea565b92506145d38160ff16614729565b935050509250929050565b600281015460009081526003820160205260409020805465010000000000900460ff161561460d576003614610565b60025b815460ff91909116650100000000000265ff00000000001990911617905550565b6002808201546000908152600390920160205260408220828155600181018390550155565b600080806146648787614268565b905061467b610e74610e8a613908815a8a906129b4565b925060006146a08261469b6000600601548761426890919063ffffffff16565b614799565b90506146ac82826129b4565b92506146b833826142c6565b6146d45760405162461bcd60e51b81526004016108839061548a565b6146de85846142c6565b50505094509492505050565b600063ffffffff82811614156147035750600019610abd565b63ffffffff821661471657506000610abd565b5063ffffffff8116635fee57f001919050565b6000600182141561473c57506001610abd565b600282141561474d57506002610abd565b600382141561475e57506004610abd565b600482141561476f57506004610abd565b600582141561478057506003610abd565b600682141561479157506003610abd565b506000919050565b60008183106147a857816129e9565b5090919050565b604051806101200160405280600063ffffffff16815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b604051806101200160405280600063ffffffff168152602001600015158152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b604051806101800160405280600063ffffffff16815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b8035610abd8161589a565b8051610abd8161589a565b8035610abd816158b2565b8051610abd816158b2565b600061012080838503121561491c578182fd5b6149258161584a565b91505061493182614a4f565b815261493f602083016148f3565b60208201526040820135604082015260608201356060820152614964608083016148f3565b608082015261497560a083016148dd565b60a082015260c082013560c082015260e082013560e082015261010080830135818301525092915050565b60006101208083850312156149b3578182fd5b6149bc8161584a565b9150506149c882614a5a565b81526149d6602083016148fe565b602082015260408201516040820152606082015160608201526149fb608083016148fe565b6080820152614a0c60a083016148e8565b60a082015260c082015160c082015260e082015160e082015261010080830151818301525092915050565b60006101208284031215614a49578081fd5b50919050565b8035610abd816158cd565b8051610abd816158cd565b600060208284031215614a76578081fd5b81356129e98161589a565b600080600060608486031215614a95578182fd5b8351614aa08161589a565b6020850151909350614ab18161589a565b6040850151909250614ac28161589a565b809150509250925092565b600080600060608486031215614ae1578081fd5b8335614aec8161589a565b92506020840135614afc8161589a565b929592945050506040919091013590565b60008060008060008060c08789031215614b25578384fd5b8635614b308161589a565b95506020870135614b408161589a565b9450604087013593506060870135614b578161589a565b92506080870135915060a0870135614b6e816158b2565b809150509295509295509295565b60008060008060808587031215614b91578182fd5b8435614b9c8161589a565b93506020850135614bac8161589a565b9250604085013591506060850135614bc3816158b2565b939692955090935050565b60008060408385031215614be0578182fd5b8235614beb8161589a565b91506020830135614bfb816158b2565b809150509250929050565b600080600060608486031215614c1a578081fd5b8335614c258161589a565b92506020840135614c35816158c0565b91506040840135614ac2816158b2565b60008060408385031215614c57578182fd5b8235614c628161589a565b946020939093013593505050565b600060208284031215614c81578081fd5b81516129e9816158b2565b600060208284031215614c9d578081fd5b8151600681106129e9578182fd5b60008060408385031215614cbd578182fd5b8251614cc8816158c0565b6020939093015192949293505050565b60006101208284031215614cea578081fd5b6129e98383614909565b60006101208284031215614d06578081fd5b6129e983836149a0565b60006101208284031215614d22578081fd5b6129e98383614a37565b6000610180808385031215614d3f578182fd5b614d488161584a565b9050614d5383614a4f565b81526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c0820152614d9d60e084016148f3565b60e0820152610100614db08185016148dd565b9082015261012083810135908201526101408084013590820152610160928301359281019290925250919050565b6000610180808385031215614df1578182fd5b614dfa8161584a565b9050614e0583614a5a565b81526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c0820152614e4f60e084016148fe565b60e0820152610100614e628185016148e8565b9082015261012083810151908201526101408084015190820152610160928301519281019290925250919050565b60006101a08284031215614a49578081fd5b6000610120808385031215614eb5578182fd5b614ebe8161584a565b9050614ec983614a4f565b8152602083013560208201526040830135604082015260608301356060820152614ef5608084016148f3565b6080820152614f0660a084016148dd565b60a082015260c083013560c082015260e083013560e08201526101008084013581830152508091505092915050565b6000610120808385031215614f48578182fd5b614f518161584a565b9050614f5c83614a5a565b8152602083015160208201526040830151604082015260608301516060820152614f88608084016148fe565b6080820152614f9960a084016148e8565b60a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b60006101408284031215614a49578081fd5b600060208284031215614feb578081fd5b5035919050565b600060208284031215615003578081fd5b5051919050565b6000806040838503121561501c578182fd5b505080516020909101519092909150565b6001600160a01b03169052565b15159052565b6000815180845261505881602086016020860161586e565b601f01601f19169290920160200192915050565b6005811061507657fe5b9052565b63ffffffff8151168252602081015115156020830152604081015160408301526060810151606083015260808101516150b6608084018261503a565b5060a08101516150c960a084018261502d565b5060c0818101519083015260e0808201519083015261010090810151910152565b80356150f58161589a565b6001600160a01b03168252602081013561510e8161589a565b61511b602084018261502d565b50604081013560408301526060810135606083015261513c608082016148f3565b615149608084018261503a565b5061515660a082016148dd565b61516360a084018261502d565b5060c0818101359083015260e0808201359083015261010090810135910152565b63ffffffff169052565b600082516151a081846020870161586e565b9190910192915050565b90565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0396871681529486166020860152604085019390935293166060830152608082019290925290151560a082015260c00190565b6001600160a01b0394851681529290931660208301526040820152901515606082015260800190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b039290921682521515602082015260400190565b6001600160a01b03841681526060810161529e602083018561506c565b8215156040830152949350505050565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b6000606082526152ed6060830186615040565b60208301949094525060400152919050565b602081016006831061530d57fe5b91905290565b60408101615321828561506c565b8260208301529392505050565b602080825260099082015268125117d313d0d2d15160ba1b604082015260600190565b6020808252601d908201527f49445f494e53554646494349454e545f4f55545055545f414d4f554e54000000604082015260600190565b602080825260159082015274125117d3d491115497d393d517d15610d151511151605a1b604082015260600190565b60208082526015908201527449445f494e56414c49445f4f524445525f5459504560581b604082015260600190565b6020808252600a9082015269125117d156141254915160b21b604082015260600190565b60208082526010908201526f125117d4915195539117d1905253115160821b604082015260600190565b6020808252600c908201526b24a22fa327a92124a22222a760a11b604082015260600190565b602080825260169082015275125117d25394d551919250d251539517d05353d5539560521b604082015260600190565b602080825260149082015273125117d1551217d4915195539117d1905253115160621b604082015260600190565b6020808252601c908201527f49445f494e53554646494349454e545f494e5055545f414d4f554e5400000000604082015260600190565b6101208101612a3a828461507a565b90815260200190565b9283526001600160a01b03919091166020830152604082015260600190565b838152610160810161553b60208301856150ea565b82610140830152949350505050565b8381526101e0810161556760208301615562866148dd565b61502d565b615573602085016148dd565b615580604084018261502d565b506040840135606083015260608401356080830152608084013560a083015260a084013560c083015260c084013560e083015261010060e0850135818401526155ca8186016148f3565b90506101206155db8185018361503a565b6155e68187016148dd565b9150506101406155f88185018361502d565b61016091508086013582850152506101808186013581850152808601356101a08501525050826101c0830152949350505050565b828152610160810161564460208301615562856148dd565b615650602084016148dd565b61565d604084018261502d565b506040830135606083015260608301356080830152608083013560a083015261568860a084016148f3565b61569560c084018261503a565b506156a260c084016148dd565b6156af60e084018261502d565b5061010060e08401358184015261012081850135818501528085013561014085015250509392505050565b918252602082015260400190565b91825263ffffffff16602082015260400190565b600061018082019050615710828451615184565b6020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015161575e60e084018261503a565b50610100808401516157728285018261502d565b50506101208381015190830152610140808401519083015261016092830151929091019190915290565b60006101208201905063ffffffff835116825260208301516020830152604083015160408301526060830151606083015260808301511515608083015260a08301516157eb60a084018261502d565b5060c083015160c083015260e083015160e083015261010080840151818401525092915050565b60008382526040602083015261456e6040830184615040565b92835260208301919091526001600160a01b0316604082015260600190565b60405181810167ffffffffffffffff8111828210171561586657fe5b604052919050565b60005b83811015615889578181015183820152602001615871565b8381111561170f5750506000910152565b6001600160a01b03811681146158af57600080fd5b50565b80151581146158af57600080fd5b600581106158af57600080fd5b63ffffffff811681146158af57600080fdfe786212e89f390a4c768f3b935b85e0ec2561a24b6f48cda3c4b48a996b211592a264697066735822122050c5be39f6e719197a07033de942e62df6a5b8c83eeee1e0ed943ade09a5d8b764736f6c63430007050033

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

000000000000000000000000673662e97b05e001816c380ba5a628d2e29f55d1000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000008971dc0105a22e54d81593af02167fc82af935e3

-----Decoded View---------------
Arg [0] : _factory (address): 0x673662e97B05e001816c380bA5a628D2E29f55D1
Arg [1] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [2] : _bot (address): 0x8971Dc0105a22e54D81593AF02167FC82AF935E3

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000673662e97b05e001816c380ba5a628d2e29f55d1
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [2] : 0000000000000000000000008971dc0105a22e54d81593af02167fc82af935e3


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
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.