ETH Price: $2,420.67 (+1.70%)

Contract

0x5321647F3c3769bc7bb9e10aB10d7F5C2E402c56
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer Ownersh...191262582024-01-31 11:47:11254 days ago1706701631IN
Swarm Markets: BPoolProxy
0 ETH0.0004531815.74708731
Smart Swap Exact...185930132023-11-17 17:13:35328 days ago1700241215IN
Swarm Markets: BPoolProxy
0 ETH0.0259769749.98639509
Smart Swap Exact...185923192023-11-17 14:53:59328 days ago1700232839IN
Swarm Markets: BPoolProxy
0 ETH0.0253175448.71748187
Smart Swap Exact...185886372023-11-17 2:31:23329 days ago1700188283IN
Swarm Markets: BPoolProxy
0 ETH0.0407872663.9204587
Smart Swap Exact...185868602023-11-16 20:32:59329 days ago1700166779IN
Swarm Markets: BPoolProxy
0 ETH0.0271051342.47828033
Smart Swap Exact...185866232023-11-16 19:45:11329 days ago1700163911IN
Swarm Markets: BPoolProxy
0 ETH0.0292745756.33181036
Smart Swap Exact...185845982023-11-16 12:58:47330 days ago1700139527IN
Swarm Markets: BPoolProxy
0 ETH0.0218357834.22032369
Smart Swap Exact...185828892023-11-16 7:15:23330 days ago1700118923IN
Swarm Markets: BPoolProxy
0 ETH0.0178220627.93015958
Smart Swap Exact...185809882023-11-16 0:52:47330 days ago1700095967IN
Swarm Markets: BPoolProxy
0 ETH0.0216812133.97808446
Smart Swap Exact...185750342023-11-15 4:52:59331 days ago1700023979IN
Swarm Markets: BPoolProxy
0 ETH0.0147948728.4691423
Smart Swap Exact...185742542023-11-15 2:15:47331 days ago1700014547IN
Swarm Markets: BPoolProxy
0 ETH0.019044129.84529247
Smart Swap Exact...185726882023-11-14 21:00:47331 days ago1699995647IN
Swarm Markets: BPoolProxy
0 ETH0.027458243.03159325
Smart Swap Exact...185722242023-11-14 19:26:47331 days ago1699990007IN
Swarm Markets: BPoolProxy
0 ETH0.0280231653.9237886
Smart Swap Exact...185712952023-11-14 16:19:59331 days ago1699978799IN
Swarm Markets: BPoolProxy
0 ETH0.0318878661.36045716
Smart Swap Exact...185700932023-11-14 12:18:35332 days ago1699964315IN
Swarm Markets: BPoolProxy
0 ETH0.0174044833.49071421
Smart Swap Exact...185611122023-11-13 6:10:35333 days ago1699855835IN
Swarm Markets: BPoolProxy
0 ETH0.0130392125.09079799
Smart Swap Exact...185573052023-11-12 17:25:35333 days ago1699809935IN
Swarm Markets: BPoolProxy
0 ETH0.0305947447.85371914
Smart Swap Exact...185568332023-11-12 15:50:11333 days ago1699804211IN
Swarm Markets: BPoolProxy
0 ETH0.0307556848.10543768
Smart Swap Exact...185564502023-11-12 14:33:11333 days ago1699799591IN
Swarm Markets: BPoolProxy
0 ETH0.0199632538.41444111
Smart Swap Exact...185552272023-11-12 10:26:11334 days ago1699784771IN
Swarm Markets: BPoolProxy
0 ETH0.0163860625.63017745
Smart Swap Exact...185543892023-11-12 7:37:23334 days ago1699774643IN
Swarm Markets: BPoolProxy
0 ETH0.0139354221.7970303
Smart Swap Exact...185541132023-11-12 6:41:23334 days ago1699771283IN
Swarm Markets: BPoolProxy
0 ETH0.0127236419.90125476
Smart Swap Exact...185516522023-11-11 22:24:59334 days ago1699741499IN
Swarm Markets: BPoolProxy
0 ETH0.0193198530.2190532
Smart Swap Exact...185514882023-11-11 21:52:11334 days ago1699739531IN
Swarm Markets: BPoolProxy
0 ETH0.021965934.4242401
Smart Swap Exact...185512302023-11-11 21:00:35334 days ago1699736435IN
Swarm Markets: BPoolProxy
0 ETH0.0216754533.96906362
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BPoolProxy

Compiler Version
v0.7.4+commit.3f05b770

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 17 : BPoolProxy.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ISwap.sol";
import "../interfaces/IXToken.sol";
import "../interfaces/IXTokenWrapper.sol";
import "../interfaces/IBPool.sol";
import "../interfaces/IBRegistry.sol";
import "../interfaces/IProtocolFee.sol";
import "../interfaces/IUTokenPriceFeed.sol";

/**
 * @title BPoolProxy
 * @author Protofire
 * @dev Forwarding proxy that allows users to batch execute swaps and join/exit pools.
 * User should interact with pools through this contracts as it is the one that charge
 * the protocol swap fee, and wrap/unwrap pool tokens into/from xPoolToken.
 *
 * This code is based on Balancer ExchangeProxy contract
 * https://docs.balancer.finance/smart-contracts/exchange-proxy
 * (https://etherscan.io/address/0x3E66B66Fd1d0b02fDa6C811Da9E0547970DB2f21#code)
 */
contract BPoolProxy is Ownable, ISwap, ERC1155Holder {
    using SafeMath for uint256;

    struct Pool {
        address pool;
        uint256 tokenBalanceIn;
        uint256 tokenWeightIn;
        uint256 tokenBalanceOut;
        uint256 tokenWeightOut;
        uint256 swapFee;
        uint256 effectiveLiquidity;
    }

    uint256 private constant BONE = 10**18;

    /// @dev Address of BRegistry
    IBRegistry public registry;
    /// @dev Address of ProtocolFee module
    IProtocolFee public protocolFee;
    /// @dev Address of XTokenWrapper
    IXTokenWrapper public xTokenWrapper;
    /// @dev Address of Utitlity Token Price Feed - Used as feature flag for discounted fee
    IUTokenPriceFeed public utilityTokenFeed;
    /// @dev Address who receives fees
    address public feeReceiver;
    /// @dev Address Utitlity Token - Used as feature flag for discounted fee
    address public utilityToken;

     /**
     * @dev Emitted when `joinPool` function is executed.
     */
    event JoinPool(address liquidityProvider, address bpool, uint256 shares);

    /**
     * @dev Emitted when `exitPool` function is executed.
     */
    event ExitPool(address iquidityProvider, address bpool, uint256 shares);

    /**
     * @dev Emitted when `registry` address is set.
     */
    event RegistrySet(address registry);

    /**
     * @dev Emitted when `protocolFee` address is set.
     */
    event ProtocolFeeSet(address protocolFee);

    /**
     * @dev Emitted when `feeReceiver` address is set.
     */
    event FeeReceiverSet(address feeReceiver);

    /**
     * @dev Emitted when `xTokenWrapper` address is set.
     */
    event XTokenWrapperSet(address xTokenWrapper);

    /**
     * @dev Emitted when `utilityToken` address is set.
     */
    event UtilityTokenSet(address utilityToken);

    /**
     * @dev Emitted when `utilityTokenFeed` address is set.
     */
    event UtilityTokenFeedSet(address utilityTokenFeed);

    /**
     * @dev Sets the values for {registry}, {protocolFee}, {feeReceiver},
     * {xTokenWrapper}, {utilityToken} and {utilityTokenFeed}.
     *
     * Sets ownership to the account that deploys the contract.
     *
     */
    constructor(
        address _registry,
        address _protocolFee,
        address _feeReceiver,
        address _xTokenWrapper,
        address _utilityToken,
        address _utilityTokenFeed
    ) {
        _setRegistry(_registry);
        _setProtocolFee(_protocolFee);
        _setFeeReceiver(_feeReceiver);
        _setXTokenWrapper(_xTokenWrapper);
        _setUtilityToken(_utilityToken);
        _setUtilityTokenFeed(_utilityTokenFeed);
    }

    /**
     * @dev Sets `_registry` as the new registry.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     * - `_registry` should not be the zero address.
     *
     * @param _registry The address of the registry.
     */
    function setRegistry(address _registry) external onlyOwner {
        _setRegistry(_registry);
    }

    /**
     * @dev Sets `_protocolFee` as the new protocolFee.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     * - `_protocolFee` should not be the zero address.
     *
     * @param _protocolFee The address of the protocolFee.
     */
    function setProtocolFee(address _protocolFee) external onlyOwner {
        _setProtocolFee(_protocolFee);
    }

    /**
     * @dev Sets `_feeReceiver` as the new feeReceiver.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     * - `_feeReceiver` should not be the zero address.
     *
     * @param _feeReceiver The address of the feeReceiver.
     */
    function setFeeReceiver(address _feeReceiver) external onlyOwner {
        _setFeeReceiver(_feeReceiver);
    }

    /**
     * @dev Sets `_xTokenWrapper` as the new xTokenWrapper.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     * - `_xTokenWrapper` should not be the zero address.
     *
     * @param _xTokenWrapper The address of the xTokenWrapper.
     */
    function setXTokenWrapper(address _xTokenWrapper) external onlyOwner {
        _setXTokenWrapper(_xTokenWrapper);
    }

    /**
     * @dev Sets `_utilityToken` as the new utilityToken.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     *
     * @param _utilityToken The address of the utilityToken.
     */
    function setUtilityToken(address _utilityToken) external onlyOwner {
        _setUtilityToken(_utilityToken);
    }

    /**
     * @dev Sets `_utilityTokenFeed` as the new utilityTokenFeed.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     *
     * @param _utilityTokenFeed The address of the utilityTokenFeed.
     */
    function setUtilityTokenFeed(address _utilityTokenFeed) external onlyOwner {
        _setUtilityTokenFeed(_utilityTokenFeed);
    }

    /**
     * @dev Sets `_registry` as the new registry.
     *
     * Requirements:
     *
     * - `_registry` should not be the zero address.
     *
     * @param _registry The address of the registry.
     */
    function _setRegistry(address _registry) internal {
        require(_registry != address(0), "registry is the zero address");
        emit RegistrySet(_registry);
        registry = IBRegistry(_registry);
    }

    /**
     * @dev Sets `_protocolFee` as the new protocolFee.
     *
     * Requirements:
     *
     * - `_protocolFee` should not be the zero address.
     *
     * @param _protocolFee The address of the protocolFee.
     */
    function _setProtocolFee(address _protocolFee) internal {
        require(_protocolFee != address(0), "protocolFee is the zero address");
        emit ProtocolFeeSet(_protocolFee);
        protocolFee = IProtocolFee(_protocolFee);
    }

    /**
     * @dev Sets `_feeReceiver` as the new feeReceiver.
     *
     * Requirements:
     *
     * - `_feeReceiver` should not be the zero address.
     *
     * @param _feeReceiver The address of the feeReceiver.
     */
    function _setFeeReceiver(address _feeReceiver) internal {
        require(_feeReceiver != address(0), "feeReceiver is the zero address");
        emit FeeReceiverSet(_feeReceiver);
        feeReceiver = _feeReceiver;
    }

    /**
     * @dev Sets `_xTokenWrapper` as the new xTokenWrapper.
     *
     * Requirements:
     *
     * - `_xTokenWrapper` should not be the zero address.
     *
     * @param _xTokenWrapper The address of the xTokenWrapper.
     */
    function _setXTokenWrapper(address _xTokenWrapper) internal {
        require(_xTokenWrapper != address(0), "xTokenWrapper is the zero address");
        emit XTokenWrapperSet(_xTokenWrapper);
        xTokenWrapper = IXTokenWrapper(_xTokenWrapper);
    }

    /**
     * @dev Sets `_utilityToken` as the new utilityToken.
     *
     * @param _utilityToken The address of the utilityToken.
     */
    function _setUtilityToken(address _utilityToken) internal {
        emit UtilityTokenSet(_utilityToken);
        utilityToken = _utilityToken;
    }

    /**
     * @dev Sets `_utilityTokenFeed` as the new utilityTokenFeed.
     *
     * Requirements:
     *
     * - the caller must be the owner.
     *
     * @param _utilityTokenFeed The address of the utilityTokenFeed.
     */
    function _setUtilityTokenFeed(address _utilityTokenFeed) internal {
        emit UtilityTokenFeedSet(_utilityTokenFeed);
        utilityTokenFeed = IUTokenPriceFeed(_utilityTokenFeed);
    }

    /**
     * @dev Execute single-hop swaps for swapExactIn trade type. Used for swaps
     * returned from viewSplit function and legacy off-chain SOR.
     *
     * @param swaps Array of single-hop swaps.
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param totalAmountIn Total amount of tokenIn.
     * @param minTotalAmountOut Minumum amount of tokenOut.
     * @param useUtilityToken Flag to determine if the protocol swap fee is paid using UtilityToken or TokenIn.
     */
    function batchSwapExactIn(
        Swap[] memory swaps,
        IXToken tokenIn,
        IXToken tokenOut,
        uint256 totalAmountIn,
        uint256 minTotalAmountOut,
        bool useUtilityToken
    ) public returns (uint256 totalAmountOut) {
        transferFrom(tokenIn, totalAmountIn);

        for (uint256 i = 0; i < swaps.length; i++) {
            Swap memory swap = swaps[i];
            IXToken swapTokenIn = IXToken(swap.tokenIn);
            IBPool pool = IBPool(swap.pool);

            if (swapTokenIn.allowance(address(this), swap.pool) > 0) {
                swapTokenIn.approve(swap.pool, 0);
            }
            swapTokenIn.approve(swap.pool, swap.swapAmount);

            (uint256 tokenAmountOut, ) =
                pool.swapExactAmountIn(
                    swap.tokenIn,
                    swap.swapAmount,
                    swap.tokenOut,
                    swap.limitReturnAmount,
                    swap.maxPrice
                );
            totalAmountOut = tokenAmountOut.add(totalAmountOut);
        }

        require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT");

        transferFeeFrom(tokenIn, protocolFee.batchFee(swaps, totalAmountIn), useUtilityToken);

        transfer(tokenOut, totalAmountOut);
        transfer(tokenIn, getBalance(tokenIn));
    }

    /**
     * @dev Execute single-hop swaps for swapExactOut trade type. Used for swaps
     * returned from viewSplit function and legacy off-chain SOR.
     *
     * @param swaps Array of single-hop swaps.
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param maxTotalAmountIn Maximum total amount of tokenIn.
     * @param useUtilityToken Flag to determine if the protocol swap fee is paid using UtilityToken or TokenIn.
     */
    function batchSwapExactOut(
        Swap[] memory swaps,
        IXToken tokenIn,
        IXToken tokenOut,
        uint256 maxTotalAmountIn,
        bool useUtilityToken
    ) public returns (uint256 totalAmountIn) {
        transferFrom(tokenIn, maxTotalAmountIn);

        for (uint256 i = 0; i < swaps.length; i++) {
            Swap memory swap = swaps[i];
            IXToken swapTokenIn = IXToken(swap.tokenIn);
            IBPool pool = IBPool(swap.pool);

            if (swapTokenIn.allowance(address(this), swap.pool) > 0) {
                swapTokenIn.approve(swap.pool, 0);
            }
            swapTokenIn.approve(swap.pool, swap.limitReturnAmount);

            (uint256 tokenAmountIn, ) =
                pool.swapExactAmountOut(
                    swap.tokenIn,
                    swap.limitReturnAmount,
                    swap.tokenOut,
                    swap.swapAmount,
                    swap.maxPrice
                );
            totalAmountIn = tokenAmountIn.add(totalAmountIn);
        }
        require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN");

        transferFeeFrom(tokenIn, protocolFee.batchFee(swaps, totalAmountIn), useUtilityToken);

        transfer(tokenOut, getBalance(tokenOut));
        transfer(tokenIn, getBalance(tokenIn));
    }

    /**
     * @dev Execute multi-hop swaps returned from off-chain SOR for swapExactIn trade type.
     *
     * @param swapSequences multi-hop swaps sequence.
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param totalAmountIn Total amount of tokenIn.
     * @param minTotalAmountOut Minumum amount of tokenOut.
     * @param useUtilityToken Flag to determine if the protocol swap fee is paid using UtilityToken or TokenIn.
     */
    function multihopBatchSwapExactIn(
        Swap[][] memory swapSequences,
        IXToken tokenIn,
        IXToken tokenOut,
        uint256 totalAmountIn,
        uint256 minTotalAmountOut,
        bool useUtilityToken
    ) external returns (uint256 totalAmountOut) {
        transferFrom(tokenIn, totalAmountIn);

        for (uint256 i = 0; i < swapSequences.length; i++) {
            uint256 tokenAmountOut;
            for (uint256 k = 0; k < swapSequences[i].length; k++) {
                Swap memory swap = swapSequences[i][k];
                IXToken swapTokenIn = IXToken(swap.tokenIn);
                if (k == 1) {
                    // Makes sure that on the second swap the output of the first was used
                    // so there is not intermediate token leftover
                    swap.swapAmount = tokenAmountOut;
                }

                IBPool pool = IBPool(swap.pool);
                if (swapTokenIn.allowance(address(this), swap.pool) > 0) {
                    swapTokenIn.approve(swap.pool, 0);
                }
                swapTokenIn.approve(swap.pool, swap.swapAmount);
                (tokenAmountOut, ) = pool.swapExactAmountIn(
                    swap.tokenIn,
                    swap.swapAmount,
                    swap.tokenOut,
                    swap.limitReturnAmount,
                    swap.maxPrice
                );
            }
            // This takes the amountOut of the last swap
            totalAmountOut = tokenAmountOut.add(totalAmountOut);
        }

        require(totalAmountOut >= minTotalAmountOut, "ERR_LIMIT_OUT");

        transferFeeFrom(tokenIn, protocolFee.multihopBatch(swapSequences, totalAmountIn), useUtilityToken);

        transfer(tokenOut, totalAmountOut);
        transfer(tokenIn, getBalance(tokenIn));
    }

    /**
     * @dev Execute multi-hop swaps returned from off-chain SOR for swapExactOut trade type.
     *
     * @param swapSequences multi-hop swaps sequence.
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param maxTotalAmountIn Maximum total amount of tokenIn.
     * @param useUtilityToken Flag to determine if the protocol swap fee is paid using UtilityToken or TokenIn.
     */
    function multihopBatchSwapExactOut(
        Swap[][] memory swapSequences,
        IXToken tokenIn,
        IXToken tokenOut,
        uint256 maxTotalAmountIn,
        bool useUtilityToken
    ) external returns (uint256 totalAmountIn) {
        transferFrom(tokenIn, maxTotalAmountIn);

        for (uint256 i = 0; i < swapSequences.length; i++) {
            uint256 tokenAmountInFirstSwap;
            // Specific code for a simple swap and a multihop (2 swaps in sequence)
            if (swapSequences[i].length == 1) {
                Swap memory swap = swapSequences[i][0];
                IXToken swapTokenIn = IXToken(swap.tokenIn);

                IBPool pool = IBPool(swap.pool);
                if (swapTokenIn.allowance(address(this), swap.pool) > 0) {
                    swapTokenIn.approve(swap.pool, 0);
                }
                swapTokenIn.approve(swap.pool, swap.limitReturnAmount);

                (tokenAmountInFirstSwap, ) = pool.swapExactAmountOut(
                    swap.tokenIn,
                    swap.limitReturnAmount,
                    swap.tokenOut,
                    swap.swapAmount,
                    swap.maxPrice
                );
            } else {
                // Consider we are swapping A -> B and B -> C. The goal is to buy a given amount
                // of token C. But first we need to buy B with A so we can then buy C with B
                // To get the exact amount of C we then first need to calculate how much B we'll need:
                uint256 intermediateTokenAmount; // This would be token B as described above
                Swap memory secondSwap = swapSequences[i][1];
                IBPool poolSecondSwap = IBPool(secondSwap.pool);
                intermediateTokenAmount = poolSecondSwap.calcInGivenOut(
                    poolSecondSwap.getBalance(secondSwap.tokenIn),
                    poolSecondSwap.getDenormalizedWeight(secondSwap.tokenIn),
                    poolSecondSwap.getBalance(secondSwap.tokenOut),
                    poolSecondSwap.getDenormalizedWeight(secondSwap.tokenOut),
                    secondSwap.swapAmount,
                    poolSecondSwap.getSwapFee()
                );

                //// Buy intermediateTokenAmount of token B with A in the first pool
                Swap memory firstSwap = swapSequences[i][0];
                IXToken firstswapTokenIn = IXToken(firstSwap.tokenIn);
                IBPool poolFirstSwap = IBPool(firstSwap.pool);
                if (firstswapTokenIn.allowance(address(this), firstSwap.pool) < uint256(-1)) {
                    firstswapTokenIn.approve(firstSwap.pool, uint256(-1));
                }

                (tokenAmountInFirstSwap, ) = poolFirstSwap.swapExactAmountOut(
                    firstSwap.tokenIn,
                    firstSwap.limitReturnAmount,
                    firstSwap.tokenOut,
                    intermediateTokenAmount, // This is the amount of token B we need
                    firstSwap.maxPrice
                );

                //// Buy the final amount of token C desired
                IXToken secondswapTokenIn = IXToken(secondSwap.tokenIn);
                if (secondswapTokenIn.allowance(address(this), secondSwap.pool) < uint256(-1)) {
                    secondswapTokenIn.approve(secondSwap.pool, uint256(-1));
                }

                poolSecondSwap.swapExactAmountOut(
                    secondSwap.tokenIn,
                    secondSwap.limitReturnAmount,
                    secondSwap.tokenOut,
                    secondSwap.swapAmount,
                    secondSwap.maxPrice
                );
            }
            totalAmountIn = tokenAmountInFirstSwap.add(totalAmountIn);
        }

        require(totalAmountIn <= maxTotalAmountIn, "ERR_LIMIT_IN");

        transferFeeFrom(tokenIn, protocolFee.multihopBatch(swapSequences, totalAmountIn), useUtilityToken);

        transfer(tokenOut, getBalance(tokenOut));
        transfer(tokenIn, getBalance(tokenIn));
    }

    /**
     * @dev Used for swaps returned from viewSplit function.
     *
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param totalAmountIn Total amount of tokenIn.
     * @param minTotalAmountOut Minumum amount of tokenOut.
     * @param nPools Maximum mumber of pools.
     * @param useUtilityToken Flag to determine if the protocol swap fee is paid using UtilityToken or TokenIn.
     */
    function smartSwapExactIn(
        IXToken tokenIn,
        IXToken tokenOut,
        uint256 totalAmountIn,
        uint256 minTotalAmountOut,
        uint256 nPools,
        bool useUtilityToken
    ) external returns (uint256 totalAmountOut) {
        Swap[] memory swaps;
        uint256 totalOutput;
        (swaps, totalOutput) = viewSplitExactIn(address(tokenIn), address(tokenOut), totalAmountIn, nPools);

        require(totalOutput >= minTotalAmountOut, "ERR_LIMIT_OUT");

        totalAmountOut = batchSwapExactIn(swaps, tokenIn, tokenOut, totalAmountIn, minTotalAmountOut, useUtilityToken);
    }

    /**
     * @dev Used for swaps returned from viewSplit function.
     *
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param maxTotalAmountIn Maximum total amount of tokenIn.
     * @param nPools Maximum mumber of pools.
     * @param useUtilityToken Flag to determine if the protocol swap fee is paid using UtilityToken or TokenIn.
     */
    function smartSwapExactOut(
        IXToken tokenIn,
        IXToken tokenOut,
        uint256 totalAmountOut,
        uint256 maxTotalAmountIn,
        uint256 nPools,
        bool useUtilityToken
    ) external returns (uint256 totalAmountIn) {
        Swap[] memory swaps;
        uint256 totalInput;
        (swaps, totalInput) = viewSplitExactOut(address(tokenIn), address(tokenOut), totalAmountOut, nPools);

        require(totalInput <= maxTotalAmountIn, "ERR_LIMIT_IN");

        totalAmountIn = batchSwapExactOut(swaps, tokenIn, tokenOut, maxTotalAmountIn, useUtilityToken);
    }

    /**
     * @dev Join the `pool`, getting `poolAmountOut` pool tokens. This will pull some of each of the currently
     * trading tokens in the pool, meaning you must have called approve for each token for this pool. These
     * values are limited by the array of `maxAmountsIn` in the order of the pool tokens.
     *
     * @param pool Pool address.
     * @param poolAmountOut Exact pool amount out.
     * @param maxAmountsIn Maximum amounts in.
     */
    function joinPool(
        address pool,
        uint256 poolAmountOut,
        uint256[] calldata maxAmountsIn
    ) external {
        address[] memory tokens = IBPool(pool).getCurrentTokens();

        // pull xTokens
        for (uint256 i = 0; i < tokens.length; i++) {
            transferFrom(IXToken(tokens[i]), maxAmountsIn[i]);
            IXToken(tokens[i]).approve(pool, maxAmountsIn[i]);
        }

        IBPool(pool).joinPool(poolAmountOut, maxAmountsIn);

        // push remaining xTokens
        for (uint256 i = 0; i < tokens.length; i++) {
            transfer(IXToken(tokens[i]), getBalance(IXToken(tokens[i])));
        }

        // Wrap balancer liquidity tokens into its representing xToken
        IBPool(pool).approve(address(xTokenWrapper), poolAmountOut);
        require(xTokenWrapper.wrap(pool, poolAmountOut), "ERR_WRAP_POOL");

        transfer(IXToken(xTokenWrapper.tokenToXToken(pool)), poolAmountOut);

        emit JoinPool(msg.sender, pool,  poolAmountOut);
    }

    /**
     * @dev Exit the pool, paying poolAmountIn pool tokens and getting some of each of the currently trading
     * tokens in return. These values are limited by the array of minAmountsOut in the order of the pool tokens.
     *
     * @param pool Pool address.
     * @param poolAmountIn Exact pool amount int.
     * @param minAmountsOut Minumum amounts out.
     */
    function exitPool(
        address pool,
        uint256 poolAmountIn,
        uint256[] calldata minAmountsOut
    ) external {
        address wrappedLPT = xTokenWrapper.tokenToXToken(pool);

        // pull wrapped liquitity tokens
        transferFrom(IXToken(wrappedLPT), poolAmountIn);

        // unwrap wrapped liquitity tokens
        require(xTokenWrapper.unwrap(wrappedLPT, poolAmountIn), "ERR_UNWRAP_POOL");

        // LPT do not need to be approved when exit
        IBPool(pool).exitPool(poolAmountIn, minAmountsOut);

        // push xTokens
        address[] memory tokens = IBPool(pool).getCurrentTokens();
        for (uint256 i = 0; i < tokens.length; i++) {
            transfer(IXToken(tokens[i]), getBalance(IXToken(tokens[i])));
        }

        emit ExitPool(msg.sender, pool, poolAmountIn); 
    }

    /**
     * @dev Pay `tokenAmountIn` of token `tokenIn` to join the pool, getting `poolAmountOut` of the pool shares.
     *
     * @param pool Pool address.
     * @param tokenIn Input token.
     * @param tokenAmountIn Exact amount of tokenIn to pay.
     * @param minPoolAmountOut Minumum amount of pool shares to get.
     */
    function joinswapExternAmountIn(
        address pool,
        address tokenIn,
        uint256 tokenAmountIn,
        uint256 minPoolAmountOut
    ) external returns (uint256 poolAmountOut) {
        // pull xToken
        transferFrom(IXToken(tokenIn), tokenAmountIn);
        IXToken(tokenIn).approve(pool, tokenAmountIn);

        poolAmountOut = IBPool(pool).joinswapExternAmountIn(tokenIn, tokenAmountIn, minPoolAmountOut);

        // Wrap balancer liquidity tokens into its representing xToken
        IBPool(pool).approve(address(xTokenWrapper), poolAmountOut);
        require(xTokenWrapper.wrap(pool, poolAmountOut), "ERR_WRAP_POOL");

        transfer(IXToken(xTokenWrapper.tokenToXToken(pool)), poolAmountOut);

        emit JoinPool(msg.sender, pool,  poolAmountOut);
    }

    /**
     * @dev Specify `poolAmountOut` pool shares that you want to get, and a token `tokenIn` to pay with.
     * This costs `tokenAmountIn` tokens (these went into the pool).
     *
     * @param pool Pool address.
     * @param tokenIn Input token.
     * @param poolAmountOut Exact amount of pool shares to get.
     * @param maxAmountIn Minumum amount of tokenIn to pay.
     */
    function joinswapPoolAmountOut(
        address pool,
        address tokenIn,
        uint256 poolAmountOut,
        uint256 maxAmountIn
    ) external returns (uint256 tokenAmountIn) {
        // pull xToken
        transferFrom(IXToken(tokenIn), maxAmountIn);
        IXToken(tokenIn).approve(pool, maxAmountIn);

        tokenAmountIn = IBPool(pool).joinswapPoolAmountOut(tokenIn, poolAmountOut, maxAmountIn);

        // push remaining xTokens
        transfer(IXToken(tokenIn), getBalance(IXToken(tokenIn)));

        // Wrap balancer liquidity tokens into its representing xToken
        IBPool(pool).approve(address(xTokenWrapper), poolAmountOut);
        require(xTokenWrapper.wrap(pool, poolAmountOut), "ERR_WRAP_POOL");

        transfer(IXToken(xTokenWrapper.tokenToXToken(pool)), poolAmountOut);

        emit JoinPool(msg.sender, pool,  poolAmountOut);
    }

    /**
     * @dev Pay `poolAmountIn` pool shares into the pool, getting `tokenAmountOut` of the given
     * token `tokenOut` out of the pool.
     *
     * @param pool Pool address.
     * @param tokenOut Input token.
     * @param poolAmountIn Exact amount of pool shares to pay.
     * @param minAmountOut Minumum amount of tokenIn to get.
     */
    function exitswapPoolAmountIn(
        address pool,
        address tokenOut,
        uint256 poolAmountIn,
        uint256 minAmountOut
    ) external returns (uint256 tokenAmountOut) {
        address wrappedLPT = xTokenWrapper.tokenToXToken(pool);

        // pull wrapped liquitity tokens
        transferFrom(IXToken(wrappedLPT), poolAmountIn);

        // unwrap wrapped liquitity tokens
        require(xTokenWrapper.unwrap(wrappedLPT, poolAmountIn), "ERR_UNWRAP_POOL");

        // LPT do not need to be approved when exit
        tokenAmountOut = IBPool(pool).exitswapPoolAmountIn(tokenOut, poolAmountIn, minAmountOut);

        // push xToken
        transfer(IXToken(tokenOut), tokenAmountOut);

        emit ExitPool(msg.sender, pool, poolAmountIn);
    }

    /**
     * @dev Specify tokenAmountOut of token tokenOut that you want to get out of the pool.
     * This costs poolAmountIn pool shares (these went into the pool).
     *
     * @param pool Pool address.
     * @param tokenOut Input token.
     * @param tokenAmountOut Exact amount of of tokenIn to get.
     * @param maxPoolAmountIn Maximum amount of pool shares to pay.
     */
    function exitswapExternAmountOut(
        address pool,
        address tokenOut,
        uint256 tokenAmountOut,
        uint256 maxPoolAmountIn
    ) external returns (uint256 poolAmountIn) {
        address wrappedLPT = xTokenWrapper.tokenToXToken(pool);

        // pull wrapped liquitity tokens
        transferFrom(IXToken(wrappedLPT), maxPoolAmountIn);

        // unwrap wrapped liquitity tokens
        require(xTokenWrapper.unwrap(wrappedLPT, maxPoolAmountIn), "ERR_UNWRAP_POOL");

        // LPT do not need to be approved when exit
        poolAmountIn = IBPool(pool).exitswapExternAmountOut(tokenOut, tokenAmountOut, maxPoolAmountIn);

        // push xToken
        transfer(IXToken(tokenOut), tokenAmountOut);

        uint256 remainingLPT = maxPoolAmountIn.sub(poolAmountIn);
        if (remainingLPT > 0) {
            // Wrap remaining balancer liquidity tokens into its representing xToken
            IBPool(pool).approve(address(xTokenWrapper), remainingLPT);
            require(xTokenWrapper.wrap(pool, remainingLPT), "ERR_WRAP_POOL");

            transfer(IXToken(wrappedLPT), remainingLPT);
        }

        emit ExitPool(msg.sender, pool, poolAmountIn);
    }

    /**
     * @dev View function that calculates most optimal swaps (exactIn swap type) across a max of nPools.
     * Returns an array of `Swaps` and the total amount out for swap.
     *
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param swapAmount Amount of tokenIn.
     * @param nPools Maximum mumber of pools.
     */
    function viewSplitExactIn(
        address tokenIn,
        address tokenOut,
        uint256 swapAmount,
        uint256 nPools
    ) public view returns (Swap[] memory swaps, uint256 totalOutput) {
        address[] memory poolAddresses = registry.getBestPoolsWithLimit(tokenIn, tokenOut, nPools);

        Pool[] memory pools = new Pool[](poolAddresses.length);
        uint256 sumEffectiveLiquidity;
        for (uint256 i = 0; i < poolAddresses.length; i++) {
            pools[i] = getPoolData(tokenIn, tokenOut, poolAddresses[i]);
            sumEffectiveLiquidity = sumEffectiveLiquidity.add(pools[i].effectiveLiquidity);
        }

        uint256[] memory bestInputAmounts = new uint256[](pools.length);
        uint256 totalInputAmount;
        for (uint256 i = 0; i < pools.length; i++) {
            bestInputAmounts[i] = swapAmount.mul(pools[i].effectiveLiquidity).div(sumEffectiveLiquidity);
            totalInputAmount = totalInputAmount.add(bestInputAmounts[i]);
        }

        if (totalInputAmount < swapAmount) {
            bestInputAmounts[0] = bestInputAmounts[0].add(swapAmount.sub(totalInputAmount));
        } else {
            bestInputAmounts[0] = bestInputAmounts[0].sub(totalInputAmount.sub(swapAmount));
        }

        swaps = new Swap[](pools.length);

        for (uint256 i = 0; i < pools.length; i++) {
            swaps[i] = Swap({
                pool: pools[i].pool,
                tokenIn: tokenIn,
                tokenOut: tokenOut,
                swapAmount: bestInputAmounts[i],
                limitReturnAmount: 0,
                maxPrice: uint256(-1)
            });
        }

        totalOutput = calcTotalOutExactIn(bestInputAmounts, pools);

        return (swaps, totalOutput);
    }

    /**
     * @dev View function that calculates most optimal swaps (exactOut swap type) across a max of nPools.
     * Returns an array of Swaps and the total amount in for swap.
     *
     * @param tokenIn Input token.
     * @param tokenOut Output token.
     * @param swapAmount Amount of tokenIn.
     * @param nPools Maximum mumber of pools.
     */
    function viewSplitExactOut(
        address tokenIn,
        address tokenOut,
        uint256 swapAmount,
        uint256 nPools
    ) public view returns (Swap[] memory swaps, uint256 totalInput) {
        address[] memory poolAddresses = registry.getBestPoolsWithLimit(tokenIn, tokenOut, nPools);

        Pool[] memory pools = new Pool[](poolAddresses.length);
        uint256 sumEffectiveLiquidity;
        for (uint256 i = 0; i < poolAddresses.length; i++) {
            pools[i] = getPoolData(tokenIn, tokenOut, poolAddresses[i]);
            sumEffectiveLiquidity = sumEffectiveLiquidity.add(pools[i].effectiveLiquidity);
        }

        uint256[] memory bestInputAmounts = new uint256[](pools.length);
        uint256 totalInputAmount;
        for (uint256 i = 0; i < pools.length; i++) {
            bestInputAmounts[i] = swapAmount.mul(pools[i].effectiveLiquidity).div(sumEffectiveLiquidity);
            totalInputAmount = totalInputAmount.add(bestInputAmounts[i]);
        }

        if (totalInputAmount < swapAmount) {
            bestInputAmounts[0] = bestInputAmounts[0].add(swapAmount.sub(totalInputAmount));
        } else {
            bestInputAmounts[0] = bestInputAmounts[0].sub(totalInputAmount.sub(swapAmount));
        }

        swaps = new Swap[](pools.length);

        for (uint256 i = 0; i < pools.length; i++) {
            swaps[i] = Swap({
                pool: pools[i].pool,
                tokenIn: tokenIn,
                tokenOut: tokenOut,
                swapAmount: bestInputAmounts[i],
                limitReturnAmount: uint256(-1),
                maxPrice: uint256(-1)
            });
        }

        totalInput = calcTotalOutExactOut(bestInputAmounts, pools);

        return (swaps, totalInput);
    }

    function getPoolData(
        address tokenIn,
        address tokenOut,
        address poolAddress
    ) internal view returns (Pool memory) {
        IBPool pool = IBPool(poolAddress);
        uint256 tokenBalanceIn = pool.getBalance(tokenIn);
        uint256 tokenBalanceOut = pool.getBalance(tokenOut);
        uint256 tokenWeightIn = pool.getDenormalizedWeight(tokenIn);
        uint256 tokenWeightOut = pool.getDenormalizedWeight(tokenOut);
        uint256 swapFee = pool.getSwapFee();

        uint256 effectiveLiquidity = calcEffectiveLiquidity(tokenWeightIn, tokenBalanceOut, tokenWeightOut);
        Pool memory returnPool =
            Pool({
                pool: poolAddress,
                tokenBalanceIn: tokenBalanceIn,
                tokenWeightIn: tokenWeightIn,
                tokenBalanceOut: tokenBalanceOut,
                tokenWeightOut: tokenWeightOut,
                swapFee: swapFee,
                effectiveLiquidity: effectiveLiquidity
            });

        return returnPool;
    }

    function calcEffectiveLiquidity(
        uint256 tokenWeightIn,
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut
    ) internal pure returns (uint256 effectiveLiquidity) {
        // Bo * wi/(wi+wo)
        effectiveLiquidity = tokenWeightIn.mul(BONE).div(tokenWeightOut.add(tokenWeightIn)).mul(tokenBalanceOut).div(
            BONE
        );

        return effectiveLiquidity;
    }

    function calcTotalOutExactIn(uint256[] memory bestInputAmounts, Pool[] memory bestPools)
        internal
        pure
        returns (uint256 totalOutput)
    {
        totalOutput = 0;
        for (uint256 i = 0; i < bestInputAmounts.length; i++) {
            uint256 output =
                IBPool(bestPools[i].pool).calcOutGivenIn(
                    bestPools[i].tokenBalanceIn,
                    bestPools[i].tokenWeightIn,
                    bestPools[i].tokenBalanceOut,
                    bestPools[i].tokenWeightOut,
                    bestInputAmounts[i],
                    bestPools[i].swapFee
                );

            totalOutput = totalOutput.add(output);
        }
        return totalOutput;
    }

    function calcTotalOutExactOut(uint256[] memory bestInputAmounts, Pool[] memory bestPools)
        internal
        pure
        returns (uint256 totalOutput)
    {
        totalOutput = 0;
        for (uint256 i = 0; i < bestInputAmounts.length; i++) {
            uint256 output =
                IBPool(bestPools[i].pool).calcInGivenOut(
                    bestPools[i].tokenBalanceIn,
                    bestPools[i].tokenWeightIn,
                    bestPools[i].tokenBalanceOut,
                    bestPools[i].tokenWeightOut,
                    bestInputAmounts[i],
                    bestPools[i].swapFee
                );

            totalOutput = totalOutput.add(output);
        }
        return totalOutput;
    }

    /**
     * @dev Trtansfers `token` from the sender to this conteract.
     *
     */
    function transferFrom(IXToken token, uint256 amount) internal {
        require(token.transferFrom(msg.sender, address(this), amount), "ERR_TRANSFER_FAILED");
    }

    /**
     * @dev Trtansfers protocol swap fee from the sender to this `feeReceiver`.
     *
     */
    function transferFeeFrom(
        IXToken token,
        uint256 amount,
        bool useUtitlityToken
    ) internal {
        if (useUtitlityToken && utilityToken != address(0) && address(utilityTokenFeed) != address(0)) {
            uint256 discountedFee = utilityTokenFeed.calculateAmount(address(token), amount.div(2));

            if (discountedFee > 0) {
                require(
                    IERC20(utilityToken).transferFrom(msg.sender, feeReceiver, discountedFee),
                    "ERR_FEE_UTILITY_TRANSFER_FAILED"
                );
            } else {
                require(token.transferFrom(msg.sender, feeReceiver, amount), "ERR_FEE_TRANSFER_FAILED");
            }
        } else {
            require(token.transferFrom(msg.sender, feeReceiver, amount), "ERR_FEE_TRANSFER_FAILED");
        }
    }

    function getBalance(IXToken token) internal view returns (uint256) {
        return token.balanceOf(address(this));
    }

    function transfer(IXToken token, uint256 amount) internal {
        require(token.transfer(msg.sender, amount), "ERR_TRANSFER_FAILED");
    }
}

File 2 of 17 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

File 3 of 17 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

File 4 of 17 : ERC1155Holder.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./ERC1155Receiver.sol";

/**
 * @dev _Available since v3.1._
 */
contract ERC1155Holder is ERC1155Receiver {
    function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}

File 5 of 17 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 6 of 17 : ISwap.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;

interface ISwap {
    struct Swap {
        address pool;
        address tokenIn;
        address tokenOut;
        uint256 swapAmount; // tokenInAmount / tokenOutAmount
        uint256 limitReturnAmount; // minAmountOut / maxAmountIn
        uint256 maxPrice;
    }
}

File 7 of 17 : IXToken.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;

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

/**
 * @title IXToken
 * @author Protofire
 * @dev XToken Interface.
 *
 */
interface IXToken is IERC20 {
    /**
     * @dev Triggers stopped state.
     *
     */
    function pause() external;

    /**
     * @dev Returns to normal state.
     */
    function unpause() external;

    /**
     * @dev Sets authorization.
     *
     */
    function setAuthorization(address authorization_) external;

    /**
     * @dev Sets operationsRegistry.
     *
     */
    function setOperationsRegistry(address operationsRegistry_) external;

    /**
     * @dev Sets kya.
     *
     */
    function setKya(string memory kya_) external;

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     */
    function mint(address account, uint256 amount) external;

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     */
    function burnFrom(address account, uint256 amount) external;
}

File 8 of 17 : IXTokenWrapper.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;

import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";

/**
 * @title IXTokenWrapper
 * @author Protofire
 * @dev XTokenWrapper Interface.
 *
 */
interface IXTokenWrapper is IERC1155Receiver {
    /**
     * @dev Token to xToken registry.
     */
    function tokenToXToken(address _token) external view returns (address);

    /**
     * @dev xToken to Token registry.
     */
    function xTokenToToken(address _xToken) external view returns (address);

    /**
     * @dev Wraps `_token` into its associated xToken.
     *
     */
    function wrap(address _token, uint256 _amount) external payable returns (bool);

    /**
     * @dev Unwraps `_xToken`.
     *
     */
    function unwrap(address _xToken, uint256 _amount) external returns (bool);
}

File 9 of 17 : IBPool.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;

/**
 * @title IBPool
 * @author Protofire
 * @dev Balancer BPool contract interface.
 *
 */
interface IBPool {
    function isPublicSwap() external view returns (bool);

    function isFinalized() external view returns (bool);

    function isBound(address t) external view returns (bool);

    function getNumTokens() external view returns (uint256);

    function getCurrentTokens() external view returns (address[] memory tokens);

    function getFinalTokens() external view returns (address[] memory tokens);

    function getDenormalizedWeight(address token) external view returns (uint256);

    function getTotalDenormalizedWeight() external view returns (uint256);

    function getNormalizedWeight(address token) external view returns (uint256);

    function getBalance(address token) external view returns (uint256);

    function getSwapFee() external view returns (uint256);

    function getController() external view returns (address);

    function setSwapFee(uint256 swapFee) external;

    function setController(address manager) external;

    function setPublicSwap(bool public_) external;

    function finalize() external;

    function bind(
        address token,
        uint256 balance,
        uint256 denorm
    ) external;

    function rebind(
        address token,
        uint256 balance,
        uint256 denorm
    ) external;

    function unbind(address token) external;

    function gulp(address token) external;

    function getSpotPrice(address tokenIn, address tokenOut) external view returns (uint256 spotPrice);

    function getSpotPriceSansFee(address tokenIn, address tokenOut) external view returns (uint256 spotPrice);

    function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn) external;

    function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external;

    function swapExactAmountIn(
        address tokenIn,
        uint256 tokenAmountIn,
        address tokenOut,
        uint256 minAmountOut,
        uint256 maxPrice
    ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter);

    function swapExactAmountOut(
        address tokenIn,
        uint256 maxAmountIn,
        address tokenOut,
        uint256 tokenAmountOut,
        uint256 maxPrice
    ) external returns (uint256 tokenAmountIn, uint256 spotPriceAfter);

    function joinswapExternAmountIn(
        address tokenIn,
        uint256 tokenAmountIn,
        uint256 minPoolAmountOut
    ) external returns (uint256 poolAmountOut);

    function joinswapPoolAmountOut(
        address tokenIn,
        uint256 poolAmountOut,
        uint256 maxAmountIn
    ) external returns (uint256 tokenAmountIn);

    function exitswapPoolAmountIn(
        address tokenOut,
        uint256 poolAmountIn,
        uint256 minAmountOut
    ) external returns (uint256 tokenAmountOut);

    function exitswapExternAmountOut(
        address tokenOut,
        uint256 tokenAmountOut,
        uint256 maxPoolAmountIn
    ) external returns (uint256 poolAmountIn);

    function totalSupply() external view returns (uint256);

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

    function allowance(address src, address dst) external view returns (uint256);

    function approve(address dst, uint256 amt) external returns (bool);

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

    function transferFrom(
        address src,
        address dst,
        uint256 amt
    ) external returns (bool);

    function calcSpotPrice(
        uint256 tokenBalanceIn,
        uint256 tokenWeightIn,
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 swapFee
    ) external pure returns (uint256 spotPrice);

    function calcOutGivenIn(
        uint256 tokenBalanceIn,
        uint256 tokenWeightIn,
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 tokenAmountIn,
        uint256 swapFee
    ) external pure returns (uint256 tokenAmountOut);

    function calcInGivenOut(
        uint256 tokenBalanceIn,
        uint256 tokenWeightIn,
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 tokenAmountOut,
        uint256 swapFee
    ) external pure returns (uint256 tokenAmountIn);

    function calcPoolOutGivenSingleIn(
        uint256 tokenBalanceIn,
        uint256 tokenWeightIn,
        uint256 poolSupply,
        uint256 totalWeight,
        uint256 tokenAmountIn,
        uint256 swapFee
    ) external pure returns (uint256 poolAmountOut);

    function calcSingleInGivenPoolOut(
        uint256 tokenBalanceIn,
        uint256 tokenWeightIn,
        uint256 poolSupply,
        uint256 totalWeight,
        uint256 poolAmountOut,
        uint256 swapFee
    ) external pure returns (uint256 tokenAmountIn);

    function calcSingleOutGivenPoolIn(
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 poolSupply,
        uint256 totalWeight,
        uint256 poolAmountIn,
        uint256 swapFee
    ) external pure returns (uint256 tokenAmountOut);

    function calcPoolInGivenSingleOut(
        uint256 tokenBalanceOut,
        uint256 tokenWeightOut,
        uint256 poolSupply,
        uint256 totalWeight,
        uint256 tokenAmountOut,
        uint256 swapFee
    ) external pure returns (uint256 poolAmountIn);
}

File 10 of 17 : IBRegistry.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;

/**
 * @title IBRegistry
 * @author Protofire
 * @dev Balancer BRegistry contract interface.
 *
 */

interface IBRegistry {
    function getBestPoolsWithLimit(
        address,
        address,
        uint256
    ) external view returns (address[] memory);
}

File 11 of 17 : IProtocolFee.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "../balancer/ISwap.sol";

/**
 * @title IProtocolFee
 * @author Protofire
 * @dev ProtocolFee interface.
 *
 */
interface IProtocolFee is ISwap {
    function batchFee(Swap[] memory swaps, uint256 amountIn) external view returns (uint256);

    function multihopBatch(Swap[][] memory swapSequences, uint256 amountIn) external view returns (uint256);
}

File 12 of 17 : IUTokenPriceFeed.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.7.0;

/**
 * @title IUtilTokenPriceFeed
 * @author Protofire
 * @dev Interface to be implemented by any UtilityToken price feed logic contract used in the protocol.
 *
 */
interface IUTokenPriceFeed {
    /**
     * @dev Gets the price a `_asset` in UtilityToken.
     *
     * @param _asset address of asset to get the price.
     */
    function getPrice(address _asset) external returns (uint256);

    /**
     * @dev Gets how many UtilityToken represents the `_amount` of `_asset`.
     *
     * @param _asset address of asset to get the amount.
     * @param _amount amount of `_asset`.
     */
    function calculateAmount(address _asset, uint256 _amount) external view returns (uint256);
}

File 13 of 17 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

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

File 14 of 17 : ERC1155Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC1155Receiver.sol";
import "../../introspection/ERC165.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
    constructor() {
        _registerInterface(
            ERC1155Receiver(0).onERC1155Received.selector ^
            ERC1155Receiver(0).onERC1155BatchReceived.selector
        );
    }
}

File 15 of 17 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "../../introspection/IERC165.sol";

/**
 * _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {

    /**
        @dev Handles the receipt of a single ERC1155 token type. This function is
        called at the end of a `safeTransferFrom` after the balance has been updated.
        To accept the transfer, this must return
        `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
        (i.e. 0xf23a6e61, or its own function selector).
        @param operator The address which initiated the transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param id The ID of the token being transferred
        @param value The amount of tokens being transferred
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
    */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    )
        external
        returns(bytes4);

    /**
        @dev Handles the receipt of a multiple ERC1155 token types. This function
        is called at the end of a `safeBatchTransferFrom` after the balances have
        been updated. To accept the transfer(s), this must return
        `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
        (i.e. 0xbc197c81, or its own function selector).
        @param operator The address which initiated the batch transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param ids An array containing ids of each token being transferred (order and length must match values array)
        @param values An array containing amounts of each token being transferred (order and length must match ids array)
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
    */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    )
        external
        returns(bytes4);
}

File 16 of 17 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
abstract contract ERC165 is IERC165 {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    constructor () {
        // Derived contracts need only register support for their own interfaces,
        // we register support for ERC165 itself here
        _registerInterface(_INTERFACE_ID_ERC165);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     *
     * Time complexity O(1), guaranteed to always use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal virtual {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

File 17 of 17 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_registry","type":"address"},{"internalType":"address","name":"_protocolFee","type":"address"},{"internalType":"address","name":"_feeReceiver","type":"address"},{"internalType":"address","name":"_xTokenWrapper","type":"address"},{"internalType":"address","name":"_utilityToken","type":"address"},{"internalType":"address","name":"_utilityTokenFeed","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iquidityProvider","type":"address"},{"indexed":false,"internalType":"address","name":"bpool","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"ExitPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeReceiver","type":"address"}],"name":"FeeReceiverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidityProvider","type":"address"},{"indexed":false,"internalType":"address","name":"bpool","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"JoinPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"protocolFee","type":"address"}],"name":"ProtocolFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"registry","type":"address"}],"name":"RegistrySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"utilityTokenFeed","type":"address"}],"name":"UtilityTokenFeedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"utilityToken","type":"address"}],"name":"UtilityTokenSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"xTokenWrapper","type":"address"}],"name":"XTokenWrapperSet","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ISwap.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"contract IXToken","name":"tokenIn","type":"address"},{"internalType":"contract IXToken","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountIn","type":"uint256"},{"internalType":"uint256","name":"minTotalAmountOut","type":"uint256"},{"internalType":"bool","name":"useUtilityToken","type":"bool"}],"name":"batchSwapExactIn","outputs":[{"internalType":"uint256","name":"totalAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ISwap.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"contract IXToken","name":"tokenIn","type":"address"},{"internalType":"contract IXToken","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"maxTotalAmountIn","type":"uint256"},{"internalType":"bool","name":"useUtilityToken","type":"bool"}],"name":"batchSwapExactOut","outputs":[{"internalType":"uint256","name":"totalAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256[]","name":"minAmountsOut","type":"uint256[]"}],"name":"exitPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"tokenAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxPoolAmountIn","type":"uint256"}],"name":"exitswapExternAmountOut","outputs":[{"internalType":"uint256","name":"poolAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"exitswapPoolAmountIn","outputs":[{"internalType":"uint256","name":"tokenAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"uint256[]","name":"maxAmountsIn","type":"uint256[]"}],"name":"joinPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"tokenAmountIn","type":"uint256"},{"internalType":"uint256","name":"minPoolAmountOut","type":"uint256"}],"name":"joinswapExternAmountIn","outputs":[{"internalType":"uint256","name":"poolAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxAmountIn","type":"uint256"}],"name":"joinswapPoolAmountOut","outputs":[{"internalType":"uint256","name":"tokenAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ISwap.Swap[][]","name":"swapSequences","type":"tuple[][]"},{"internalType":"contract IXToken","name":"tokenIn","type":"address"},{"internalType":"contract IXToken","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountIn","type":"uint256"},{"internalType":"uint256","name":"minTotalAmountOut","type":"uint256"},{"internalType":"bool","name":"useUtilityToken","type":"bool"}],"name":"multihopBatchSwapExactIn","outputs":[{"internalType":"uint256","name":"totalAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ISwap.Swap[][]","name":"swapSequences","type":"tuple[][]"},{"internalType":"contract IXToken","name":"tokenIn","type":"address"},{"internalType":"contract IXToken","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"maxTotalAmountIn","type":"uint256"},{"internalType":"bool","name":"useUtilityToken","type":"bool"}],"name":"multihopBatchSwapExactOut","outputs":[{"internalType":"uint256","name":"totalAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"contract IProtocolFee","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract IBRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"setFeeReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolFee","type":"address"}],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_registry","type":"address"}],"name":"setRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_utilityToken","type":"address"}],"name":"setUtilityToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_utilityTokenFeed","type":"address"}],"name":"setUtilityTokenFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_xTokenWrapper","type":"address"}],"name":"setXTokenWrapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IXToken","name":"tokenIn","type":"address"},{"internalType":"contract IXToken","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountIn","type":"uint256"},{"internalType":"uint256","name":"minTotalAmountOut","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"},{"internalType":"bool","name":"useUtilityToken","type":"bool"}],"name":"smartSwapExactIn","outputs":[{"internalType":"uint256","name":"totalAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IXToken","name":"tokenIn","type":"address"},{"internalType":"contract IXToken","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"totalAmountOut","type":"uint256"},{"internalType":"uint256","name":"maxTotalAmountIn","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"},{"internalType":"bool","name":"useUtilityToken","type":"bool"}],"name":"smartSwapExactOut","outputs":[{"internalType":"uint256","name":"totalAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"utilityToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"utilityTokenFeed","outputs":[{"internalType":"contract IUTokenPriceFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"}],"name":"viewSplitExactIn","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ISwap.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"nPools","type":"uint256"}],"name":"viewSplitExactOut","outputs":[{"components":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"swapAmount","type":"uint256"},{"internalType":"uint256","name":"limitReturnAmount","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"internalType":"struct ISwap.Swap[]","name":"swaps","type":"tuple[]"},{"internalType":"uint256","name":"totalInput","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xTokenWrapper","outputs":[{"internalType":"contract IXTokenWrapper","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b506040516200577338038062005773833981016040819052620000349162000474565b600062000040620000fc565b600080546001600160a01b0319166001600160a01b0383169081178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506200009c6301ffc9a760e01b62000100565b620000ae630271189760e51b62000100565b620000b98662000188565b620000c48562000215565b620000cf8462000299565b620000da836200031d565b620000e582620003a1565b620000f081620003fc565b505050505050620005ee565b3390565b6001600160e01b0319808216141562000160576040805162461bcd60e51b815260206004820152601c60248201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604482015290519081900360640190fd5b6001600160e01b0319166000908152600160208190526040909120805460ff19169091179055565b6001600160a01b038116620001ba5760405162461bcd60e51b8152600401620001b19062000576565b60405180910390fd5b7f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b81604051620001eb9190620004f4565b60405180910390a1600280546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166200023e5760405162461bcd60e51b8152600401620001b1906200053f565b7f12f03356618ab5217502a4b424ab82e84af7bd05a27dd1d2c10b59a2445acc7f816040516200026f9190620004f4565b60405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038116620002c25760405162461bcd60e51b8152600401620001b19062000508565b7fbdf37c276f641820b141429d245add2552b4118c0866e5a78638e3de5ef18d9d81604051620002f39190620004f4565b60405180910390a1600680546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038116620003465760405162461bcd60e51b8152600401620001b190620005ad565b7fb7829e66b6a542cac12356b2a24b4f0737b0f19b3a309b0409b7a45cc71902fd81604051620003779190620004f4565b60405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b7f61b3b3859569cecb601513cda243516790683fdd32e445a73da99153c0741b4181604051620003d29190620004f4565b60405180910390a1600780546001600160a01b0319166001600160a01b0392909216919091179055565b7ff41b986c5672c286a4b5982affea1a51421eb382f58cb2fe3e84ac25618b3d0e816040516200042d9190620004f4565b60405180910390a1600580546001600160a01b0319166001600160a01b0392909216919091179055565b80516001600160a01b03811681146200046f57600080fd5b919050565b60008060008060008060c087890312156200048d578182fd5b620004988762000457565b9550620004a86020880162000457565b9450620004b86040880162000457565b9350620004c86060880162000457565b9250620004d86080880162000457565b9150620004e860a0880162000457565b90509295509295509295565b6001600160a01b0391909116815260200190565b6020808252601f908201527f666565526563656976657220697320746865207a65726f206164647265737300604082015260600190565b6020808252601f908201527f70726f746f636f6c46656520697320746865207a65726f206164647265737300604082015260600190565b6020808252601c908201527f726567697374727920697320746865207a65726f206164647265737300000000604082015260600190565b60208082526021908201527f78546f6b656e5772617070657220697320746865207a65726f206164647265736040820152607360f81b606082015260800190565b61517580620005fe6000396000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c80638da5cb5b1161010f578063bc197c81116100a2578063e83f663711610071578063e83f6637146103fe578063efdcd97414610411578063f23a6e6114610424578063f2fde38b14610437576101f0565b8063bc197c81146103a5578063c1762b15146103c5578063c667ccbd146103d8578063d7936088146103eb576101f0565b8063a94460f5116100de578063a94460f514610385578063ae86ed891461038d578063b0e21e8a14610395578063b3f006741461039d576101f0565b80638da5cb5b146103445780639013ae081461034c578063a3c1e1361461035f578063a91ee0dc14610372576101f0565b806363ced0921161018757806375da771d1161015657806375da771d146103035780637a15038c146103165780637b103999146103295780638a5c57df14610331576101f0565b806363ced092146102c057806363db0d3d146102d55780636a46cdfc146102e8578063715018a6146102fb576101f0565b8063368bb1fc116101c3578063368bb1fc1461026457806342b81415146102855780634b0f93fb1461029857806351fc218b146102ab576101f0565b806301ffc9a7146101f55780630b13c4101461021e5780632822226f1461023e5780632b1a45c314610251575b600080fd5b610208610203366004614b42565b61044a565b6040516102159190614dc4565b60405180910390f35b61023161022c3660046149b1565b61046d565b6040516102159190615002565b61023161024c366004614b6a565b610ed3565b61023161025f3660046147e9565b610f24565b6102776102723660046147e9565b611232565b604051610215929190614da2565b6102316102933660046147e9565b61160f565b6102776102a63660046147e9565b611950565b6102b3611c76565b6040516102159190614c7b565b6102d36102ce366004614894565b611c85565b005b6102d36102e3366004614708565b611f18565b6102316102f6366004614a2f565b611f7c565b6102d36122cb565b6102316103113660046147e9565b61236d565b610231610324366004614aed565b61256c565b6102b3612851565b6102d361033f366004614894565b612860565b6102b3612c38565b6102d361035a366004614708565b612c47565b6102d361036d366004614708565b612ca8565b6102d3610380366004614708565b612d09565b6102b3612d6a565b6102b3612d79565b6102b3612d88565b6102b3612d97565b6103b86103b3366004614740565b612da6565b6040516102159190614dcf565b6102316103d33660046147e9565b612db7565b6102316103e6366004614b6a565b6130aa565b6102d36103f9366004614708565b6130ef565b61023161040c366004614ab5565b613150565b6102d361041f366004614708565b613435565b6103b861043236600461482e565b613496565b6102d3610445366004614708565b6134a7565b6001600160e01b0319811660009081526001602052604090205460ff165b919050565b6000610479858461359f565b60005b8651811015610df357600087828151811061049357fe5b60200260200101515160011415610717576104ac614456565b8883815181106104b857fe5b60200260200101516000815181106104cc57fe5b602090810291909101810151908101518151604051636eb1769f60e11b815292935090916000906001600160a01b0384169063dd62ed3e906105149030908690600401614ccc565b60206040518083038186803b15801561052c57600080fd5b505afa158015610540573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105649190614bc2565b11156105ee57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b39161059a9190600090600401614cb3565b602060405180830381600087803b1580156105b457600080fd5b505af11580156105c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ec9190614b26565b505b8251608084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b39261061f92600401614cb3565b602060405180830381600087803b15801561063957600080fd5b505af115801561064d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106719190614b26565b50806001600160a01b0316637c5e9ea484602001518560800151866040015187606001518860a001516040518663ffffffff1660e01b81526004016106ba959493929190614ce6565b6040805180830381600087803b1580156106d357600080fd5b505af11580156106e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070b9190614bda565b509350610dde92505050565b6000610721614456565b89848151811061072d57fe5b602002602001015160018151811061074157fe5b60209081029190910181015180519181015160405163f8b2cb4f60e01b81529193506001600160a01b0383169163f8d6aed491839163f8b2cb4f9161078891600401614c7b565b60206040518083038186803b1580156107a057600080fd5b505afa1580156107b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d89190614bc2565b6020850151604051634a46c67360e11b81526001600160a01b0386169163948d8ce6916108089190600401614c7b565b60206040518083038186803b15801561082057600080fd5b505afa158015610834573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108589190614bc2565b604080870151905163f8b2cb4f60e01b81526001600160a01b0387169163f8b2cb4f916108889190600401614c7b565b60206040518083038186803b1580156108a057600080fd5b505afa1580156108b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d89190614bc2565b6040808801519051634a46c67360e11b81526001600160a01b0388169163948d8ce6916109089190600401614c7b565b60206040518083038186803b15801561092057600080fd5b505afa158015610934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109589190614bc2565b8760600151876001600160a01b031663d4cadf686040518163ffffffff1660e01b815260040160206040518083038186803b15801561099657600080fd5b505afa1580156109aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ce9190614bc2565b6040518763ffffffff1660e01b81526004016109ef9695949392919061504d565b60206040518083038186803b158015610a0757600080fd5b505afa158015610a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3f9190614bc2565b9250610a49614456565b8b8681518110610a5557fe5b6020026020010151600081518110610a6957fe5b602090810291909101810151908101518151604051636eb1769f60e11b81529293509091600019906001600160a01b0384169063dd62ed3e90610ab29030908690600401614ccc565b60206040518083038186803b158015610aca57600080fd5b505afa158015610ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b029190614bc2565b1015610b8d57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b391610b39919060001990600401614cb3565b602060405180830381600087803b158015610b5357600080fd5b505af1158015610b67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8b9190614b26565b505b806001600160a01b0316637c5e9ea48460200151856080015186604001518a8860a001516040518663ffffffff1660e01b8152600401610bd1959493929190614ce6565b6040805180830381600087803b158015610bea57600080fd5b505af1158015610bfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c229190614bda565b5060208601518651604051636eb1769f60e11b81529299509091600019916001600160a01b0384169163dd62ed3e91610c6091309190600401614ccc565b60206040518083038186803b158015610c7857600080fd5b505afa158015610c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb09190614bc2565b1015610d3b57855160405163095ea7b360e01b81526001600160a01b0383169163095ea7b391610ce7919060001990600401614cb3565b602060405180830381600087803b158015610d0157600080fd5b505af1158015610d15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d399190614b26565b505b846001600160a01b0316637c5e9ea48760200151886080015189604001518a606001518b60a001516040518663ffffffff1660e01b8152600401610d83959493929190614ce6565b6040805180830381600087803b158015610d9c57600080fd5b505af1158015610db0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd49190614bda565b5050505050505050505b610de88184613641565b92505060010161047c565b5082811115610e1d5760405162461bcd60e51b8152600401610e1490614f37565b60405180910390fd5b60035460405163848ed17f60e01b8152610eab9187916001600160a01b039091169063848ed17f90610e55908b908790600401614d3b565b60206040518083038186803b158015610e6d57600080fd5b505afa158015610e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea59190614bc2565b846136a4565b610ebd84610eb886613966565b6139e5565b610eca85610eb887613966565b95945050505050565b600060606000610ee589898988611232565b909250905085811115610f0a5760405162461bcd60e51b8152600401610e1490614f37565b610f17828a8a8988613150565b9998505050505050505050565b6000610f30848361359f565b60405163095ea7b360e01b81526001600160a01b0385169063095ea7b390610f5e9088908690600401614cb3565b602060405180830381600087803b158015610f7857600080fd5b505af1158015610f8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb09190614b26565b5060405163036836fd60e51b81526001600160a01b03861690636d06dfa090610fe190879087908790600401614d1a565b602060405180830381600087803b158015610ffb57600080fd5b505af115801561100f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110339190614bc2565b905061104284610eb886613966565b6004805460405163095ea7b360e01b81526001600160a01b038089169363095ea7b39361107493921691889101614cb3565b602060405180830381600087803b15801561108e57600080fd5b505af11580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a916110f8918991889101614cb3565b602060405180830381600087803b15801561111257600080fd5b505af1158015611126573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114a9190614b26565b6111665760405162461bcd60e51b8152600401610e1490614ee9565b600480546040516327009c7560e01b81526111ef926001600160a01b03909216916327009c7591611199918a9101614c7b565b60206040518083038186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e99190614724565b846139e5565b7f4671f31d5ada3c01641120fcda075c42345f98aecbc27a921204235adfd602aa33868560405161122293929190614c8f565b60405180910390a1949350505050565b60025460405163bfdbfc4360e01b815260609160009183916001600160a01b03169063bfdbfc439061126c908a908a908990600401614c8f565b60006040518083038186803b15801561128457600080fd5b505afa158015611298573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112c09190810190614919565b9050606081516001600160401b03811180156112db57600080fd5b5060405190808252806020026020018201604052801561131557816020015b6113026144a7565b8152602001906001900390816112fa5790505b5090506000805b835181101561138e576113438a8a86848151811061133657fe5b6020026020010151613a13565b83828151811061134f57fe5b602002602001018190525061138483828151811061136957fe5b602002602001015160c001518361364190919063ffffffff16565b915060010161131c565b50606082516001600160401b03811180156113a857600080fd5b506040519080825280602002602001820160405280156113d2578160200160208202803683370190505b5090506000805b845181101561145e57611416846114108784815181106113f557fe5b602002602001015160c001518d613cf390919063ffffffff16565b90613d4c565b83828151811061142257fe5b60200260200101818152505061145483828151811061143d57fe5b60200260200101518361364190919063ffffffff16565b91506001016113d9565b50888110156114b5576114976114748a83613d8e565b8360008151811061148157fe5b602002602001015161364190919063ffffffff16565b826000815181106114a457fe5b6020026020010181815250506114ff565b6114e56114c2828b613d8e565b836000815181106114cf57fe5b6020026020010151613d8e90919063ffffffff16565b826000815181106114f257fe5b6020026020010181815250505b83516001600160401b038111801561151657600080fd5b5060405190808252806020026020018201604052801561155057816020015b61153d614456565b8152602001906001900390816115355790505b50965060005b84518110156115f4576040518060c0016040528086838151811061157657fe5b6020026020010151600001516001600160a01b031681526020018d6001600160a01b031681526020018c6001600160a01b031681526020018483815181106115ba57fe5b6020026020010151815260200160001981526020016000198152508882815181106115e157fe5b6020908102919091010152600101611556565b506115ff8285613dd0565b9550505050505094509492505050565b600480546040516327009c7560e01b815260009283926001600160a01b0316916327009c7591611641918a9101614c7b565b60206040518083038186803b15801561165957600080fd5b505afa15801561166d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116919190614724565b905061169d818461359f565b600480546040516339f4769360e01b81526001600160a01b03909116916339f47693916116ce918591889101614cb3565b602060405180830381600087803b1580156116e857600080fd5b505af11580156116fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117209190614b26565b61173c5760405162461bcd60e51b8152600401610e1490614e52565b60405162592ce960e31b81526001600160a01b038716906302c967489061176b90889088908890600401614d1a565b602060405180830381600087803b15801561178557600080fd5b505af1158015611799573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bd9190614bc2565b91506117c985856139e5565b60006117d58484613d8e565b9050801561190b576004805460405163095ea7b360e01b81526001600160a01b03808b169363095ea7b39361180f93921691869101614cb3565b602060405180830381600087803b15801561182957600080fd5b505af115801561183d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118619190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a91611893918b91869101614cb3565b602060405180830381600087803b1580156118ad57600080fd5b505af11580156118c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e59190614b26565b6119015760405162461bcd60e51b8152600401610e1490614ee9565b61190b82826139e5565b7f2206cb302a65b0877ae38d30e1ad81ec174734a0709a9664e91f77cac85303fb33888560405161193e93929190614c8f565b60405180910390a15050949350505050565b60025460405163bfdbfc4360e01b815260609160009183916001600160a01b03169063bfdbfc439061198a908a908a908990600401614c8f565b60006040518083038186803b1580156119a257600080fd5b505afa1580156119b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119de9190810190614919565b9050606081516001600160401b03811180156119f957600080fd5b50604051908082528060200260200182016040528015611a3357816020015b611a206144a7565b815260200190600190039081611a185790505b5090506000805b8351811015611a8457611a548a8a86848151811061133657fe5b838281518110611a6057fe5b6020026020010181905250611a7a83828151811061136957fe5b9150600101611a3a565b50606082516001600160401b0381118015611a9e57600080fd5b50604051908082528060200260200182016040528015611ac8578160200160208202803683370190505b5090506000805b8451811015611b1c57611aeb846114108784815181106113f557fe5b838281518110611af757fe5b602002602001018181525050611b1283828151811061143d57fe5b9150600101611acf565b5088811015611b5057611b326114748a83613d8e565b82600081518110611b3f57fe5b602002602001018181525050611b77565b611b5d6114c2828b613d8e565b82600081518110611b6a57fe5b6020026020010181815250505b83516001600160401b0381118015611b8e57600080fd5b50604051908082528060200260200182016040528015611bc857816020015b611bb5614456565b815260200190600190039081611bad5790505b50965060005b8451811015611c6b576040518060c00160405280868381518110611bee57fe5b6020026020010151600001516001600160a01b031681526020018d6001600160a01b031681526020018c6001600160a01b03168152602001848381518110611c3257fe5b6020026020010151815260200160008152602001600019815250888281518110611c5857fe5b6020908102919091010152600101611bce565b506115ff8285613f20565b6004546001600160a01b031681565b600480546040516327009c7560e01b81526000926001600160a01b03909216916327009c7591611cb791899101614c7b565b60206040518083038186803b158015611ccf57600080fd5b505afa158015611ce3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d079190614724565b9050611d13818561359f565b600480546040516339f4769360e01b81526001600160a01b03909116916339f4769391611d44918591899101614cb3565b602060405180830381600087803b158015611d5e57600080fd5b505af1158015611d72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d969190614b26565b611db25760405162461bcd60e51b8152600401610e1490614e52565b60405163b02f0b7360e01b81526001600160a01b0386169063b02f0b7390611de29087908790879060040161500b565b600060405180830381600087803b158015611dfc57600080fd5b505af1158015611e10573d6000803e3d6000fd5b505050506060856001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b158015611e4f57600080fd5b505afa158015611e63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e8b9190810190614919565b905060005b8151811015611ed457611ecc828281518110611ea857fe5b6020026020010151610eb8848481518110611ebf57fe5b6020026020010151613966565b600101611e90565b507f2206cb302a65b0877ae38d30e1ad81ec174734a0709a9664e91f77cac85303fb338787604051611f0893929190614c8f565b60405180910390a1505050505050565b611f20614069565b6000546001600160a01b03908116911614611f70576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f798161406d565b50565b6000611f88868561359f565b60005b8751811015612251576000805b898381518110611fa457fe5b60200260200101515181101561223b57611fbc614456565b8a8481518110611fc857fe5b60200260200101518281518110611fdb57fe5b602002602001015190506000816020015190508260011415611fff57606082018490525b8151604051636eb1769f60e11b81526000906001600160a01b0384169063dd62ed3e906120329030908690600401614ccc565b60206040518083038186803b15801561204a57600080fd5b505afa15801561205e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120829190614bc2565b111561210c57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b3916120b89190600090600401614cb3565b602060405180830381600087803b1580156120d257600080fd5b505af11580156120e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210a9190614b26565b505b8251606084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b39261213d92600401614cb3565b602060405180830381600087803b15801561215757600080fd5b505af115801561216b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218f9190614b26565b50806001600160a01b0316638201aa3f84602001518560600151866040015187608001518860a001516040518663ffffffff1660e01b81526004016121d8959493929190614ce6565b6040805180830381600087803b1580156121f157600080fd5b505af1158015612205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122299190614bda565b5094505060019092019150611f989050565b506122468184613641565b925050600101611f8b565b50828110156122725760405162461bcd60e51b8152600401610e1490614f10565b60035460405163848ed17f60e01b81526122aa9188916001600160a01b039091169063848ed17f90610e55908c908a90600401614d3b565b6122b485826139e5565b6122c186610eb888613966565b9695505050505050565b6122d3614069565b6000546001600160a01b03908116911614612323576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b600480546040516327009c7560e01b815260009283926001600160a01b0316916327009c759161239f918a9101614c7b565b60206040518083038186803b1580156123b757600080fd5b505afa1580156123cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ef9190614724565b90506123fb818561359f565b600480546040516339f4769360e01b81526001600160a01b03909116916339f476939161242c918591899101614cb3565b602060405180830381600087803b15801561244657600080fd5b505af115801561245a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247e9190614b26565b61249a5760405162461bcd60e51b8152600401610e1490614e52565b6040516346ab38f160e01b81526001600160a01b038716906346ab38f1906124ca90889088908890600401614d1a565b602060405180830381600087803b1580156124e457600080fd5b505af11580156124f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251c9190614bc2565b915061252885836139e5565b7f2206cb302a65b0877ae38d30e1ad81ec174734a0709a9664e91f77cac85303fb33878660405161255b93929190614c8f565b60405180910390a150949350505050565b6000612578868561359f565b60005b87518110156127f85761258c614456565b88828151811061259857fe5b602090810291909101810151908101518151604051636eb1769f60e11b815292935090916000906001600160a01b0384169063dd62ed3e906125e09030908690600401614ccc565b60206040518083038186803b1580156125f857600080fd5b505afa15801561260c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126309190614bc2565b11156126ba57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b3916126669190600090600401614cb3565b602060405180830381600087803b15801561268057600080fd5b505af1158015612694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b89190614b26565b505b8251606084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b3926126eb92600401614cb3565b602060405180830381600087803b15801561270557600080fd5b505af1158015612719573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273d9190614b26565b5060208301516060840151604080860151608087015160a08801519251638201aa3f60e01b81526000956001600160a01b03881695638201aa3f956127889592949193600401614ce6565b6040805180830381600087803b1580156127a157600080fd5b505af11580156127b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127d99190614bda565b5090506127e68187613641565b9550506001909301925061257b915050565b50828110156128195760405162461bcd60e51b8152600401610e1490614f10565b60035460405163886b917560e01b81526122aa9188916001600160a01b039091169063886b917590610e55908c908a90600401614da2565b6002546001600160a01b031681565b6060846001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561289b57600080fd5b505afa1580156128af573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128d79190810190614919565b905060005b81518110156129c2576129148282815181106128f457fe5b602002602001015185858481811061290857fe5b9050602002013561359f565b81818151811061292057fe5b60200260200101516001600160a01b031663095ea7b38786868581811061294357fe5b905060200201356040518363ffffffff1660e01b8152600401612967929190614cb3565b602060405180830381600087803b15801561298157600080fd5b505af1158015612995573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b99190614b26565b506001016128dc565b506040516313da703560e21b81526001600160a01b03861690634f69c0d4906129f39087908790879060040161500b565b600060405180830381600087803b158015612a0d57600080fd5b505af1158015612a21573d6000803e3d6000fd5b5050505060005b8151811015612a4857612a40828281518110611ea857fe5b600101612a28565b506004805460405163095ea7b360e01b81526001600160a01b038089169363095ea7b393612a7b93921691899101614cb3565b602060405180830381600087803b158015612a9557600080fd5b505af1158015612aa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acd9190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a91612aff918991899101614cb3565b602060405180830381600087803b158015612b1957600080fd5b505af1158015612b2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b519190614b26565b612b6d5760405162461bcd60e51b8152600401610e1490614ee9565b600480546040516327009c7560e01b8152612bf6926001600160a01b03909216916327009c7591612ba0918a9101614c7b565b60206040518083038186803b158015612bb857600080fd5b505afa158015612bcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf09190614724565b856139e5565b7f4671f31d5ada3c01641120fcda075c42345f98aecbc27a921204235adfd602aa338686604051612c2993929190614c8f565b60405180910390a15050505050565b6000546001600160a01b031690565b612c4f614069565b6000546001600160a01b03908116911614612c9f576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f79816140ec565b612cb0614069565b6000546001600160a01b03908116911614612d00576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f7981614145565b612d11614069565b6000546001600160a01b03908116911614612d61576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f79816141c4565b6005546001600160a01b031681565b6007546001600160a01b031681565b6003546001600160a01b031681565b6006546001600160a01b031681565b63bc197c8160e01b95945050505050565b6000612dc3848461359f565b60405163095ea7b360e01b81526001600160a01b0385169063095ea7b390612df19088908790600401614cb3565b602060405180830381600087803b158015612e0b57600080fd5b505af1158015612e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e439190614b26565b50604051635db3427760e01b81526001600160a01b03861690635db3427790612e7490879087908790600401614d1a565b602060405180830381600087803b158015612e8e57600080fd5b505af1158015612ea2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ec69190614bc2565b6004805460405163095ea7b360e01b81529293506001600160a01b038089169363095ea7b393612efc9390921691869101614cb3565b602060405180830381600087803b158015612f1657600080fd5b505af1158015612f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f4e9190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a91612f80918991869101614cb3565b602060405180830381600087803b158015612f9a57600080fd5b505af1158015612fae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd29190614b26565b612fee5760405162461bcd60e51b8152600401610e1490614ee9565b600480546040516327009c7560e01b8152613077926001600160a01b03909216916327009c7591613021918a9101614c7b565b60206040518083038186803b15801561303957600080fd5b505afa15801561304d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130719190614724565b826139e5565b7f4671f31d5ada3c01641120fcda075c42345f98aecbc27a921204235adfd602aa33868360405161122293929190614c8f565b6000606060006130bc89898988611950565b9092509050858110156130e15760405162461bcd60e51b8152600401610e1490614f10565b610f17828a8a8a8a8961256c565b6130f7614069565b6000546001600160a01b03908116911614613147576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f7981614243565b600061315c858461359f565b60005b86518110156133dc57613170614456565b87828151811061317c57fe5b602090810291909101810151908101518151604051636eb1769f60e11b815292935090916000906001600160a01b0384169063dd62ed3e906131c49030908690600401614ccc565b60206040518083038186803b1580156131dc57600080fd5b505afa1580156131f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132149190614bc2565b111561329e57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b39161324a9190600090600401614cb3565b602060405180830381600087803b15801561326457600080fd5b505af1158015613278573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061329c9190614b26565b505b8251608084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b3926132cf92600401614cb3565b602060405180830381600087803b1580156132e957600080fd5b505af11580156132fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133219190614b26565b5060208301516080840151604080860151606087015160a08801519251631f17a7a960e21b81526000956001600160a01b03881695637c5e9ea49561336c9592949193600401614ce6565b6040805180830381600087803b15801561338557600080fd5b505af1158015613399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133bd9190614bda565b5090506133ca8187613641565b9550506001909301925061315f915050565b50828111156133fd5760405162461bcd60e51b8152600401610e1490614f37565b60035460405163886b917560e01b8152610eab9187916001600160a01b039091169063886b917590610e55908b908790600401614da2565b61343d614069565b6000546001600160a01b0390811691161461348d576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f798161429c565b63f23a6e6160e01b95945050505050565b6134af614069565b6000546001600160a01b039081169116146134ff576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b6001600160a01b0381166135445760405162461bcd60e51b81526004018080602001828103825260268152602001806150d96026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6040516323b872dd60e01b81526001600160a01b038316906323b872dd906135cf90339030908690600401614c8f565b602060405180830381600087803b1580156135e957600080fd5b505af11580156135fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136219190614b26565b61363d5760405162461bcd60e51b8152600401610e1490614f94565b5050565b60008282018381101561369b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b8080156136bb57506007546001600160a01b031615155b80156136d157506005546001600160a01b031615155b156138bf576005546000906001600160a01b03166329550f97856136f6866002613d4c565b6040518363ffffffff1660e01b8152600401613713929190614cb3565b60206040518083038186803b15801561372b57600080fd5b505afa15801561373f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137639190614bc2565b90508015613817576007546006546040516323b872dd60e01b81526001600160a01b03928316926323b872dd926137a4923392909116908690600401614c8f565b602060405180830381600087803b1580156137be57600080fd5b505af11580156137d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f69190614b26565b6138125760405162461bcd60e51b8152600401610e1490614de4565b6138b9565b6006546040516323b872dd60e01b81526001600160a01b03808716926323b872dd9261384b92339216908890600401614c8f565b602060405180830381600087803b15801561386557600080fd5b505af1158015613879573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061389d9190614b26565b6138b95760405162461bcd60e51b8152600401610e1490614f5d565b50613961565b6006546040516323b872dd60e01b81526001600160a01b03808616926323b872dd926138f392339216908790600401614c8f565b602060405180830381600087803b15801561390d57600080fd5b505af1158015613921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139459190614b26565b6139615760405162461bcd60e51b8152600401610e1490614f5d565b505050565b6040516370a0823160e01b81526000906001600160a01b038316906370a0823190613995903090600401614c7b565b60206040518083038186803b1580156139ad57600080fd5b505afa1580156139c1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061369e9190614bc2565b60405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb906135cf9033908590600401614cb3565b613a1b6144a7565b60405163f8b2cb4f60e01b815282906000906001600160a01b0383169063f8b2cb4f90613a4c908990600401614c7b565b60206040518083038186803b158015613a6457600080fd5b505afa158015613a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9c9190614bc2565b90506000826001600160a01b031663f8b2cb4f876040518263ffffffff1660e01b8152600401613acc9190614c7b565b60206040518083038186803b158015613ae457600080fd5b505afa158015613af8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b1c9190614bc2565b90506000836001600160a01b031663948d8ce6896040518263ffffffff1660e01b8152600401613b4c9190614c7b565b60206040518083038186803b158015613b6457600080fd5b505afa158015613b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9c9190614bc2565b90506000846001600160a01b031663948d8ce6896040518263ffffffff1660e01b8152600401613bcc9190614c7b565b60206040518083038186803b158015613be457600080fd5b505afa158015613bf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c1c9190614bc2565b90506000856001600160a01b031663d4cadf686040518163ffffffff1660e01b815260040160206040518083038186803b158015613c5957600080fd5b505afa158015613c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c919190614bc2565b90506000613ca084868561431b565b9050613caa6144a7565b506040805160e0810182526001600160a01b038b16815260208101979097528601939093526060850193909352608084015260a083019190915260c08201529150509392505050565b600082613d025750600061369e565b82820282848281613d0f57fe5b041461369b5760405162461bcd60e51b81526004018080602001828103825260218152602001806150ff6021913960400191505060405180910390fd5b600061369b83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061435a565b600061369b83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506143fc565b6000805b8351811015613f19576000838281518110613deb57fe5b6020026020010151600001516001600160a01b031663f8d6aed4858481518110613e1157fe5b602002602001015160200151868581518110613e2957fe5b602002602001015160400151878681518110613e4157fe5b602002602001015160600151888781518110613e5957fe5b6020026020010151608001518a8881518110613e7157fe5b60200260200101518a8981518110613e8557fe5b602002602001015160a001516040518763ffffffff1660e01b8152600401613eb29695949392919061504d565b60206040518083038186803b158015613eca57600080fd5b505afa158015613ede573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f029190614bc2565b9050613f0e8382613641565b925050600101613dd4565b5092915050565b6000805b8351811015613f19576000838281518110613f3b57fe5b6020026020010151600001516001600160a01b031663ba9530a6858481518110613f6157fe5b602002602001015160200151868581518110613f7957fe5b602002602001015160400151878681518110613f9157fe5b602002602001015160600151888781518110613fa957fe5b6020026020010151608001518a8881518110613fc157fe5b60200260200101518a8981518110613fd557fe5b602002602001015160a001516040518763ffffffff1660e01b81526004016140029695949392919061504d565b60206040518083038186803b15801561401a57600080fd5b505afa15801561402e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140529190614bc2565b905061405e8382613641565b925050600101613f24565b3390565b6001600160a01b0381166140935760405162461bcd60e51b8152600401610e1490614e7b565b7f12f03356618ab5217502a4b424ab82e84af7bd05a27dd1d2c10b59a2445acc7f816040516140c29190614c7b565b60405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b7f61b3b3859569cecb601513cda243516790683fdd32e445a73da99153c0741b418160405161411b9190614c7b565b60405180910390a1600780546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03811661416b5760405162461bcd60e51b8152600401610e1490614fc1565b7fb7829e66b6a542cac12356b2a24b4f0737b0f19b3a309b0409b7a45cc71902fd8160405161419a9190614c7b565b60405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166141ea5760405162461bcd60e51b8152600401610e1490614eb2565b7f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b816040516142199190614c7b565b60405180910390a1600280546001600160a01b0319166001600160a01b0392909216919091179055565b7ff41b986c5672c286a4b5982affea1a51421eb382f58cb2fe3e84ac25618b3d0e816040516142729190614c7b565b60405180910390a1600580546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166142c25760405162461bcd60e51b8152600401610e1490614e1b565b7fbdf37c276f641820b141429d245add2552b4118c0866e5a78638e3de5ef18d9d816040516142f19190614c7b565b60405180910390a1600680546001600160a01b0319166001600160a01b0392909216919091179055565b6000614352670de0b6b3a76400006114108561434c61433a878a613641565b6114108a670de0b6b3a7640000613cf3565b90613cf3565b949350505050565b600081836143e65760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156143ab578181015183820152602001614393565b50505050905090810190601f1680156143d85780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816143f257fe5b0495945050505050565b6000818484111561444e5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156143ab578181015183820152602001614393565b505050900390565b6040518060c0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610468816150b5565b600082601f830112614508578081fd5b813561451b61451682615098565b615075565b818152915060208083019084810160005b8481101561455557614543888484358a0101614560565b8452928201929082019060010161452c565b505050505092915050565b600082601f830112614570578081fd5b813561457e61451682615098565b818152915060208083019084810160c0808502870183018810156145a157600080fd5b6000805b868110156146375782848b0312156145bb578182fd5b604080518481018181106001600160401b03821117156145d757fe5b825285356145e4816150b5565b81526145f18688016144ed565b878201526146008287016144ed565b91810191909152606085810135908201526080808601359082015260a08086013590820152865294840194928201926001016145a5565b5050505050505092915050565b600082601f830112614654578081fd5b813561466261451682615098565b81815291506020808301908481018184028601820187101561468357600080fd5b60005b8481101561455557813584529282019290820190600101614686565b600082601f8301126146b2578081fd5b81356001600160401b038111156146c557fe5b6146d8601f8201601f1916602001615075565b91508082528360208285010111156146ef57600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215614719578081fd5b813561369b816150b5565b600060208284031215614735578081fd5b815161369b816150b5565b600080600080600060a08688031215614757578081fd5b8535614762816150b5565b94506020860135614772816150b5565b935060408601356001600160401b038082111561478d578283fd5b61479989838a01614644565b945060608801359150808211156147ae578283fd5b6147ba89838a01614644565b935060808801359150808211156147cf578283fd5b506147dc888289016146a2565b9150509295509295909350565b600080600080608085870312156147fe578384fd5b8435614809816150b5565b93506020850135614819816150b5565b93969395505050506040820135916060013590565b600080600080600060a08688031215614845578081fd5b8535614850816150b5565b94506020860135614860816150b5565b9350604086013592506060860135915060808601356001600160401b03811115614888578182fd5b6147dc888289016146a2565b600080600080606085870312156148a9578182fd5b84356148b4816150b5565b93506020850135925060408501356001600160401b03808211156148d6578384fd5b818701915087601f8301126148e9578384fd5b8135818111156148f7578485fd5b886020808302850101111561490a578485fd5b95989497505060200194505050565b6000602080838503121561492b578182fd5b82516001600160401b03811115614940578283fd5b8301601f81018513614950578283fd5b805161495e61451682615098565b818152838101908385018584028501860189101561497a578687fd5b8694505b838510156149a5578051614991816150b5565b83526001949094019391850191850161497e565b50979650505050505050565b600080600080600060a086880312156149c8578283fd5b85356001600160401b038111156149dd578384fd5b6149e9888289016144f8565b95505060208601356149fa816150b5565b93506040860135614a0a816150b5565b9250606086013591506080860135614a21816150ca565b809150509295509295909350565b60008060008060008060c08789031215614a47578384fd5b86356001600160401b03811115614a5c578485fd5b614a6889828a016144f8565b9650506020870135614a79816150b5565b94506040870135614a89816150b5565b9350606087013592506080870135915060a0870135614aa7816150ca565b809150509295509295509295565b600080600080600060a08688031215614acc578283fd5b85356001600160401b03811115614ae1578384fd5b6149e988828901614560565b60008060008060008060c08789031215614b05578384fd5b86356001600160401b03811115614b1a578485fd5b614a6889828a01614560565b600060208284031215614b37578081fd5b815161369b816150ca565b600060208284031215614b53578081fd5b81356001600160e01b03198116811461369b578182fd5b60008060008060008060c08789031215614b82578384fd5b8635614b8d816150b5565b95506020870135614b9d816150b5565b945060408701359350606087013592506080870135915060a0870135614aa7816150ca565b600060208284031215614bd3578081fd5b5051919050565b60008060408385031215614bec578182fd5b505080516020909101519092909150565b6000815180845260208085019450808401835b83811015614c7057815180516001600160a01b039081168952848201518116858a015260408083015190911690890152606080820151908901526080808201519089015260a0908101519088015260c09096019590820190600101614c10565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03958616815260208101949094529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b039390931683526020830191909152604082015260600190565b60006040820160408352808551808352606085019150602092506060838202860101838801855b83811015614d9057605f19888403018552614d7e838351614bfd565b94860194925090850190600101614d62565b50509290940194909452949350505050565b600060408252614db56040830185614bfd565b90508260208301529392505050565b901515815260200190565b6001600160e01b031991909116815260200190565b6020808252601f908201527f4552525f4645455f5554494c4954595f5452414e534645525f4641494c454400604082015260600190565b6020808252601f908201527f666565526563656976657220697320746865207a65726f206164647265737300604082015260600190565b6020808252600f908201526e11549497d55395d4905417d413d3d3608a1b604082015260600190565b6020808252601f908201527f70726f746f636f6c46656520697320746865207a65726f206164647265737300604082015260600190565b6020808252601c908201527f726567697374727920697320746865207a65726f206164647265737300000000604082015260600190565b6020808252600d908201526c11549497d5d4905417d413d3d3609a1b604082015260600190565b6020808252600d908201526c11549497d31253525517d3d555609a1b604082015260600190565b6020808252600c908201526b22a9292fa624a6a4aa2fa4a760a11b604082015260600190565b60208082526017908201527f4552525f4645455f5452414e534645525f4641494c4544000000000000000000604082015260600190565b60208082526013908201527211549497d514905394d1915497d19052531151606a1b604082015260600190565b60208082526021908201527f78546f6b656e5772617070657220697320746865207a65726f206164647265736040820152607360f81b606082015260800190565b90815260200190565b838152604060208201819052810182905260006001600160fb1b03831115615031578081fd5b6020830280856060850137919091016060019081529392505050565b958652602086019490945260408501929092526060840152608083015260a082015260c00190565b6040518181016001600160401b038111828210171561509057fe5b604052919050565b60006001600160401b038211156150ab57fe5b5060209081020190565b6001600160a01b0381168114611f7957600080fd5b8015158114611f7957600080fdfe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572a2646970667358221220e8783311e9e3c448553ee8f646a7d435433e17eec5aaa1a22e4d299d3b5441e464736f6c634300070400330000000000000000000000008133eeb249f0636bba0b8230ba1089a219263c04000000000000000000000000abc2108199f33adf0343ef3a67565313c0aed6c1000000000000000000000000cf6a97d02f9cc47c94de5a4874c32a0182638fb40000000000000000000000002b9dc65253c035eb21778cb3898eab5a0ada0cce00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101f05760003560e01c80638da5cb5b1161010f578063bc197c81116100a2578063e83f663711610071578063e83f6637146103fe578063efdcd97414610411578063f23a6e6114610424578063f2fde38b14610437576101f0565b8063bc197c81146103a5578063c1762b15146103c5578063c667ccbd146103d8578063d7936088146103eb576101f0565b8063a94460f5116100de578063a94460f514610385578063ae86ed891461038d578063b0e21e8a14610395578063b3f006741461039d576101f0565b80638da5cb5b146103445780639013ae081461034c578063a3c1e1361461035f578063a91ee0dc14610372576101f0565b806363ced0921161018757806375da771d1161015657806375da771d146103035780637a15038c146103165780637b103999146103295780638a5c57df14610331576101f0565b806363ced092146102c057806363db0d3d146102d55780636a46cdfc146102e8578063715018a6146102fb576101f0565b8063368bb1fc116101c3578063368bb1fc1461026457806342b81415146102855780634b0f93fb1461029857806351fc218b146102ab576101f0565b806301ffc9a7146101f55780630b13c4101461021e5780632822226f1461023e5780632b1a45c314610251575b600080fd5b610208610203366004614b42565b61044a565b6040516102159190614dc4565b60405180910390f35b61023161022c3660046149b1565b61046d565b6040516102159190615002565b61023161024c366004614b6a565b610ed3565b61023161025f3660046147e9565b610f24565b6102776102723660046147e9565b611232565b604051610215929190614da2565b6102316102933660046147e9565b61160f565b6102776102a63660046147e9565b611950565b6102b3611c76565b6040516102159190614c7b565b6102d36102ce366004614894565b611c85565b005b6102d36102e3366004614708565b611f18565b6102316102f6366004614a2f565b611f7c565b6102d36122cb565b6102316103113660046147e9565b61236d565b610231610324366004614aed565b61256c565b6102b3612851565b6102d361033f366004614894565b612860565b6102b3612c38565b6102d361035a366004614708565b612c47565b6102d361036d366004614708565b612ca8565b6102d3610380366004614708565b612d09565b6102b3612d6a565b6102b3612d79565b6102b3612d88565b6102b3612d97565b6103b86103b3366004614740565b612da6565b6040516102159190614dcf565b6102316103d33660046147e9565b612db7565b6102316103e6366004614b6a565b6130aa565b6102d36103f9366004614708565b6130ef565b61023161040c366004614ab5565b613150565b6102d361041f366004614708565b613435565b6103b861043236600461482e565b613496565b6102d3610445366004614708565b6134a7565b6001600160e01b0319811660009081526001602052604090205460ff165b919050565b6000610479858461359f565b60005b8651811015610df357600087828151811061049357fe5b60200260200101515160011415610717576104ac614456565b8883815181106104b857fe5b60200260200101516000815181106104cc57fe5b602090810291909101810151908101518151604051636eb1769f60e11b815292935090916000906001600160a01b0384169063dd62ed3e906105149030908690600401614ccc565b60206040518083038186803b15801561052c57600080fd5b505afa158015610540573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105649190614bc2565b11156105ee57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b39161059a9190600090600401614cb3565b602060405180830381600087803b1580156105b457600080fd5b505af11580156105c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ec9190614b26565b505b8251608084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b39261061f92600401614cb3565b602060405180830381600087803b15801561063957600080fd5b505af115801561064d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106719190614b26565b50806001600160a01b0316637c5e9ea484602001518560800151866040015187606001518860a001516040518663ffffffff1660e01b81526004016106ba959493929190614ce6565b6040805180830381600087803b1580156106d357600080fd5b505af11580156106e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061070b9190614bda565b509350610dde92505050565b6000610721614456565b89848151811061072d57fe5b602002602001015160018151811061074157fe5b60209081029190910181015180519181015160405163f8b2cb4f60e01b81529193506001600160a01b0383169163f8d6aed491839163f8b2cb4f9161078891600401614c7b565b60206040518083038186803b1580156107a057600080fd5b505afa1580156107b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d89190614bc2565b6020850151604051634a46c67360e11b81526001600160a01b0386169163948d8ce6916108089190600401614c7b565b60206040518083038186803b15801561082057600080fd5b505afa158015610834573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108589190614bc2565b604080870151905163f8b2cb4f60e01b81526001600160a01b0387169163f8b2cb4f916108889190600401614c7b565b60206040518083038186803b1580156108a057600080fd5b505afa1580156108b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d89190614bc2565b6040808801519051634a46c67360e11b81526001600160a01b0388169163948d8ce6916109089190600401614c7b565b60206040518083038186803b15801561092057600080fd5b505afa158015610934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109589190614bc2565b8760600151876001600160a01b031663d4cadf686040518163ffffffff1660e01b815260040160206040518083038186803b15801561099657600080fd5b505afa1580156109aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ce9190614bc2565b6040518763ffffffff1660e01b81526004016109ef9695949392919061504d565b60206040518083038186803b158015610a0757600080fd5b505afa158015610a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3f9190614bc2565b9250610a49614456565b8b8681518110610a5557fe5b6020026020010151600081518110610a6957fe5b602090810291909101810151908101518151604051636eb1769f60e11b81529293509091600019906001600160a01b0384169063dd62ed3e90610ab29030908690600401614ccc565b60206040518083038186803b158015610aca57600080fd5b505afa158015610ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b029190614bc2565b1015610b8d57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b391610b39919060001990600401614cb3565b602060405180830381600087803b158015610b5357600080fd5b505af1158015610b67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8b9190614b26565b505b806001600160a01b0316637c5e9ea48460200151856080015186604001518a8860a001516040518663ffffffff1660e01b8152600401610bd1959493929190614ce6565b6040805180830381600087803b158015610bea57600080fd5b505af1158015610bfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c229190614bda565b5060208601518651604051636eb1769f60e11b81529299509091600019916001600160a01b0384169163dd62ed3e91610c6091309190600401614ccc565b60206040518083038186803b158015610c7857600080fd5b505afa158015610c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb09190614bc2565b1015610d3b57855160405163095ea7b360e01b81526001600160a01b0383169163095ea7b391610ce7919060001990600401614cb3565b602060405180830381600087803b158015610d0157600080fd5b505af1158015610d15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d399190614b26565b505b846001600160a01b0316637c5e9ea48760200151886080015189604001518a606001518b60a001516040518663ffffffff1660e01b8152600401610d83959493929190614ce6565b6040805180830381600087803b158015610d9c57600080fd5b505af1158015610db0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd49190614bda565b5050505050505050505b610de88184613641565b92505060010161047c565b5082811115610e1d5760405162461bcd60e51b8152600401610e1490614f37565b60405180910390fd5b60035460405163848ed17f60e01b8152610eab9187916001600160a01b039091169063848ed17f90610e55908b908790600401614d3b565b60206040518083038186803b158015610e6d57600080fd5b505afa158015610e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea59190614bc2565b846136a4565b610ebd84610eb886613966565b6139e5565b610eca85610eb887613966565b95945050505050565b600060606000610ee589898988611232565b909250905085811115610f0a5760405162461bcd60e51b8152600401610e1490614f37565b610f17828a8a8988613150565b9998505050505050505050565b6000610f30848361359f565b60405163095ea7b360e01b81526001600160a01b0385169063095ea7b390610f5e9088908690600401614cb3565b602060405180830381600087803b158015610f7857600080fd5b505af1158015610f8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb09190614b26565b5060405163036836fd60e51b81526001600160a01b03861690636d06dfa090610fe190879087908790600401614d1a565b602060405180830381600087803b158015610ffb57600080fd5b505af115801561100f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110339190614bc2565b905061104284610eb886613966565b6004805460405163095ea7b360e01b81526001600160a01b038089169363095ea7b39361107493921691889101614cb3565b602060405180830381600087803b15801561108e57600080fd5b505af11580156110a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110c69190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a916110f8918991889101614cb3565b602060405180830381600087803b15801561111257600080fd5b505af1158015611126573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114a9190614b26565b6111665760405162461bcd60e51b8152600401610e1490614ee9565b600480546040516327009c7560e01b81526111ef926001600160a01b03909216916327009c7591611199918a9101614c7b565b60206040518083038186803b1580156111b157600080fd5b505afa1580156111c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e99190614724565b846139e5565b7f4671f31d5ada3c01641120fcda075c42345f98aecbc27a921204235adfd602aa33868560405161122293929190614c8f565b60405180910390a1949350505050565b60025460405163bfdbfc4360e01b815260609160009183916001600160a01b03169063bfdbfc439061126c908a908a908990600401614c8f565b60006040518083038186803b15801561128457600080fd5b505afa158015611298573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526112c09190810190614919565b9050606081516001600160401b03811180156112db57600080fd5b5060405190808252806020026020018201604052801561131557816020015b6113026144a7565b8152602001906001900390816112fa5790505b5090506000805b835181101561138e576113438a8a86848151811061133657fe5b6020026020010151613a13565b83828151811061134f57fe5b602002602001018190525061138483828151811061136957fe5b602002602001015160c001518361364190919063ffffffff16565b915060010161131c565b50606082516001600160401b03811180156113a857600080fd5b506040519080825280602002602001820160405280156113d2578160200160208202803683370190505b5090506000805b845181101561145e57611416846114108784815181106113f557fe5b602002602001015160c001518d613cf390919063ffffffff16565b90613d4c565b83828151811061142257fe5b60200260200101818152505061145483828151811061143d57fe5b60200260200101518361364190919063ffffffff16565b91506001016113d9565b50888110156114b5576114976114748a83613d8e565b8360008151811061148157fe5b602002602001015161364190919063ffffffff16565b826000815181106114a457fe5b6020026020010181815250506114ff565b6114e56114c2828b613d8e565b836000815181106114cf57fe5b6020026020010151613d8e90919063ffffffff16565b826000815181106114f257fe5b6020026020010181815250505b83516001600160401b038111801561151657600080fd5b5060405190808252806020026020018201604052801561155057816020015b61153d614456565b8152602001906001900390816115355790505b50965060005b84518110156115f4576040518060c0016040528086838151811061157657fe5b6020026020010151600001516001600160a01b031681526020018d6001600160a01b031681526020018c6001600160a01b031681526020018483815181106115ba57fe5b6020026020010151815260200160001981526020016000198152508882815181106115e157fe5b6020908102919091010152600101611556565b506115ff8285613dd0565b9550505050505094509492505050565b600480546040516327009c7560e01b815260009283926001600160a01b0316916327009c7591611641918a9101614c7b565b60206040518083038186803b15801561165957600080fd5b505afa15801561166d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116919190614724565b905061169d818461359f565b600480546040516339f4769360e01b81526001600160a01b03909116916339f47693916116ce918591889101614cb3565b602060405180830381600087803b1580156116e857600080fd5b505af11580156116fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117209190614b26565b61173c5760405162461bcd60e51b8152600401610e1490614e52565b60405162592ce960e31b81526001600160a01b038716906302c967489061176b90889088908890600401614d1a565b602060405180830381600087803b15801561178557600080fd5b505af1158015611799573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117bd9190614bc2565b91506117c985856139e5565b60006117d58484613d8e565b9050801561190b576004805460405163095ea7b360e01b81526001600160a01b03808b169363095ea7b39361180f93921691869101614cb3565b602060405180830381600087803b15801561182957600080fd5b505af115801561183d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118619190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a91611893918b91869101614cb3565b602060405180830381600087803b1580156118ad57600080fd5b505af11580156118c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118e59190614b26565b6119015760405162461bcd60e51b8152600401610e1490614ee9565b61190b82826139e5565b7f2206cb302a65b0877ae38d30e1ad81ec174734a0709a9664e91f77cac85303fb33888560405161193e93929190614c8f565b60405180910390a15050949350505050565b60025460405163bfdbfc4360e01b815260609160009183916001600160a01b03169063bfdbfc439061198a908a908a908990600401614c8f565b60006040518083038186803b1580156119a257600080fd5b505afa1580156119b6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526119de9190810190614919565b9050606081516001600160401b03811180156119f957600080fd5b50604051908082528060200260200182016040528015611a3357816020015b611a206144a7565b815260200190600190039081611a185790505b5090506000805b8351811015611a8457611a548a8a86848151811061133657fe5b838281518110611a6057fe5b6020026020010181905250611a7a83828151811061136957fe5b9150600101611a3a565b50606082516001600160401b0381118015611a9e57600080fd5b50604051908082528060200260200182016040528015611ac8578160200160208202803683370190505b5090506000805b8451811015611b1c57611aeb846114108784815181106113f557fe5b838281518110611af757fe5b602002602001018181525050611b1283828151811061143d57fe5b9150600101611acf565b5088811015611b5057611b326114748a83613d8e565b82600081518110611b3f57fe5b602002602001018181525050611b77565b611b5d6114c2828b613d8e565b82600081518110611b6a57fe5b6020026020010181815250505b83516001600160401b0381118015611b8e57600080fd5b50604051908082528060200260200182016040528015611bc857816020015b611bb5614456565b815260200190600190039081611bad5790505b50965060005b8451811015611c6b576040518060c00160405280868381518110611bee57fe5b6020026020010151600001516001600160a01b031681526020018d6001600160a01b031681526020018c6001600160a01b03168152602001848381518110611c3257fe5b6020026020010151815260200160008152602001600019815250888281518110611c5857fe5b6020908102919091010152600101611bce565b506115ff8285613f20565b6004546001600160a01b031681565b600480546040516327009c7560e01b81526000926001600160a01b03909216916327009c7591611cb791899101614c7b565b60206040518083038186803b158015611ccf57600080fd5b505afa158015611ce3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d079190614724565b9050611d13818561359f565b600480546040516339f4769360e01b81526001600160a01b03909116916339f4769391611d44918591899101614cb3565b602060405180830381600087803b158015611d5e57600080fd5b505af1158015611d72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d969190614b26565b611db25760405162461bcd60e51b8152600401610e1490614e52565b60405163b02f0b7360e01b81526001600160a01b0386169063b02f0b7390611de29087908790879060040161500b565b600060405180830381600087803b158015611dfc57600080fd5b505af1158015611e10573d6000803e3d6000fd5b505050506060856001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b158015611e4f57600080fd5b505afa158015611e63573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e8b9190810190614919565b905060005b8151811015611ed457611ecc828281518110611ea857fe5b6020026020010151610eb8848481518110611ebf57fe5b6020026020010151613966565b600101611e90565b507f2206cb302a65b0877ae38d30e1ad81ec174734a0709a9664e91f77cac85303fb338787604051611f0893929190614c8f565b60405180910390a1505050505050565b611f20614069565b6000546001600160a01b03908116911614611f70576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f798161406d565b50565b6000611f88868561359f565b60005b8751811015612251576000805b898381518110611fa457fe5b60200260200101515181101561223b57611fbc614456565b8a8481518110611fc857fe5b60200260200101518281518110611fdb57fe5b602002602001015190506000816020015190508260011415611fff57606082018490525b8151604051636eb1769f60e11b81526000906001600160a01b0384169063dd62ed3e906120329030908690600401614ccc565b60206040518083038186803b15801561204a57600080fd5b505afa15801561205e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120829190614bc2565b111561210c57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b3916120b89190600090600401614cb3565b602060405180830381600087803b1580156120d257600080fd5b505af11580156120e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061210a9190614b26565b505b8251606084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b39261213d92600401614cb3565b602060405180830381600087803b15801561215757600080fd5b505af115801561216b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218f9190614b26565b50806001600160a01b0316638201aa3f84602001518560600151866040015187608001518860a001516040518663ffffffff1660e01b81526004016121d8959493929190614ce6565b6040805180830381600087803b1580156121f157600080fd5b505af1158015612205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122299190614bda565b5094505060019092019150611f989050565b506122468184613641565b925050600101611f8b565b50828110156122725760405162461bcd60e51b8152600401610e1490614f10565b60035460405163848ed17f60e01b81526122aa9188916001600160a01b039091169063848ed17f90610e55908c908a90600401614d3b565b6122b485826139e5565b6122c186610eb888613966565b9695505050505050565b6122d3614069565b6000546001600160a01b03908116911614612323576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b600480546040516327009c7560e01b815260009283926001600160a01b0316916327009c759161239f918a9101614c7b565b60206040518083038186803b1580156123b757600080fd5b505afa1580156123cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ef9190614724565b90506123fb818561359f565b600480546040516339f4769360e01b81526001600160a01b03909116916339f476939161242c918591899101614cb3565b602060405180830381600087803b15801561244657600080fd5b505af115801561245a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247e9190614b26565b61249a5760405162461bcd60e51b8152600401610e1490614e52565b6040516346ab38f160e01b81526001600160a01b038716906346ab38f1906124ca90889088908890600401614d1a565b602060405180830381600087803b1580156124e457600080fd5b505af11580156124f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061251c9190614bc2565b915061252885836139e5565b7f2206cb302a65b0877ae38d30e1ad81ec174734a0709a9664e91f77cac85303fb33878660405161255b93929190614c8f565b60405180910390a150949350505050565b6000612578868561359f565b60005b87518110156127f85761258c614456565b88828151811061259857fe5b602090810291909101810151908101518151604051636eb1769f60e11b815292935090916000906001600160a01b0384169063dd62ed3e906125e09030908690600401614ccc565b60206040518083038186803b1580156125f857600080fd5b505afa15801561260c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126309190614bc2565b11156126ba57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b3916126669190600090600401614cb3565b602060405180830381600087803b15801561268057600080fd5b505af1158015612694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b89190614b26565b505b8251606084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b3926126eb92600401614cb3565b602060405180830381600087803b15801561270557600080fd5b505af1158015612719573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273d9190614b26565b5060208301516060840151604080860151608087015160a08801519251638201aa3f60e01b81526000956001600160a01b03881695638201aa3f956127889592949193600401614ce6565b6040805180830381600087803b1580156127a157600080fd5b505af11580156127b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127d99190614bda565b5090506127e68187613641565b9550506001909301925061257b915050565b50828110156128195760405162461bcd60e51b8152600401610e1490614f10565b60035460405163886b917560e01b81526122aa9188916001600160a01b039091169063886b917590610e55908c908a90600401614da2565b6002546001600160a01b031681565b6060846001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561289b57600080fd5b505afa1580156128af573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128d79190810190614919565b905060005b81518110156129c2576129148282815181106128f457fe5b602002602001015185858481811061290857fe5b9050602002013561359f565b81818151811061292057fe5b60200260200101516001600160a01b031663095ea7b38786868581811061294357fe5b905060200201356040518363ffffffff1660e01b8152600401612967929190614cb3565b602060405180830381600087803b15801561298157600080fd5b505af1158015612995573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b99190614b26565b506001016128dc565b506040516313da703560e21b81526001600160a01b03861690634f69c0d4906129f39087908790879060040161500b565b600060405180830381600087803b158015612a0d57600080fd5b505af1158015612a21573d6000803e3d6000fd5b5050505060005b8151811015612a4857612a40828281518110611ea857fe5b600101612a28565b506004805460405163095ea7b360e01b81526001600160a01b038089169363095ea7b393612a7b93921691899101614cb3565b602060405180830381600087803b158015612a9557600080fd5b505af1158015612aa9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612acd9190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a91612aff918991899101614cb3565b602060405180830381600087803b158015612b1957600080fd5b505af1158015612b2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b519190614b26565b612b6d5760405162461bcd60e51b8152600401610e1490614ee9565b600480546040516327009c7560e01b8152612bf6926001600160a01b03909216916327009c7591612ba0918a9101614c7b565b60206040518083038186803b158015612bb857600080fd5b505afa158015612bcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf09190614724565b856139e5565b7f4671f31d5ada3c01641120fcda075c42345f98aecbc27a921204235adfd602aa338686604051612c2993929190614c8f565b60405180910390a15050505050565b6000546001600160a01b031690565b612c4f614069565b6000546001600160a01b03908116911614612c9f576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f79816140ec565b612cb0614069565b6000546001600160a01b03908116911614612d00576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f7981614145565b612d11614069565b6000546001600160a01b03908116911614612d61576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f79816141c4565b6005546001600160a01b031681565b6007546001600160a01b031681565b6003546001600160a01b031681565b6006546001600160a01b031681565b63bc197c8160e01b95945050505050565b6000612dc3848461359f565b60405163095ea7b360e01b81526001600160a01b0385169063095ea7b390612df19088908790600401614cb3565b602060405180830381600087803b158015612e0b57600080fd5b505af1158015612e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e439190614b26565b50604051635db3427760e01b81526001600160a01b03861690635db3427790612e7490879087908790600401614d1a565b602060405180830381600087803b158015612e8e57600080fd5b505af1158015612ea2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ec69190614bc2565b6004805460405163095ea7b360e01b81529293506001600160a01b038089169363095ea7b393612efc9390921691869101614cb3565b602060405180830381600087803b158015612f1657600080fd5b505af1158015612f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f4e9190614b26565b5060048054604051635f9bb63d60e11b81526001600160a01b039091169163bf376c7a91612f80918991869101614cb3565b602060405180830381600087803b158015612f9a57600080fd5b505af1158015612fae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd29190614b26565b612fee5760405162461bcd60e51b8152600401610e1490614ee9565b600480546040516327009c7560e01b8152613077926001600160a01b03909216916327009c7591613021918a9101614c7b565b60206040518083038186803b15801561303957600080fd5b505afa15801561304d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130719190614724565b826139e5565b7f4671f31d5ada3c01641120fcda075c42345f98aecbc27a921204235adfd602aa33868360405161122293929190614c8f565b6000606060006130bc89898988611950565b9092509050858110156130e15760405162461bcd60e51b8152600401610e1490614f10565b610f17828a8a8a8a8961256c565b6130f7614069565b6000546001600160a01b03908116911614613147576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f7981614243565b600061315c858461359f565b60005b86518110156133dc57613170614456565b87828151811061317c57fe5b602090810291909101810151908101518151604051636eb1769f60e11b815292935090916000906001600160a01b0384169063dd62ed3e906131c49030908690600401614ccc565b60206040518083038186803b1580156131dc57600080fd5b505afa1580156131f0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132149190614bc2565b111561329e57825160405163095ea7b360e01b81526001600160a01b0384169163095ea7b39161324a9190600090600401614cb3565b602060405180830381600087803b15801561326457600080fd5b505af1158015613278573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061329c9190614b26565b505b8251608084015160405163095ea7b360e01b81526001600160a01b0385169263095ea7b3926132cf92600401614cb3565b602060405180830381600087803b1580156132e957600080fd5b505af11580156132fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133219190614b26565b5060208301516080840151604080860151606087015160a08801519251631f17a7a960e21b81526000956001600160a01b03881695637c5e9ea49561336c9592949193600401614ce6565b6040805180830381600087803b15801561338557600080fd5b505af1158015613399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133bd9190614bda565b5090506133ca8187613641565b9550506001909301925061315f915050565b50828111156133fd5760405162461bcd60e51b8152600401610e1490614f37565b60035460405163886b917560e01b8152610eab9187916001600160a01b039091169063886b917590610e55908b908790600401614da2565b61343d614069565b6000546001600160a01b0390811691161461348d576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b611f798161429c565b63f23a6e6160e01b95945050505050565b6134af614069565b6000546001600160a01b039081169116146134ff576040805162461bcd60e51b81526020600482018190526024820152600080516020615120833981519152604482015290519081900360640190fd5b6001600160a01b0381166135445760405162461bcd60e51b81526004018080602001828103825260268152602001806150d96026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b6040516323b872dd60e01b81526001600160a01b038316906323b872dd906135cf90339030908690600401614c8f565b602060405180830381600087803b1580156135e957600080fd5b505af11580156135fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136219190614b26565b61363d5760405162461bcd60e51b8152600401610e1490614f94565b5050565b60008282018381101561369b576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b8080156136bb57506007546001600160a01b031615155b80156136d157506005546001600160a01b031615155b156138bf576005546000906001600160a01b03166329550f97856136f6866002613d4c565b6040518363ffffffff1660e01b8152600401613713929190614cb3565b60206040518083038186803b15801561372b57600080fd5b505afa15801561373f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137639190614bc2565b90508015613817576007546006546040516323b872dd60e01b81526001600160a01b03928316926323b872dd926137a4923392909116908690600401614c8f565b602060405180830381600087803b1580156137be57600080fd5b505af11580156137d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f69190614b26565b6138125760405162461bcd60e51b8152600401610e1490614de4565b6138b9565b6006546040516323b872dd60e01b81526001600160a01b03808716926323b872dd9261384b92339216908890600401614c8f565b602060405180830381600087803b15801561386557600080fd5b505af1158015613879573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061389d9190614b26565b6138b95760405162461bcd60e51b8152600401610e1490614f5d565b50613961565b6006546040516323b872dd60e01b81526001600160a01b03808616926323b872dd926138f392339216908790600401614c8f565b602060405180830381600087803b15801561390d57600080fd5b505af1158015613921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139459190614b26565b6139615760405162461bcd60e51b8152600401610e1490614f5d565b505050565b6040516370a0823160e01b81526000906001600160a01b038316906370a0823190613995903090600401614c7b565b60206040518083038186803b1580156139ad57600080fd5b505afa1580156139c1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061369e9190614bc2565b60405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb906135cf9033908590600401614cb3565b613a1b6144a7565b60405163f8b2cb4f60e01b815282906000906001600160a01b0383169063f8b2cb4f90613a4c908990600401614c7b565b60206040518083038186803b158015613a6457600080fd5b505afa158015613a78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9c9190614bc2565b90506000826001600160a01b031663f8b2cb4f876040518263ffffffff1660e01b8152600401613acc9190614c7b565b60206040518083038186803b158015613ae457600080fd5b505afa158015613af8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b1c9190614bc2565b90506000836001600160a01b031663948d8ce6896040518263ffffffff1660e01b8152600401613b4c9190614c7b565b60206040518083038186803b158015613b6457600080fd5b505afa158015613b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b9c9190614bc2565b90506000846001600160a01b031663948d8ce6896040518263ffffffff1660e01b8152600401613bcc9190614c7b565b60206040518083038186803b158015613be457600080fd5b505afa158015613bf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c1c9190614bc2565b90506000856001600160a01b031663d4cadf686040518163ffffffff1660e01b815260040160206040518083038186803b158015613c5957600080fd5b505afa158015613c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c919190614bc2565b90506000613ca084868561431b565b9050613caa6144a7565b506040805160e0810182526001600160a01b038b16815260208101979097528601939093526060850193909352608084015260a083019190915260c08201529150509392505050565b600082613d025750600061369e565b82820282848281613d0f57fe5b041461369b5760405162461bcd60e51b81526004018080602001828103825260218152602001806150ff6021913960400191505060405180910390fd5b600061369b83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061435a565b600061369b83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506143fc565b6000805b8351811015613f19576000838281518110613deb57fe5b6020026020010151600001516001600160a01b031663f8d6aed4858481518110613e1157fe5b602002602001015160200151868581518110613e2957fe5b602002602001015160400151878681518110613e4157fe5b602002602001015160600151888781518110613e5957fe5b6020026020010151608001518a8881518110613e7157fe5b60200260200101518a8981518110613e8557fe5b602002602001015160a001516040518763ffffffff1660e01b8152600401613eb29695949392919061504d565b60206040518083038186803b158015613eca57600080fd5b505afa158015613ede573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f029190614bc2565b9050613f0e8382613641565b925050600101613dd4565b5092915050565b6000805b8351811015613f19576000838281518110613f3b57fe5b6020026020010151600001516001600160a01b031663ba9530a6858481518110613f6157fe5b602002602001015160200151868581518110613f7957fe5b602002602001015160400151878681518110613f9157fe5b602002602001015160600151888781518110613fa957fe5b6020026020010151608001518a8881518110613fc157fe5b60200260200101518a8981518110613fd557fe5b602002602001015160a001516040518763ffffffff1660e01b81526004016140029695949392919061504d565b60206040518083038186803b15801561401a57600080fd5b505afa15801561402e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140529190614bc2565b905061405e8382613641565b925050600101613f24565b3390565b6001600160a01b0381166140935760405162461bcd60e51b8152600401610e1490614e7b565b7f12f03356618ab5217502a4b424ab82e84af7bd05a27dd1d2c10b59a2445acc7f816040516140c29190614c7b565b60405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b7f61b3b3859569cecb601513cda243516790683fdd32e445a73da99153c0741b418160405161411b9190614c7b565b60405180910390a1600780546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03811661416b5760405162461bcd60e51b8152600401610e1490614fc1565b7fb7829e66b6a542cac12356b2a24b4f0737b0f19b3a309b0409b7a45cc71902fd8160405161419a9190614c7b565b60405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166141ea5760405162461bcd60e51b8152600401610e1490614eb2565b7f27fe5f0c1c3b1ed427cc63d0f05759ffdecf9aec9e18d31ef366fc8a6cb5dc3b816040516142199190614c7b565b60405180910390a1600280546001600160a01b0319166001600160a01b0392909216919091179055565b7ff41b986c5672c286a4b5982affea1a51421eb382f58cb2fe3e84ac25618b3d0e816040516142729190614c7b565b60405180910390a1600580546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b0381166142c25760405162461bcd60e51b8152600401610e1490614e1b565b7fbdf37c276f641820b141429d245add2552b4118c0866e5a78638e3de5ef18d9d816040516142f19190614c7b565b60405180910390a1600680546001600160a01b0319166001600160a01b0392909216919091179055565b6000614352670de0b6b3a76400006114108561434c61433a878a613641565b6114108a670de0b6b3a7640000613cf3565b90613cf3565b949350505050565b600081836143e65760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156143ab578181015183820152602001614393565b50505050905090810190601f1680156143d85780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816143f257fe5b0495945050505050565b6000818484111561444e5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156143ab578181015183820152602001614393565b505050900390565b6040518060c0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610468816150b5565b600082601f830112614508578081fd5b813561451b61451682615098565b615075565b818152915060208083019084810160005b8481101561455557614543888484358a0101614560565b8452928201929082019060010161452c565b505050505092915050565b600082601f830112614570578081fd5b813561457e61451682615098565b818152915060208083019084810160c0808502870183018810156145a157600080fd5b6000805b868110156146375782848b0312156145bb578182fd5b604080518481018181106001600160401b03821117156145d757fe5b825285356145e4816150b5565b81526145f18688016144ed565b878201526146008287016144ed565b91810191909152606085810135908201526080808601359082015260a08086013590820152865294840194928201926001016145a5565b5050505050505092915050565b600082601f830112614654578081fd5b813561466261451682615098565b81815291506020808301908481018184028601820187101561468357600080fd5b60005b8481101561455557813584529282019290820190600101614686565b600082601f8301126146b2578081fd5b81356001600160401b038111156146c557fe5b6146d8601f8201601f1916602001615075565b91508082528360208285010111156146ef57600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215614719578081fd5b813561369b816150b5565b600060208284031215614735578081fd5b815161369b816150b5565b600080600080600060a08688031215614757578081fd5b8535614762816150b5565b94506020860135614772816150b5565b935060408601356001600160401b038082111561478d578283fd5b61479989838a01614644565b945060608801359150808211156147ae578283fd5b6147ba89838a01614644565b935060808801359150808211156147cf578283fd5b506147dc888289016146a2565b9150509295509295909350565b600080600080608085870312156147fe578384fd5b8435614809816150b5565b93506020850135614819816150b5565b93969395505050506040820135916060013590565b600080600080600060a08688031215614845578081fd5b8535614850816150b5565b94506020860135614860816150b5565b9350604086013592506060860135915060808601356001600160401b03811115614888578182fd5b6147dc888289016146a2565b600080600080606085870312156148a9578182fd5b84356148b4816150b5565b93506020850135925060408501356001600160401b03808211156148d6578384fd5b818701915087601f8301126148e9578384fd5b8135818111156148f7578485fd5b886020808302850101111561490a578485fd5b95989497505060200194505050565b6000602080838503121561492b578182fd5b82516001600160401b03811115614940578283fd5b8301601f81018513614950578283fd5b805161495e61451682615098565b818152838101908385018584028501860189101561497a578687fd5b8694505b838510156149a5578051614991816150b5565b83526001949094019391850191850161497e565b50979650505050505050565b600080600080600060a086880312156149c8578283fd5b85356001600160401b038111156149dd578384fd5b6149e9888289016144f8565b95505060208601356149fa816150b5565b93506040860135614a0a816150b5565b9250606086013591506080860135614a21816150ca565b809150509295509295909350565b60008060008060008060c08789031215614a47578384fd5b86356001600160401b03811115614a5c578485fd5b614a6889828a016144f8565b9650506020870135614a79816150b5565b94506040870135614a89816150b5565b9350606087013592506080870135915060a0870135614aa7816150ca565b809150509295509295509295565b600080600080600060a08688031215614acc578283fd5b85356001600160401b03811115614ae1578384fd5b6149e988828901614560565b60008060008060008060c08789031215614b05578384fd5b86356001600160401b03811115614b1a578485fd5b614a6889828a01614560565b600060208284031215614b37578081fd5b815161369b816150ca565b600060208284031215614b53578081fd5b81356001600160e01b03198116811461369b578182fd5b60008060008060008060c08789031215614b82578384fd5b8635614b8d816150b5565b95506020870135614b9d816150b5565b945060408701359350606087013592506080870135915060a0870135614aa7816150ca565b600060208284031215614bd3578081fd5b5051919050565b60008060408385031215614bec578182fd5b505080516020909101519092909150565b6000815180845260208085019450808401835b83811015614c7057815180516001600160a01b039081168952848201518116858a015260408083015190911690890152606080820151908901526080808201519089015260a0908101519088015260c09096019590820190600101614c10565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03958616815260208101949094529190931660408301526060820192909252608081019190915260a00190565b6001600160a01b039390931683526020830191909152604082015260600190565b60006040820160408352808551808352606085019150602092506060838202860101838801855b83811015614d9057605f19888403018552614d7e838351614bfd565b94860194925090850190600101614d62565b50509290940194909452949350505050565b600060408252614db56040830185614bfd565b90508260208301529392505050565b901515815260200190565b6001600160e01b031991909116815260200190565b6020808252601f908201527f4552525f4645455f5554494c4954595f5452414e534645525f4641494c454400604082015260600190565b6020808252601f908201527f666565526563656976657220697320746865207a65726f206164647265737300604082015260600190565b6020808252600f908201526e11549497d55395d4905417d413d3d3608a1b604082015260600190565b6020808252601f908201527f70726f746f636f6c46656520697320746865207a65726f206164647265737300604082015260600190565b6020808252601c908201527f726567697374727920697320746865207a65726f206164647265737300000000604082015260600190565b6020808252600d908201526c11549497d5d4905417d413d3d3609a1b604082015260600190565b6020808252600d908201526c11549497d31253525517d3d555609a1b604082015260600190565b6020808252600c908201526b22a9292fa624a6a4aa2fa4a760a11b604082015260600190565b60208082526017908201527f4552525f4645455f5452414e534645525f4641494c4544000000000000000000604082015260600190565b60208082526013908201527211549497d514905394d1915497d19052531151606a1b604082015260600190565b60208082526021908201527f78546f6b656e5772617070657220697320746865207a65726f206164647265736040820152607360f81b606082015260800190565b90815260200190565b838152604060208201819052810182905260006001600160fb1b03831115615031578081fd5b6020830280856060850137919091016060019081529392505050565b958652602086019490945260408501929092526060840152608083015260a082015260c00190565b6040518181016001600160401b038111828210171561509057fe5b604052919050565b60006001600160401b038211156150ab57fe5b5060209081020190565b6001600160a01b0381168114611f7957600080fd5b8015158114611f7957600080fdfe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572a2646970667358221220e8783311e9e3c448553ee8f646a7d435433e17eec5aaa1a22e4d299d3b5441e464736f6c63430007040033

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

0000000000000000000000008133eeb249f0636bba0b8230ba1089a219263c04000000000000000000000000abc2108199f33adf0343ef3a67565313c0aed6c1000000000000000000000000cf6a97d02f9cc47c94de5a4874c32a0182638fb40000000000000000000000002b9dc65253c035eb21778cb3898eab5a0ada0cce00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _registry (address): 0x8133EEB249F0636bba0B8230Ba1089a219263c04
Arg [1] : _protocolFee (address): 0xabC2108199F33ADF0343ef3A67565313C0Aed6C1
Arg [2] : _feeReceiver (address): 0xCf6a97d02f9Cc47C94dE5a4874C32a0182638fb4
Arg [3] : _xTokenWrapper (address): 0x2b9dc65253c035Eb21778cB3898eab5A0AdA0cCe
Arg [4] : _utilityToken (address): 0x0000000000000000000000000000000000000000
Arg [5] : _utilityTokenFeed (address): 0x0000000000000000000000000000000000000000

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000008133eeb249f0636bba0b8230ba1089a219263c04
Arg [1] : 000000000000000000000000abc2108199f33adf0343ef3a67565313c0aed6c1
Arg [2] : 000000000000000000000000cf6a97d02f9cc47c94de5a4874c32a0182638fb4
Arg [3] : 0000000000000000000000002b9dc65253c035eb21778cb3898eab5a0ada0cce
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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