ETH Price: $3,337.43 (-3.46%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Burn Exact And S...195572532024-03-31 23:52:59282 days ago1711929179IN
0x429302C7...9dEC4D050
0 ETH0.0039374816.91272225
Swap Exact ETH F...194836852024-03-21 14:39:23293 days ago1711031963IN
0x429302C7...9dEC4D050
0.01 ETH0.0076592631.67511684
Burn Exact And S...192322532024-02-15 8:51:35328 days ago1707987095IN
0x429302C7...9dEC4D050
0 ETH0.003940717
Burn Exact And S...185429612023-11-10 17:13:47424 days ago1699636427IN
0x429302C7...9dEC4D050
0 ETH0.0137878153.12999733
Burn Exact And S...184555602023-10-29 11:31:59437 days ago1698579119IN
0x429302C7...9dEC4D050
0 ETH0.0031394413.81869237
Swap Exact Token...174980242023-06-17 7:24:47571 days ago1686986687IN
0x429302C7...9dEC4D050
0 ETH0.0022448617.71893136
Burn And Swap Fo...174578632023-06-11 15:48:59577 days ago1686498539IN
0x429302C7...9dEC4D050
0 ETH0.0053926214.57195819
Burn Exact And S...171645932023-05-01 7:48:35618 days ago1682927315IN
0x429302C7...9dEC4D050
0 ETH0.0094803440.02137435
Burn Exact And S...170929892023-04-21 6:28:35628 days ago1682058515IN
0x429302C7...9dEC4D050
0 ETH0.0098694936.47788238
Burn Exact And S...168785602023-03-21 21:04:35658 days ago1679432675IN
0x429302C7...9dEC4D050
0 ETH0.0044300819.91668712
Burn Exact And S...168785482023-03-21 21:02:11658 days ago1679432531IN
0x429302C7...9dEC4D050
0 ETH0.0040246318.81514561
Burn Exact And S...168785112023-03-21 20:54:47658 days ago1679432087IN
0x429302C7...9dEC4D050
0 ETH0.0047037317.84359379
Burn Exact And S...168785102023-03-21 20:54:35658 days ago1679432075IN
0x429302C7...9dEC4D050
0 ETH0.0036583916.81271194
Burn Exact And S...168785072023-03-21 20:53:59658 days ago1679432039IN
0x429302C7...9dEC4D050
0 ETH0.0039313818.06729265
Burn Exact And S...168783942023-03-21 20:30:59658 days ago1679430659IN
0x429302C7...9dEC4D050
0 ETH0.0044736920.91448725
Burn Exact And S...168783882023-03-21 20:29:47658 days ago1679430587IN
0x429302C7...9dEC4D050
0 ETH0.0036384516.79794871
Swap ETH For Tok...168759532023-03-21 12:16:11659 days ago1679400971IN
0x429302C7...9dEC4D050
0.01714071 ETH0.0031185812.77941581
Swap ETH For Tok...168759492023-03-21 12:15:23659 days ago1679400923IN
0x429302C7...9dEC4D050
0.01759275 ETH0.0031821912.55734659
Swap ETH For Tok...168753332023-03-21 10:10:35659 days ago1679393435IN
0x429302C7...9dEC4D050
0.01395695 ETH0.0032876212.97383646
Swap ETH For Tok...168753292023-03-21 10:09:47659 days ago1679393387IN
0x429302C7...9dEC4D050
0.01254466 ETH0.004202112.76070693
Swap ETH For Tok...168753242023-03-21 10:08:47659 days ago1679393327IN
0x429302C7...9dEC4D050
0.01259809 ETH0.0032400813.17816092
Swap ETH For Tok...168752032023-03-21 9:44:11659 days ago1679391851IN
0x429302C7...9dEC4D050
0.01258547 ETH0.0040509715.6948678
Swap ETH For Tok...168751512023-03-21 9:33:11659 days ago1679391191IN
0x429302C7...9dEC4D050
0.02299658 ETH0.003056712.52582313
Swap ETH For Tok...168751042023-03-21 9:23:23659 days ago1679390603IN
0x429302C7...9dEC4D050
0.0106712 ETH0.0032730112.69517718
Swap ETH For Tok...168750852023-03-21 9:19:35659 days ago1679390375IN
0x429302C7...9dEC4D050
0.01527291 ETH0.0032479712.58378416
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
195572532024-03-31 23:52:59282 days ago1711929179
0x429302C7...9dEC4D050
0.29467762 ETH
195572532024-03-31 23:52:59282 days ago1711929179
0x429302C7...9dEC4D050
0.29467762 ETH
194836852024-03-21 14:39:23293 days ago1711031963
0x429302C7...9dEC4D050
0.01 ETH
192322532024-02-15 8:51:35328 days ago1707987095
0x429302C7...9dEC4D050
0.07864238 ETH
192322532024-02-15 8:51:35328 days ago1707987095
0x429302C7...9dEC4D050
0.07864238 ETH
185429612023-11-10 17:13:47424 days ago1699636427
0x429302C7...9dEC4D050
0.00000007 ETH
185429612023-11-10 17:13:47424 days ago1699636427
0x429302C7...9dEC4D050
0.00000007 ETH
184555602023-10-29 11:31:59437 days ago1698579119
0x429302C7...9dEC4D050
0.00548903 ETH
184555602023-10-29 11:31:59437 days ago1698579119
0x429302C7...9dEC4D050
0.00548903 ETH
174980242023-06-17 7:24:47571 days ago1686986687
0x429302C7...9dEC4D050
0.60933087 ETH
174980242023-06-17 7:24:47571 days ago1686986687
0x429302C7...9dEC4D050
0.60933087 ETH
171645932023-05-01 7:48:35618 days ago1682927315
0x429302C7...9dEC4D050
0.09124878 ETH
171645932023-05-01 7:48:35618 days ago1682927315
0x429302C7...9dEC4D050
0.09124878 ETH
170929892023-04-21 6:28:35628 days ago1682058515
0x429302C7...9dEC4D050
0.00000103 ETH
170929892023-04-21 6:28:35628 days ago1682058515
0x429302C7...9dEC4D050
0.00000103 ETH
168785602023-03-21 21:04:35658 days ago1679432675
0x429302C7...9dEC4D050
0.02034351 ETH
168785602023-03-21 21:04:35658 days ago1679432675
0x429302C7...9dEC4D050
0.02034351 ETH
168785482023-03-21 21:02:11658 days ago1679432531
0x429302C7...9dEC4D050
0.02063111 ETH
168785482023-03-21 21:02:11658 days ago1679432531
0x429302C7...9dEC4D050
0.02063111 ETH
168785102023-03-21 20:54:35658 days ago1679432075
0x429302C7...9dEC4D050
0.02209614 ETH
168785102023-03-21 20:54:35658 days ago1679432075
0x429302C7...9dEC4D050
0.02209614 ETH
168785072023-03-21 20:53:59658 days ago1679432039
0x429302C7...9dEC4D050
0.02996772 ETH
168785072023-03-21 20:53:59658 days ago1679432039
0x429302C7...9dEC4D050
0.02996772 ETH
168783942023-03-21 20:30:59658 days ago1679430659
0x429302C7...9dEC4D050
0.01742385 ETH
168783942023-03-21 20:30:59658 days ago1679430659
0x429302C7...9dEC4D050
0.01742385 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
IndexedNarwhalRouter

Compiler Version
v0.7.6+commit.7338295f

Optimization Enabled:
Yes with 800 runs

Other Settings:
default evmVersion, MIT license
File 1 of 13 : IndexedNarwhalRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
pragma abicoder v2;

import "./NarwhalRouter.sol";
import "./BMath.sol";
import "./interfaces/IIndexPool.sol";
import "./interfaces/IERC20.sol";
import "./libraries/TransferHelper.sol";


contract IndexedNarwhalRouter is NarwhalRouter, BMath {
  using TokenInfo for bytes32;
  using TokenInfo for address;
  using TransferHelper for address;
  using SafeMath for uint256;

  constructor(
    address _uniswapFactory,
    address _sushiswapFactory,
    address _weth
  ) NarwhalRouter(_uniswapFactory, _sushiswapFactory, _weth) {}

/** ========== Mint Single: Exact In ========== */

  /**
   * @dev Swaps ether for each token in `path` using their Uniswap pairs,
   * then mints at least `minPoolAmountOut` pool tokens from `indexPool`.
   *
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param indexPool Address of the index pool to mint tokens from.
   * @param minPoolAmountOut Amount of pool tokens that must be received to not revert.
   */
  function swapExactETHForTokensAndMint(
    bytes32[] calldata path,
    address indexPool,
    uint minPoolAmountOut
  ) external payable returns (uint poolAmountOut) {
    require(path[0].readToken() == address(weth), "NRouter: INVALID_PATH");
    uint256[] memory amounts = getAmountsOut(path, msg.value);

    weth.deposit{value: amounts[0]}();
    address(weth).safeTransfer(pairFor(path[0], path[1]), amounts[0]);
    _swap(amounts, path, address(this));

    uint amountOut =  amounts[amounts.length - 1];
    return _mintExactIn(
      path[path.length - 1].readToken(),
      amountOut,
      indexPool,
      minPoolAmountOut
    );
  }

  /**
   * @dev Swaps a token for each other token in `path` using their Uniswap pairs,
   * then mints at least `minPoolAmountOut` pool tokens from `indexPool`.
   *
   * @param amountIn Amount of the first token in `path` to swap.
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param indexPool Address of the index pool to mint tokens from.
   * @param minPoolAmountOut Amount of pool tokens that must be received to not revert.
   */
  function swapExactTokensForTokensAndMint(
    uint amountIn,
    bytes32[] calldata path,
    address indexPool,
    uint minPoolAmountOut
  ) external returns (uint poolAmountOut) {
    uint256[] memory amounts = getAmountsOut(path, amountIn);
    path[0].readToken().safeTransferFrom(
      msg.sender, pairFor(path[0], path[1]), amounts[0]
    );
    _swap(amounts, path, address(this));
    uint amountOut = amounts[amounts.length - 1];

    return _mintExactIn(
      path[path.length - 1].readToken(),
      amountOut,
      indexPool,
      minPoolAmountOut
    );
  }

  function _mintExactIn(
    address tokenIn,
    uint amountIn,
    address indexPool,
    uint minPoolAmountOut
  ) internal returns (uint poolAmountOut) {
    TransferHelper.safeApprove(tokenIn, indexPool, amountIn);
    poolAmountOut = IIndexPool(indexPool).joinswapExternAmountIn(
      tokenIn,
      amountIn,
      minPoolAmountOut
    );
    TransferHelper.safeTransfer(indexPool, msg.sender, poolAmountOut);
  }

/** ========== Burn Single: Exact In ========== */


  /**
   * @dev Redeems `poolAmountIn` pool tokens for the first token in `path`
   * and swaps it to at least `minAmountOut` of the last token in `path`.
   *
   * @param indexPool Address of the index pool to burn tokens from.
   * @param poolAmountIn Amount of pool tokens to burn.
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param minAmountOut Amount of last token in `path` that must be received to not revert.
   * @return amountOut Amount of output tokens received.
   */
  function burnExactAndSwapForTokens(
    address indexPool,
    uint poolAmountIn,
    bytes32[] calldata path,
    uint minAmountOut
  ) external returns (uint amountOut) {
    amountOut = _burnExactAndSwap(
      indexPool,
      poolAmountIn,
      path,
      minAmountOut,
      msg.sender
    );
  }

  /**
   * @dev Redeems `poolAmountIn` pool tokens for the first token in `path`
   * and swaps it to at least `minAmountOut` ether.
   *
   * @param indexPool Address of the index pool to burn tokens from.
   * @param poolAmountIn Amount of pool tokens to burn.
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param minAmountOut Amount of ether that must be received to not revert.
   * @return amountOut Amount of ether received.
   */
  function burnExactAndSwapForETH(
    address indexPool,
    uint poolAmountIn,
    bytes32[] calldata path,
    uint minAmountOut
  ) external returns (uint amountOut) {
    require(path[path.length - 1].readToken() == address(weth), "NRouter: INVALID_PATH");
    amountOut = _burnExactAndSwap(
      indexPool,
      poolAmountIn,
      path,
      minAmountOut,
      address(this)
    );
    IWETH(weth).withdraw(amountOut);
    TransferHelper.safeTransferETH(msg.sender, amountOut);
  }

  function _burnExactAndSwap(
    address indexPool,
    uint poolAmountIn,
    bytes32[] memory path,
    uint minAmountOut,
    address recipient
  ) internal returns (uint amountOut) {
    // Transfer the pool tokens to the router.
    TransferHelper.safeTransferFrom(
      indexPool,
      msg.sender,
      address(this),
      poolAmountIn
    );
    // Burn the pool tokens for the first token in `path`.
    uint redeemedAmountOut = IIndexPool(indexPool).exitswapPoolAmountIn(
      path[0].readToken(),
      poolAmountIn,
      0
    );
    // Calculate the swap amounts for the redeemed amount of the first token in `path`.
    uint[] memory amounts = getAmountsOut(path, redeemedAmountOut);
    amountOut = amounts[amounts.length - 1];
    require(amountOut >= minAmountOut, "NRouter: MIN_OUT");
    // Transfer the redeemed tokens to the first Uniswap pair.
    TransferHelper.safeTransfer(
      path[0].readToken(),
      pairFor(path[0], path[1]),
      amounts[0]
    );
    // Execute the routed swaps and send the output tokens to `recipient`.
    _swap(amounts, path, recipient);
  }

/** ========== Mint Single: Exact Out ========== */

  /**
   * @dev Swaps ether for each token in `path` through Uniswap,
   * then mints `poolAmountOut` pool tokens from `indexPool`.
   *
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param indexPool Address of the index pool to mint tokens from.
   * @param poolAmountOut Amount of pool tokens that must be received to not revert.
   */
  function swapETHForTokensAndMintExact(
    bytes32[] calldata path,
    address indexPool,
    uint poolAmountOut
  ) external payable {
    address swapTokenOut = path[path.length - 1].readToken();
    uint amountOut = _tokenInGivenPoolOut(indexPool, swapTokenOut, poolAmountOut);
    require(path[0].readToken() == address(weth), "INVALID_PATH");

    uint[] memory amounts = getAmountsIn(path, amountOut);
    require(amounts[0] <= msg.value, "NRouter: MAX_IN");

    weth.deposit{value: amounts[0]}();
    address(weth).safeTransfer(pairFor(path[0], path[1]), amounts[0]);
    _swap(amounts, path, address(this));

    // refund dust eth, if any
    if (msg.value > amounts[0]) {
      TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }

    return _mintExactOut(
      swapTokenOut,
      amountOut,
      indexPool,
      poolAmountOut
    );
  }

  /**
   * @dev Swaps a token for each other token in `path` through Uniswap,
   * then mints at least `poolAmountOut` pool tokens from `indexPool`.
   *
   * @param amountInMax Maximum amount of the first token in `path` to give.
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param indexPool Address of the index pool to mint tokens from.
   * @param poolAmountOut Amount of pool tokens that must be received to not revert.
   */
  function swapTokensForTokensAndMintExact(
    uint amountInMax,
    bytes32[] calldata path,
    address indexPool,
    uint poolAmountOut
  ) external {
    address swapTokenOut = path[path.length - 1].readToken();
    uint amountOut = _tokenInGivenPoolOut(indexPool, swapTokenOut, poolAmountOut);
    uint[] memory amounts = getAmountsIn(path, amountOut);
    require(amounts[0] <= amountInMax, "NRouter: MAX_IN");
    path[0].readToken().safeTransferFrom(
      msg.sender, pairFor(path[0], path[1]), amounts[0]
    );
    _swap(amounts, path, address(this));
    _mintExactOut(
      swapTokenOut,
      amountOut,
      indexPool,
      poolAmountOut
    );
  }

  function _mintExactOut(
    address tokenIn,
    uint amountIn,
    address indexPool,
    uint poolAmountOut
  ) internal {
    TransferHelper.safeApprove(tokenIn, indexPool, amountIn);
    IIndexPool(indexPool).joinswapPoolAmountOut(
      tokenIn,
      poolAmountOut,
      amountIn
    );
    TransferHelper.safeTransfer(indexPool, msg.sender, poolAmountOut);
  }

  function _tokenInGivenPoolOut(
    address indexPool,
    address tokenIn,
    uint256 poolAmountOut
  ) internal view returns (uint256 amountIn) {
    IIndexPool.Record memory record = IIndexPool(indexPool).getTokenRecord(tokenIn);
    if (!record.ready) {
      uint256 minimumBalance = IIndexPool(indexPool).getMinimumBalance(tokenIn);
      uint256 realToMinRatio = bdiv(
        bsub(minimumBalance, record.balance),
        minimumBalance
      );
      uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
      record.balance = minimumBalance;
      record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
    }

    uint256 totalSupply = IERC20(indexPool).totalSupply();
    uint256 totalWeight = IIndexPool(indexPool).getTotalDenormalizedWeight();
    uint256 swapFee = IIndexPool(indexPool).getSwapFee();

    return calcSingleInGivenPoolOut(
      record.balance,
      record.denorm,
      totalSupply,
      totalWeight,
      poolAmountOut,
      swapFee
    );
  }

/** ========== Burn Single: Exact Out ========== */

  /**
   * @dev Redeems up to `poolAmountInMax` pool tokens for the first token in `path`
   * and swaps it to exactly `tokenAmountOut` of the last token in `path`.
   *
   * @param indexPool Address of the index pool to burn tokens from.
   * @param poolAmountInMax Maximum amount of pool tokens to burn.
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param tokenAmountOut Amount of last token in `path` to receive.
   * @return poolAmountIn Amount of pool tokens burned.
   */
  function burnAndSwapForExactTokens(
    address indexPool,
    uint poolAmountInMax,
    bytes32[] calldata path,
    uint tokenAmountOut
  ) external returns (uint poolAmountIn) {
    poolAmountIn = _burnAndSwapForExact(
      indexPool,
      poolAmountInMax,
      path,
      tokenAmountOut,
      msg.sender
    );
  }

  /**
   * @dev Redeems up to `poolAmountInMax` pool tokens for the first token in `path`
   * and swaps it to exactly `ethAmountOut` ether.
   *
   * @param indexPool Address of the index pool to burn tokens from.
   * @param poolAmountInMax Maximum amount of pool tokens to burn.
   * @param path Array of encoded tokens to swap using the Narwhal router.
   * @param ethAmountOut Amount of eth to receive.
   * @return poolAmountIn Amount of pool tokens burned.
   */
  function burnAndSwapForExactETH(
    address indexPool,
    uint poolAmountInMax,
    bytes32[] calldata path,
    uint ethAmountOut
  ) external returns (uint poolAmountIn) {
    require(path[path.length - 1].readToken() == address(weth), "NRouter: INVALID_PATH");
    poolAmountIn = _burnAndSwapForExact(
      indexPool,
      poolAmountInMax,
      path,
      ethAmountOut,
      address(this)
    );
    IWETH(weth).withdraw(ethAmountOut);
    TransferHelper.safeTransferETH(msg.sender, ethAmountOut);
  }

  function _burnAndSwapForExact(
    address indexPool,
    uint poolAmountInMax,
    bytes32[] memory path,
    uint tokenAmountOut,
    address recipient
  ) internal returns (uint poolAmountIn) {
    // Transfer the maximum pool tokens to the router.
    indexPool.safeTransferFrom(
      msg.sender,
      address(this),
      poolAmountInMax
    );
    // Calculate the swap amounts for `tokenAmountOut` of the last token in `path`.
    uint[] memory amounts = getAmountsIn(path, tokenAmountOut);
    // Burn the pool tokens for the exact amount of the first token in `path`.
    poolAmountIn = IIndexPool(indexPool).exitswapExternAmountOut(
      path[0].readToken(),
      amounts[0],
      poolAmountInMax
    );
    // Transfer the redeemed tokens to the first Uniswap pair.
    TransferHelper.safeTransfer(
      path[0].readToken(),
      pairFor(path[0], path[1]),
      amounts[0]
    );
    // Execute the routed swaps and send the output tokens to `recipient`.
    _swap(amounts, path, recipient);
    // Return any unburned pool tokens to the caller.
    indexPool.safeTransfer(
      msg.sender,
      poolAmountInMax.sub(poolAmountIn)
    );
  }

/** ========== Mint All: Exact Out ========== */

  /**
   * @dev Swaps an input token for every underlying token in an index pool,
   * then mints `poolAmountOut` pool tokens from the pool.
   *
   * Up to one intermediary token may be provided in `intermediaries` for each
   * underlying token in the index pool.
   *
   * If a null address is provided as an intermediary, the input token will be
   * swapped directly for the output token.
   *
   * `intermediaries` is an encoded Narwhal path with a one-byte prefix indicating
   * whether the first swap should use sushiswap.
   *
   * @param indexPool Address of the index pool to mint tokens with.
   * @param intermediaries Encoded Narwhal tokens array with a one-byte prefix
   * indicating whether the swap to the underlying token should use sushiswap.
   * @param poolAmountOut Amount of index pool tokens to mint.
   * @param tokenIn Token to buy the underlying tokens with.
   * @param amountInMax Maximumm amount of `tokenIn` to spend.
   * @return Amount of `tokenIn` spent.
   */
  function swapTokensForAllTokensAndMintExact(
    address indexPool,
    bytes32[] calldata intermediaries,
    uint256 poolAmountOut,
    address tokenIn,
    uint256 amountInMax
  ) external returns (uint256) {
    uint256 remainder = amountInMax;
    address[] memory tokens = IIndexPool(indexPool).getCurrentTokens();
    require(
      tokens.length == intermediaries.length,
      "NRouter: ARR_LEN"
    );
    tokenIn.safeTransferFrom(msg.sender, address(this), amountInMax);
    uint256[] memory amountsToPool = new uint256[](tokens.length);

    uint256 ratio = bdiv(poolAmountOut, IERC20(indexPool).totalSupply());

    // Reserve 3 slots in memory for the addresses
    bytes32[] memory path = new bytes32[](3);
    path[0] = tokenIn.pack(false);
    for (uint256 i = 0; i < tokens.length; i++) {
      (amountsToPool[i], remainder) = _handleMintInput(
        indexPool,
        intermediaries[i],
        tokens[i],
        path,
        ratio,
        remainder
      );
    }
    IIndexPool(indexPool).joinPool(poolAmountOut, amountsToPool);
    TransferHelper.safeTransfer(indexPool, msg.sender, poolAmountOut);
    if (remainder > 0) {
      tokenIn.safeTransfer(msg.sender, remainder);
    }
    return amountInMax.sub(remainder);
  }

  /**
   * @dev Swaps ether for every underlying token in an index pool,
   * then mints `poolAmountOut` pool tokens from the pool.
   *
   * Up to one intermediary token may be provided in `intermediaries` for each
   * underlying token in the index pool.
   *
   * If a null address is provided as an intermediary, the input token will be
   * swapped directly for the output token.
   *
   * `intermediaries` is an encoded Narwhal path with a one-byte prefix indicating
   * whether the first swap should use sushiswap.
   *
   * @param indexPool Address of the index pool to mint tokens with.
   * @param intermediaries Encoded Narwhal tokens array with a one-byte prefix
   * indicating whether the swap to the underlying token should use sushiswap.
   * @param poolAmountOut Amount of index pool tokens to mint.
   * @return Amount of ether spent.
   */
  function swapETHForAllTokensAndMintExact(
    address indexPool,
    bytes32[] calldata intermediaries,
    uint256 poolAmountOut
  ) external payable returns (uint) {
    uint256 remainder = msg.value;
    IWETH(weth).deposit{value: msg.value}();
    address[] memory tokens = IIndexPool(indexPool).getCurrentTokens();
    require(tokens.length == intermediaries.length, "NRouter: ARR_LEN");
    uint256[] memory amountsToPool = new uint256[](tokens.length);

    uint256 ratio = bdiv(poolAmountOut, IERC20(indexPool).totalSupply());

    // Reserve 3 slots in memory for the addresses
    bytes32[] memory path = new bytes32[](3);
    path[0] = address(weth).pack(false);

    for (uint256 i = 0; i < tokens.length; i++) {
      (amountsToPool[i], remainder) = _handleMintInput(
        indexPool,
        intermediaries[i],
        tokens[i],
        path,
        ratio,
        remainder
      );
    }
    IIndexPool(indexPool).joinPool(poolAmountOut, amountsToPool);
    TransferHelper.safeTransfer(indexPool, msg.sender, poolAmountOut);

    if (remainder > 0) {
      IWETH(weth).withdraw(remainder);
      TransferHelper.safeTransferETH(msg.sender, remainder);
    }
    return msg.value.sub(remainder);
  }

  function _handleMintInput(
    address indexPool,
    bytes32 intermediate,
    address poolToken,
    bytes32[] memory path,
    uint256 poolRatio,
    uint256 amountInMax
  ) internal returns (uint256 amountToPool, uint256 remainder) {
    address tokenIn = path[0].readToken();
    uint256 usedBalance = IIndexPool(indexPool).getUsedBalance(poolToken);
    amountToPool = bmul(poolRatio, usedBalance);
    if (tokenIn == poolToken) {
      remainder = amountInMax.sub(amountToPool, "NRouter: MAX_IN");
    } else {
      bool sushiFirst;
      assembly {
        sushiFirst := shr(168,  intermediate)
        intermediate := and(
          0x0000000000000000000000ffffffffffffffffffffffffffffffffffffffffff,
          intermediate
        )
      }
      path[0] = tokenIn.pack(sushiFirst);
      if (intermediate == bytes32(0)) {
        // If no intermediate token is given, set path length to 2 so the other
        // functions will not use the 3rd address.
        assembly { mstore(path, 2) }
        // It doesn't matter whether a token is set to use sushi or not
        // if it is the last token in the list.
        path[1] = poolToken.pack(false);
      } else {
        // If an intermediary is given, set path length to 3 so the other
        // functions will use all addresses.
        assembly { mstore(path, 3) }
        path[1] = intermediate;
        path[2] = poolToken.pack(false);
      }
      uint[] memory amounts = getAmountsIn(path, amountToPool);
      remainder = amountInMax.sub(amounts[0], "NRouter: MAX_IN");
      tokenIn.safeTransfer(pairFor(path[0], path[1]), amounts[0]);
      _swap(amounts, path, address(this));
    }
    poolToken.safeApprove(indexPool, amountToPool);
  }

/** ========== Burn All: Exact In ========== */

  /**
   * @dev Burns `poolAmountOut` for all the underlying tokens in a pool, then
   * swaps each of them on Uniswap for at least `minAmountOut` of `tokenOut`.
   *
   * Up to one intermediary token may be provided in `intermediaries` for each
   * underlying token in the index pool.
   *
   * If a null address is provided as an intermediary, the input token will be
   * swapped directly for the output token.
   *
   * @param indexPool Address of the index pool to burn tokens from.
   * @param minAmountsOut Minimum amount of each underlying token that must be
   * received from the pool to not revert.
   * @param intermediaries Encoded Narwhal tokens array with a one-byte prefix
   * indicating whether the swap to the underlying token should use sushiswap.
   * @param poolAmountIn Amount of index pool tokens to burn.
   * @param tokenOut Address of the token to buy.
   * @param minAmountOut Minimum amount of `tokenOut` that must be received to
   * not revert.
   * @return amountOutTotal Amount of `tokenOut` received.
   */
  function burnForAllTokensAndSwapForTokens(
    address indexPool,
    uint256[] calldata minAmountsOut,
    bytes32[] calldata intermediaries,
    uint256 poolAmountIn,
    address tokenOut,
    uint256 minAmountOut
  ) external returns (uint256 amountOutTotal) {
    amountOutTotal = _burnForAllTokensAndSwap(
      indexPool,
      tokenOut,
      minAmountsOut,
      intermediaries,
      poolAmountIn,
      minAmountOut,
      msg.sender
    );
  }

  /**
   * @dev Burns `poolAmountOut` for all the underlying tokens in a pool, then
   * swaps each of them on Uniswap for at least `minAmountOut` ether.
   *
   * Up to one intermediary token may be provided in `intermediaries` for each
   * underlying token in the index pool.
   *
   * If a null address is provided as an intermediary, the input token will be
   * swapped directly for the output token.
   *
   * @param indexPool Address of the index pool to burn tokens from.
   * @param minAmountsOut Minimum amount of each underlying token that must be
   * received from the pool to not revert.
   * @param intermediaries Encoded Narwhal tokens array with a one-byte prefix
   * indicating whether the swap to the underlying token should use sushiswap.
   * @param poolAmountIn Amount of index pool tokens to burn.
   * @param minAmountOut Minimum amount of ether that must be received to
   * not revert.
   * @return amountOutTotal Amount of ether received.
   */
  function burnForAllTokensAndSwapForETH(
    address indexPool,
    uint256[] calldata minAmountsOut,
    bytes32[] calldata intermediaries,
    uint256 poolAmountIn,
    uint256 minAmountOut
  ) external returns (uint amountOutTotal) {
    amountOutTotal = _burnForAllTokensAndSwap(
      indexPool,
      address(weth),
      minAmountsOut,
      intermediaries,
      poolAmountIn,
      minAmountOut,
      address(this)
    );
    IWETH(weth).withdraw(amountOutTotal);
    TransferHelper.safeTransferETH(msg.sender, amountOutTotal);
  }

  function _burnForAllTokensAndSwap(
    address indexPool,
    address tokenOut,
    uint256[] calldata minAmountsOut,
    bytes32[] calldata intermediaries,
    uint256 poolAmountIn,
    uint256 minAmountOut,
    address recipient
  ) internal returns (uint amountOutTotal) {
    // Transfer the pool tokens from the caller.
    TransferHelper.safeTransferFrom(indexPool, msg.sender, address(this), poolAmountIn);
    address[] memory tokens = IIndexPool(indexPool).getCurrentTokens();
    require(
      intermediaries.length == tokens.length && minAmountsOut.length == tokens.length,
      "IndexedUniswapRouterBurner: BAD_ARRAY_LENGTH"
    );
    IIndexPool(indexPool).exitPool(poolAmountIn, minAmountsOut);
    // Reserve 3 slots in memory for the addresses
    bytes32[] memory path = new bytes32[](3);

    for (uint256 i = 0; i < tokens.length; i++) {
      uint amountOut = _handleBurnOutput(
        tokens[i],
        intermediaries[i],
        tokenOut,
        path,
        recipient
      );
      amountOutTotal = amountOutTotal.add(amountOut);
    }
    require(amountOutTotal >= minAmountOut, "NRouter: MIN_OUT");
  }

  function _handleBurnOutput(
    address tokenIn,
    bytes32 intermediate,
    address tokenOut,
    bytes32[] memory path,
    address recipient
  ) internal returns (uint amountOut) {
    uint256 _balance = IERC20(tokenIn).balanceOf(address(this));
    if (tokenIn == tokenOut) {
      amountOut = _balance;
      if (recipient != address(this)) {
        tokenIn.safeTransfer(recipient, _balance);
      }
    } else {
      bool sushiFirst;
      assembly {
        sushiFirst := shr(168,  intermediate)
        intermediate := and(
          0x0000000000000000000000ffffffffffffffffffffffffffffffffffffffffff,
          intermediate
        )
      }
      path[0] = tokenIn.pack(sushiFirst);
      if (intermediate == bytes32(0)) {
        // If no intermediate token is given, set path length to 2 so the other
        // functions will not use the 3rd address.
        assembly { mstore(path, 2) }
        // It doesn't matter whether a token is set to use sushi or not
        // if it is the last token in the list.
        path[1] = tokenOut.pack(false);
      } else {
        // If an intermediary is given, set path length to 3 so the other
        // functions will use all addresses.
        assembly { mstore(path, 3) }
        path[1] = intermediate;
        path[2] = tokenOut.pack(false);
      }
      uint[] memory amounts = getAmountsOut(path, _balance);
      tokenIn.safeTransfer(pairFor(path[0], path[1]), amounts[0]);
      _swap(amounts, path, recipient);
      amountOut = amounts[amounts.length - 1];
    }
  }
}

File 2 of 13 : BMath.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0;

import "./BNum.sol";


contract BMath is BNum {
  function calcSingleInGivenPoolOut(
    uint256 tokenBalanceIn,
    uint256 tokenWeightIn,
    uint256 poolSupply,
    uint256 totalWeight,
    uint256 poolAmountOut,
    uint256 swapFee
  ) internal pure returns (uint256 tokenAmountIn) {
    uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
    uint256 newPoolSupply = badd(poolSupply, poolAmountOut);
    uint256 poolRatio = bdiv(newPoolSupply, poolSupply);

    //uint newBalTi = poolRatio^(1/weightTi) * balTi;
    uint256 boo = bdiv(BONE, normalizedWeight);
    uint256 tokenInRatio = bpow(poolRatio, boo);
    uint256 newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
    uint256 tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);
    // Do reverse order of fees charged in joinswap_ExternAmountIn, this way
    //     ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```
    //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;
    uint256 zar = bmul(bsub(BONE, normalizedWeight), swapFee);
    tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));
    return tokenAmountIn;
  }
}

File 3 of 13 : BNum.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0;


contract BNum {
  uint256 internal constant BONE = 1e18;
  uint256 internal constant MIN_BPOW_BASE = 1 wei;
  uint256 internal constant MAX_BPOW_BASE = (2 * BONE) - 1 wei;
  uint256 internal constant BPOW_PRECISION = BONE / 10**10;
  uint256 internal constant MIN_WEIGHT = BONE / 4;

  function btoi(uint256 a) internal pure returns (uint256) {
    return a / BONE;
  }

  function bfloor(uint256 a) internal pure returns (uint256) {
    return btoi(a) * BONE;
  }

  function badd(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    require(c >= a, "ERR_ADD_OVERFLOW");
    return c;
  }

  function bsub(uint256 a, uint256 b) internal pure returns (uint256) {
    (uint256 c, bool flag) = bsubSign(a, b);
    require(!flag, "ERR_SUB_UNDERFLOW");
    return c;
  }

  function bsubSign(uint256 a, uint256 b)
    internal
    pure
    returns (uint256, bool)
  {
    if (a >= b) {
      return (a - b, false);
    } else {
      return (b - a, true);
    }
  }

  function bmul(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c0 = a * b;
    require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW");
    uint256 c1 = c0 + (BONE / 2);
    require(c1 >= c0, "ERR_MUL_OVERFLOW");
    uint256 c2 = c1 / BONE;
    return c2;
  }

  function bdiv(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0, "ERR_DIV_ZERO");
    uint256 c0 = a * BONE;
    require(a == 0 || c0 / a == BONE, "ERR_DIV_INTERNAL"); // bmul overflow
    uint256 c1 = c0 + (b / 2);
    require(c1 >= c0, "ERR_DIV_INTERNAL"); //  badd require
    uint256 c2 = c1 / b;
    return c2;
  }

  // DSMath.wpow
  function bpowi(uint256 a, uint256 n) internal pure returns (uint256) {
    uint256 z = n % 2 != 0 ? a : BONE;

    for (n /= 2; n != 0; n /= 2) {
      a = bmul(a, a);

      if (n % 2 != 0) {
        z = bmul(z, a);
      }
    }
    return z;
  }

  // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
  // Use `bpowi` for `b^e` and `bpowK` for k iterations
  // of approximation of b^0.w
  function bpow(uint256 base, uint256 exp) internal pure returns (uint256) {
    require(base >= MIN_BPOW_BASE, "ERR_BPOW_BASE_TOO_LOW");
    require(base <= MAX_BPOW_BASE, "ERR_BPOW_BASE_TOO_HIGH");

    uint256 whole = bfloor(exp);
    uint256 remain = bsub(exp, whole);

    uint256 wholePow = bpowi(base, btoi(whole));

    if (remain == 0) {
      return wholePow;
    }

    uint256 partialResult = bpowApprox(base, remain, BPOW_PRECISION);
    return bmul(wholePow, partialResult);
  }

  function bpowApprox(
    uint256 base,
    uint256 exp,
    uint256 precision
  ) internal pure returns (uint256) {
    // term 0:
    uint256 a = exp;
    (uint256 x, bool xneg) = bsubSign(base, BONE);
    uint256 term = BONE;
    uint256 sum = term;
    bool negative = false;

    // term(k) = numer / denom
    //         = (product(a - i - 1, i=1-->k) * x^k) / (k!)
    // each iteration, multiply previous term by (a-(k-1)) * x / k
    // continue until term is less than precision
    for (uint256 i = 1; term >= precision; i++) {
      uint256 bigK = i * BONE;
      (uint256 c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
      term = bmul(term, bmul(c, x));
      term = bdiv(term, bigK);
      if (term == 0) break;

      if (xneg) negative = !negative;
      if (cneg) negative = !negative;
      if (negative) {
        sum = bsub(sum, term);
      } else {
        sum = badd(sum, term);
      }
    }

    return sum;
  }
}

File 4 of 13 : Narwhal.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "./interfaces/IUniswapV2Pair.sol";
import "./interfaces/IWETH.sol";
import "./libraries/SafeMath.sol";
import "./libraries/TokenInfo.sol";


contract Narwhal {
  using SafeMath for uint256;
  using TokenInfo for bytes32;

  address public immutable uniswapFactory;
  address public immutable sushiswapFactory;
  IWETH public immutable weth;

/** ========== Constructor ========== */

  constructor(
    address _uniswapFactory,
    address _sushiswapFactory,
    address _weth
  ) {
    uniswapFactory = _uniswapFactory;
    sushiswapFactory = _sushiswapFactory;
    weth = IWETH(_weth);
  }

/** ========== Fallback ========== */

  receive() external payable {
    assert(msg.sender == address(weth)); // only accept ETH via fallback from the WETH contract
  }

/** ========== Swaps ========== */

  // requires the initial amount to have already been sent to the first pair
  function _swap(uint[] memory amounts, bytes32[] memory path, address recipient) internal {
    for (uint i; i < path.length - 1; i++) {
      (bytes32 input, bytes32 output) = (path[i], path[i + 1]);
      uint amountOut = amounts[i + 1];
      (uint amount0Out, uint amount1Out) = (input < output) ? (uint(0), amountOut) : (amountOut, uint(0));
      address to = i < path.length - 2 ? pairFor(output, path[i + 2]) : recipient;
      IUniswapV2Pair(pairFor(input, output)).swap(
        amount0Out, amount1Out, to, new bytes(0)
      );
    }
  }

/** ========== Pair Calculation & Sorting ========== */

  // returns sorted token addresses, used to handle return values from pairs sorted in this order
  function sortTokens(address tokenA, address tokenB)
    internal
    pure
    returns (address token0, address token1)
  {
    require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
  }

  function zeroForOne(bytes32 tokenA, bytes32 tokenB) internal pure returns (bool) {
    return tokenA < tokenB;
  }

  // returns sorted token addresses, used to handle return values from pairs sorted in this order
  function sortTokens(bytes32 tokenA, bytes32 tokenB)
    internal
    pure
    returns (bytes32 token0, bytes32 token1)
  {
    require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    require(token0 != bytes32(0), "UniswapV2Library: ZERO_ADDRESS");
  }

  function calculateUniPair(address token0, address token1 ) internal view returns (address pair) {
    pair = address(
      uint256(
        keccak256(
          abi.encodePacked(
            hex"ff",
            uniswapFactory,
            keccak256(abi.encodePacked(token0, token1)),
            hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f" // init code hash
          )
        )
      )
    );
  }

  function calculateSushiPair(address token0, address token1) internal view returns (address pair) {
    pair = address(
      uint256(
        keccak256(
          abi.encodePacked(
            hex"ff",
            sushiswapFactory,
            keccak256(abi.encodePacked(token0, token1)),
            hex"e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" // init code hash
          )
        )
      )
    );
  }

  // calculates the CREATE2 address for a pair without making any external calls
  function pairFor(
    address tokenA,
    address tokenB,
    bool sushi
  ) internal view returns (address pair) {
    (address token0, address token1) = sortTokens(tokenA, tokenB);
    pair = sushi ? calculateSushiPair(token0, token1) : calculateUniPair(token0, token1);
  }

  // calculates the CREATE2 address for a pair without making any external calls
  function pairFor(bytes32 tokenInfoA, bytes32 tokenInfoB) internal view returns (address pair) {
    (address tokenA, bool sushi) = tokenInfoA.unpack();
    address tokenB = tokenInfoB.readToken();
    (address token0, address token1) = sortTokens(tokenA, tokenB);
    pair = sushi ? calculateSushiPair(token0, token1) : calculateUniPair(token0, token1);
  }

/** ========== Pair Reserves ========== */

  // fetches and sorts the reserves for a pair
  function getReserves(
    bytes32 tokenInfoA,
    bytes32 tokenInfoB
  ) internal view returns (uint256 reserveA, uint256 reserveB) {
    (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(tokenInfoA, tokenInfoB)).getReserves();
    (reserveA, reserveB) = tokenInfoA < tokenInfoB
      ? (reserve0, reserve1)
      : (reserve1, reserve0);
  }

/** ========== Swap Amounts ========== */

  // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
  function getAmountOut(
    uint256 amountIn,
    uint256 reserveIn,
    uint256 reserveOut
  ) internal pure returns (uint256 amountOut) {
    require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
    require(
      reserveIn > 0 && reserveOut > 0,
      "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
    );
    uint256 amountInWithFee = amountIn.mul(997);
    uint256 numerator = amountInWithFee.mul(reserveOut);
    uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
    amountOut = numerator / denominator;
  }

  // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
  function getAmountIn(
    uint256 amountOut,
    uint256 reserveIn,
    uint256 reserveOut
  ) internal pure returns (uint256 amountIn) {
    require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
    require(
      reserveIn > 0 && reserveOut > 0,
      "UniswapV2Library: INSUFFICIENT_LIQUIDITY"
    );
    uint256 numerator = reserveIn.mul(amountOut).mul(1000);
    uint256 denominator = reserveOut.sub(amountOut).mul(997);
    amountIn = (numerator / denominator).add(1);
  }

  // performs chained getAmountOut calculations on any number of pairs
  function getAmountsOut(
    bytes32[] memory path,
    uint256 amountIn
  ) internal view returns (uint256[] memory amounts) {
    require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
    amounts = new uint[](path.length);
    amounts[0] = amountIn;
    for (uint i; i < path.length - 1; i++) {
      (uint reserveIn, uint reserveOut) = getReserves(path[i], path[i + 1]);
      amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
    }
  }

  // performs chained getAmountIn calculations on any number of pairs
  function getAmountsIn(
    bytes32[] memory path,
    uint256 amountOut
  ) internal view returns (uint256[] memory amounts) {
    require(path.length >= 2, "UniswapV2Library: INVALID_PATH");
    amounts = new uint256[](path.length);
    amounts[amounts.length - 1] = amountOut;
    for (uint256 i = path.length - 1; i > 0; i--) {
      (uint256 reserveIn, uint256 reserveOut) = getReserves(path[i - 1], path[i]);
      amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
    }
  }
}

File 5 of 13 : NarwhalRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;

import "./Narwhal.sol";
import "./libraries/TransferHelper.sol";
import "./interfaces/IUniswapV2Factory.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IWETH.sol";


contract NarwhalRouter is Narwhal {
  using TokenInfo for bytes32;
  using TokenInfo for address;
  using TransferHelper for address;
  using SafeMath for uint256;

  modifier ensure(uint256 deadline) {
    require(deadline >= block.timestamp, "NRouter: EXPIRED");
    _;
  }

  constructor(
    address _uniswapFactory,
    address _sushiswapFactory,
    address _weth
  ) Narwhal(_uniswapFactory, _sushiswapFactory, _weth) {}

  function swapExactTokensForTokens(
    uint256 amountIn,
    uint256 amountOutMin,
    bytes32[] calldata path,
    address to,
    uint256 deadline
  ) external ensure(deadline) returns (uint256[] memory amounts) {
    amounts = getAmountsOut(path, amountIn);
    require(amounts[amounts.length - 1] >= amountOutMin, "NRouter: MIN_OUT");
    path[0].readToken().safeTransferFrom(
      msg.sender,
      pairFor(path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, to);
  }

  function swapTokensForExactTokens(
    uint256 amountOut,
    uint256 amountInMax,
    bytes32[] calldata path,
    address to,
    uint256 deadline
  ) external ensure(deadline) returns (uint256[] memory amounts) {
    amounts = getAmountsIn(path, amountOut);
    require(amounts[0] <= amountInMax, "NRouter: MAX_IN");
    path[0].readToken().safeTransferFrom(
      msg.sender,
      pairFor(path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, to);
  }

  function swapExactETHForTokens(
    uint256 amountOutMin,
    bytes32[] calldata path,
    address to,
    uint256 deadline
  ) external payable ensure(deadline) returns (uint256[] memory amounts) {
    require(path[0].readToken() == address(weth), "NRouter: INVALID_PATH");
    amounts = getAmountsOut(path, msg.value);
    require(amounts[amounts.length - 1] >= amountOutMin, "NRouter: MIN_OUT");
    weth.deposit{value: amounts[0]}();
    address(weth).safeTransfer(pairFor(path[0], path[1]), amounts[0]);
    _swap(amounts, path, to);
  }

  function swapTokensForExactETH(
    uint256 amountOut,
    uint256 amountInMax,
    bytes32[] calldata path,
    address to,
    uint256 deadline
  ) external ensure(deadline) returns (uint256[] memory amounts) {
    require(path[path.length - 1].readToken() == address(weth), "NRouter: INVALID_PATH");
    amounts = getAmountsIn(path, amountOut);
    require(amounts[0] <= amountInMax, "NRouter: MAX_IN");
    path[0].readToken().safeTransferFrom(
      msg.sender,
      pairFor(path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, address(this));
    weth.withdraw(amounts[amounts.length - 1]);
    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
  }

  function swapExactTokensForETH(
    uint256 amountIn,
    uint256 amountOutMin,
    bytes32[] calldata path,
    address to,
    uint256 deadline
  ) external ensure(deadline) returns (uint256[] memory amounts) {
    require(path[path.length - 1].readToken() == address(weth), "NRouter: INVALID_PATH");
    amounts = getAmountsOut(path, amountIn);
    require(amounts[amounts.length - 1] >= amountOutMin, "NRouter: MIN_OUT");
    path[0].readToken().safeTransferFrom(
      msg.sender,
      pairFor(path[0], path[1]),
      amounts[0]
    );
    _swap(amounts, path, address(this));
    weth.withdraw(amounts[amounts.length - 1]);
    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
  }

  function swapETHForExactTokens(
    uint256 amountOut,
    bytes32[] calldata path,
    address to,
    uint256 deadline
  ) external payable ensure(deadline) returns (uint256[] memory amounts) {
    require(path[0].readToken() == address(weth), "NRouter: INVALID_PATH");
    amounts = getAmountsIn(path, amountOut);
    require(amounts[0] <= msg.value, "NRouter: MAX_IN");
    weth.deposit{value: amounts[0]}();
    address(weth).safeTransfer(pairFor(path[0], path[1]), amounts[0]);
    _swap(amounts, path, to);
    // // refund dust eth, if any
    if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
  }
}

File 6 of 13 : IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;


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

  function totalSupply() external view returns (uint256);
  function balanceOf(address account) external view returns (uint256);
  function transfer(address recipient, uint256 amount) external returns (bool);
  function allowance(address owner, address spender) external view returns (uint256);
  function approve(address spender, uint256 amount) external returns (bool);
  function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
  function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}

File 7 of 13 : IIndexPool.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0;
pragma experimental ABIEncoderV2;

import "./IERC20.sol";


interface IIndexPool is IERC20 {
  /**
   * @dev Token record data structure
   * @param bound is token bound to pool
   * @param ready has token been initialized
   * @param lastDenormUpdate timestamp of last denorm change
   * @param denorm denormalized weight
   * @param desiredDenorm desired denormalized weight (used for incremental changes)
   * @param index index of address in tokens array
   * @param balance token balance
   */
  struct Record {
    bool bound;
    bool ready;
    uint40 lastDenormUpdate;
    uint96 denorm;
    uint96 desiredDenorm;
    uint8 index;
    uint256 balance;
  }

  event LOG_SWAP(
    address indexed caller,
    address indexed tokenIn,
    address indexed tokenOut,
    uint256 tokenAmountIn,
    uint256 tokenAmountOut
  );

  event LOG_JOIN(
    address indexed caller,
    address indexed tokenIn,
    uint256 tokenAmountIn
  );

  event LOG_EXIT(
    address indexed caller,
    address indexed tokenOut,
    uint256 tokenAmountOut
  );

  event LOG_DENORM_UPDATED(address indexed token, uint256 newDenorm);

  event LOG_DESIRED_DENORM_SET(address indexed token, uint256 desiredDenorm);

  event LOG_TOKEN_REMOVED(address token);

  event LOG_TOKEN_ADDED(
    address indexed token,
    uint256 desiredDenorm,
    uint256 minimumBalance
  );

  event LOG_MINIMUM_BALANCE_UPDATED(address token, uint256 minimumBalance);

  event LOG_TOKEN_READY(address indexed token);

  event LOG_PUBLIC_SWAP_ENABLED();

  event LOG_MAX_TOKENS_UPDATED(uint256 maxPoolTokens);

  event LOG_SWAP_FEE_UPDATED(uint256 swapFee);

  function configure(
    address controller,
    string calldata name,
    string calldata symbol
  ) external;

  function initialize(
    address[] calldata tokens,
    uint256[] calldata balances,
    uint96[] calldata denorms,
    address tokenProvider,
    address unbindHandler,
    address exitFeeRecipient
  ) external;

  function setMaxPoolTokens(uint256 maxPoolTokens) external;

  function setSwapFee(uint256 swapFee) external;

  function delegateCompLikeToken(address token, address delegatee) external;

  function reweighTokens(
    address[] calldata tokens,
    uint96[] calldata desiredDenorms
  ) external;

  function reindexTokens(
    address[] calldata tokens,
    uint96[] calldata desiredDenorms,
    uint256[] calldata minimumBalances
  ) external;

  function setMinimumBalance(address token, uint256 minimumBalance) external;

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

  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 exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external;

  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 gulp(address token) external;

  function flashBorrow(
    address recipient,
    address token,
    uint256 amount,
    bytes calldata data
  ) 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 isPublicSwap() external view returns (bool);

  function getSwapFee() external view returns (uint256/* swapFee */);

  function getController() external view returns (address);

  function getMaxPoolTokens() external view returns (uint256);

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

  function getNumTokens() external view returns (uint256);

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

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

  function getDenormalizedWeight(address token) external view returns (uint256/* denorm */);

  function getTokenRecord(address token) external view returns (Record memory record);

  function extrapolatePoolValueFromToken() external view returns (address/* token */, uint256/* extrapolatedValue */);

  function getTotalDenormalizedWeight() external view returns (uint256);

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

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

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

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

File 8 of 13 : IUniswapV2Factory.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

interface IUniswapV2Factory {
  event PairCreated(address indexed token0, address indexed token1, address pair, uint256);

  function feeTo() external view returns (address);

  function feeToSetter() external view returns (address);

  function migrator() external view returns (address);

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

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

  function allPairsLength() external view returns (uint256);

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

  function setFeeTo(address) external;

  function setFeeToSetter(address) external;

  function setMigrator(address) external;
}

File 9 of 13 : IUniswapV2Pair.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;


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

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

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

  function decimals() external pure returns (uint8);

  function totalSupply() external view returns (uint256);

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

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

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

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

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

  function DOMAIN_SEPARATOR() external view returns (bytes32);

  function PERMIT_TYPEHASH() external pure returns (bytes32);

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

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

  event Mint(address indexed sender, uint256 amount0, uint256 amount1);
  event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
  event Swap(
    address indexed sender,
    uint256 amount0In,
    uint256 amount1In,
    uint256 amount0Out,
    uint256 amount1Out,
    address indexed to
  );
  event Sync(uint112 reserve0, uint112 reserve1);

  function MINIMUM_LIQUIDITY() external pure returns (uint256);

  function factory() external view returns (address);

  function token0() external view returns (address);

  function token1() external view returns (address);

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

  function price0CumulativeLast() external view returns (uint256);

  function price1CumulativeLast() external view returns (uint256);

  function kLast() external view returns (uint256);

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

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

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

  function skim(address to) external;

  function sync() external;

  function initialize(address, address) external;
}

File 10 of 13 : IWETH.sol
pragma solidity >=0.5.0;


interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint value) external returns (bool);
    function withdraw(uint) external;
}

File 11 of 13 : SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;


library SafeMath {
  function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
    require((z = x + y) >= x, "ds-math-add-overflow");
  }

  function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
    require((z = x - y) <= x, "ds-math-sub-underflow");
  }

  function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
    require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
  }


  function add(uint256 x, uint256 y, string memory errorMessage) internal pure returns (uint256 z) {
    require((z = x + y) >= x, errorMessage);
  }

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

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

File 12 of 13 : TokenInfo.sol
pragma solidity >=0.5.0;


library TokenInfo {
  function unpack(bytes32 tokenInfo) internal pure returns (address token, bool useSushiNext) {
    assembly {
      token := shr(8, tokenInfo)
      useSushiNext := byte(31, tokenInfo)
    }
  }

  function pack(address token, bool sushi) internal pure returns (bytes32 tokenInfo) {
    assembly {
      tokenInfo := or(
        shl(8, token),
        sushi
      )
    }
  }

  function readToken(bytes32 tokenInfo) internal pure returns (address token) {
    assembly {
      token := shr(8, tokenInfo)
    }
  }

  function readSushi(bytes32 tokenInfo) internal pure returns (bool useSushiNext) {
    assembly {
      useSushiNext := byte(31, tokenInfo)
    }
  }
}

File 13 of 13 : TransferHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;

/************************************************************************************************
Originally from https://github.com/Uniswap/uniswap-lib/blob/master/contracts/libraries/TransferHelper.sol

This source code has been modified from the original, which was copied from the github repository
at commit hash cfedb1f55864dcf8cc0831fdd8ec18eb045b7fd1.

Subject to the MIT license
*************************************************************************************************/


library TransferHelper {
  function safeApproveMax(address token, address to) internal {
    safeApprove(token, to, type(uint256).max);
  }

  function safeUnapprove(address token, address to) internal {
    safeApprove(token, to, 0);
  }

  function safeApprove(address token, address to, uint value) internal {
    // bytes4(keccak256(bytes("approve(address,uint256)")));
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
    require(success && (data.length == 0 || abi.decode(data, (bool))), "TH:SA");
  }

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

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

  function safeTransferETH(address to, uint256 value) internal {
    (bool success, ) = to.call{value: value}("");
    require(success, "TH:STE");
  }
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "none",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 800
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_uniswapFactory","type":"address"},{"internalType":"address","name":"_sushiswapFactory","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"poolAmountInMax","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"uint256","name":"ethAmountOut","type":"uint256"}],"name":"burnAndSwapForExactETH","outputs":[{"internalType":"uint256","name":"poolAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"poolAmountInMax","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"uint256","name":"tokenAmountOut","type":"uint256"}],"name":"burnAndSwapForExactTokens","outputs":[{"internalType":"uint256","name":"poolAmountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"burnExactAndSwapForETH","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"burnExactAndSwapForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256[]","name":"minAmountsOut","type":"uint256[]"},{"internalType":"bytes32[]","name":"intermediaries","type":"bytes32[]"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"burnForAllTokensAndSwapForETH","outputs":[{"internalType":"uint256","name":"amountOutTotal","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256[]","name":"minAmountsOut","type":"uint256[]"},{"internalType":"bytes32[]","name":"intermediaries","type":"bytes32[]"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"burnForAllTokensAndSwapForTokens","outputs":[{"internalType":"uint256","name":"amountOutTotal","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sushiswapFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"bytes32[]","name":"intermediaries","type":"bytes32[]"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"}],"name":"swapETHForAllTokensAndMintExact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"}],"name":"swapETHForTokensAndMintExact","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"minPoolAmountOut","type":"uint256"}],"name":"swapExactETHForTokensAndMint","outputs":[{"internalType":"uint256","name":"poolAmountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"minPoolAmountOut","type":"uint256"}],"name":"swapExactTokensForTokensAndMint","outputs":[{"internalType":"uint256","name":"poolAmountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"bytes32[]","name":"intermediaries","type":"bytes32[]"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountInMax","type":"uint256"}],"name":"swapTokensForAllTokensAndMintExact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"bytes32[]","name":"path","type":"bytes32[]"},{"internalType":"address","name":"indexPool","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"}],"name":"swapTokensForTokensAndMintExact","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e06040523480156200001157600080fd5b5060405162004dfb38038062004dfb833981016040819052620000349162000079565b6001600160601b0319606093841b811660805291831b821660a05290911b1660c052620000c2565b80516001600160a01b03811681146200007457600080fd5b919050565b6000806000606084860312156200008e578283fd5b62000099846200005c565b9250620000a9602085016200005c565b9150620000b9604085016200005c565b90509250925092565b60805160601c60a05160601c60c05160601c614c8b620001706000398061017e5280610490528061069c52806107c852806108e45280610a185280610ae85280610bf15280610d1d5280610df75280610f4f52806110045280611461528061150352806116535280611698528061171552806117ca528061186352806119395280611be75280611d5d5280611e665250806111cc5280613e045250806116295280613d275250614c8b6000f3fe60806040526004361061016e5760003560e01c8063501070be116100cb57806390ab67311161007f578063b96e9f3311610059578063b96e9f33146103ba578063beb586ae146103da578063e9040a97146103fa576101a7565b806390ab673114610367578063a86952b414610387578063b079cb8c1461039a576101a7565b806367e34f97116100b057806367e34f971461031f5780638bdb2afa146103325780638f32871e14610347576101a7565b8063501070be146102ea57806365526631146102ff576101a7565b80633ddb70281161012257806343405d511161010757806343405d511461028a5780634e4665e9146102aa5780634f553b26146102ca576101a7565b80633ddb7028146102555780633fc8cef314610268576101a7565b8063108a6d6b11610153578063108a6d6b146102025780632064bcbd146102155780633c3db8c114610235576101a7565b80630c05f882146101ac5780630c288791146101e2576101a7565b366101a757336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101a557fe5b005b600080fd5b3480156101b857600080fd5b506101cc6101c7366004614536565b61041a565b6040516101d99190614b22565b60405180910390f35b3480156101ee57600080fd5b506101cc6101fd36600461466b565b61043a565b6101cc610210366004614462565b610488565b34801561022157600080fd5b506101cc61023036600461466b565b610853565b610248610243366004614892565b610897565b6040516101d991906149c2565b610248610263366004614892565b610ba4565b34801561027457600080fd5b5061027d610df5565b6040516101d9919061498d565b34801561029657600080fd5b506101cc6102a5366004614892565b610e19565b3480156102b657600080fd5b506101cc6102c536600461466b565b610f43565b3480156102d657600080fd5b506102486102e53660046148f7565b611075565b3480156102f657600080fd5b5061027d6111ca565b34801561030b57600080fd5b506101cc61031a3660046144bc565b6111ee565b6101cc61032d36600461477a565b61145d565b34801561033e57600080fd5b5061027d611627565b34801561035357600080fd5b506101cc6103623660046145dc565b61164b565b34801561037357600080fd5b506101cc61038236600461466b565b611709565b6101a561039536600461477a565b61183b565b3480156103a657600080fd5b506101a56103b5366004614892565b611a54565b3480156103c657600080fd5b506102486103d53660046148f7565b611b92565b3480156103e657600080fd5b506102486103f53660046148f7565b611e11565b34801561040657600080fd5b506102486104153660046148f7565b611f8b565b600061042d89848a8a8a8a8a8933612071565b9998505050505050505050565b600061047e86868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503391506122329050565b9695505050505050565b6000803490507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156104e957600080fd5b505af11580156104fd573d6000803e3d6000fd5b50505050506000866001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561053d57600080fd5b505afa158015610551573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261057991908101906146cd565b805190915085146105a55760405162461bcd60e51b815260040161059c90614aeb565b60405180910390fd5b6000815167ffffffffffffffff811180156105bf57600080fd5b506040519080825280602002602001820160405280156105e9578160200160208202803683370190505b5090506000610668868a6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561062b57600080fd5b505afa15801561063f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610663919061487a565b612394565b6040805160038082526080820190925291925060009190602082016060803683370190505090506106c36001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660006124ba565b816000815181106106d057fe5b60200260200101818152505060005b845181101561073f5761071b8b8b8b848181106106f857fe5b9050602002013587848151811061070b57fe5b602002602001015185878b6124c4565b85838151811061072757fe5b602090810291909101019190915295506001016106df565b506040516313da703560e21b81526001600160a01b038b1690634f69c0d49061076e908a908790600401614b85565b600060405180830381600087803b15801561078857600080fd5b505af115801561079c573d6000803e3d6000fd5b505050506107ab8a338961275b565b841561083957604051632e1a7d4d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d906107fd908890600401614b22565b600060405180830381600087803b15801561081757600080fd5b505af115801561082b573d6000803e3d6000fd5b5050505061083933866128c4565b610843348661296f565b955050505050505b949350505050565b600061047e86868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503391506129c79050565b606081428110156108e2576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166109288787600081811061091c57fe5b90506020020135612b01565b6001600160a01b03161461097b576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b6109b98686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508b9250612b07915050565b915034826000815181106109c957fe5b60200260200101511115610a16576040805162461bcd60e51b815260206004820152600f60248201526e272937baba32b91d1026a0ac2fa4a760891b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db083600081518110610a5257fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015610a8557600080fd5b505af1158015610a99573d6000803e3d6000fd5b5050505050610b1f610ad187876000818110610ab157fe5b9050602002013588886001818110610ac557fe5b90506020020135612c52565b83600081518110610ade57fe5b60200260200101517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661275b9092919063ffffffff16565b610b5e82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612cac915050565b81600081518110610b6b57fe5b6020026020010151341115610b9a57610b9a3383600081518110610b8b57fe5b602002602001015134036128c4565b5095945050505050565b60608142811015610bef576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610c298787600081811061091c57fe5b6001600160a01b031614610c7c576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b610cba868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250349250612e72915050565b91508682600184510381518110610ccd57fe5b60200260200101511015610d1b576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e8813525397d3d55560821b604482015290519081900360640190fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db083600081518110610d5757fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d8a57600080fd5b505af1158015610d9e573d6000803e3d6000fd5b5050505050610db6610ad187876000818110610ab157fe5b610b9a82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612cac915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600080610e5a8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508b9250612e72915050565b9050610eba33610e8488886000818110610e7057fe5b9050602002013589896001818110610ac557fe5b83600081518110610e9157fe5b6020026020010151610ea98a8a600081811061091c57fe5b6001600160a01b0316929190612fa8565b610ef981878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b600081600183510381518110610f0b57fe5b60200260200101519050610f35610f2d888860018b8b90500381811061091c57fe5b82878761311a565b925050505b95945050505050565b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016610f828585600019810181811061091c57fe5b6001600160a01b031614610fa85760405162461bcd60e51b815260040161059c90614a0c565b610fea86868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503091506122329050565b604051632e1a7d4d60e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90611039908590600401614b22565b600060405180830381600087803b15801561105357600080fd5b505af1158015611067573d6000803e3d6000fd5b50505050610f3a33836128c4565b606081428110156110c0576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6110fe8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612e72915050565b9150868260018451038151811061111157fe5b6020026020010151101561115f576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e8813525397d3d55560821b604482015290519081900360640190fd5b6111803361117388886000818110610e7057fe5b84600081518110610e9157fe5b6111bf82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612cac915050565b509695505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000808290506000886001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561122f57600080fd5b505afa158015611243573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261126b91908101906146cd565b8051909150871461128e5760405162461bcd60e51b815260040161059c90614aeb565b6112a36001600160a01b038616333087612fa8565b6000815167ffffffffffffffff811180156112bd57600080fd5b506040519080825280602002602001820160405280156112e7578160200160208202803683370190505b5090506000611329888c6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561062b57600080fd5b6040805160038082526080820190925291925060009190602082016060803683370190505090506113646001600160a01b03891660006124ba565b8160008151811061137157fe5b60200260200101818152505060005b84518110156113bd576113998d8d8d848181106106f857fe5b8583815181106113a557fe5b60209081029190910101919091529550600101611380565b506040516313da703560e21b81526001600160a01b038d1690634f69c0d4906113ec908c908790600401614b85565b600060405180830381600087803b15801561140657600080fd5b505af115801561141a573d6000803e3d6000fd5b505050506114298c338b61275b565b8415611443576114436001600160a01b038916338761275b565b61144d878661296f565b9c9b505050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166114998686600081811061091c57fe5b6001600160a01b0316146114bf5760405162461bcd60e51b815260040161059c90614a0c565b60006114ff868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250349250612e72915050565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db08260008151811061153d57fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561157057600080fd5b505af1158015611584573d6000803e3d6000fd5b50505050506115a961159c87876000818110610ab157fe5b82600081518110610ade57fe5b6115e881878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b6000816001835103815181106115fa57fe5b6020026020010151905061161c610f2d888860018b8b90500381811061091c57fe5b979650505050505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600061167e887f000000000000000000000000000000000000000000000000000000000000000089898989898930612071565b604051632e1a7d4d60e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d906116cd908490600401614b22565b600060405180830381600087803b1580156116e757600080fd5b505af11580156116fb573d6000803e3d6000fd5b5050505061161c33826128c4565b60006001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166117488585600019810181811061091c57fe5b6001600160a01b03161461176e5760405162461bcd60e51b815260040161059c90614a0c565b6117b086868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503091506129c79050565b604051632e1a7d4d60e01b81529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d906117ff908490600401614b22565b600060405180830381600087803b15801561181957600080fd5b505af115801561182d573d6000803e3d6000fd5b50505050610f3a33826128c4565b60006118508585600019810181811061091c57fe5b9050600061185f8483856131b6565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661189b8787600081811061091c57fe5b6001600160a01b0316146118c15760405162461bcd60e51b815260040161059c906149d5565b6000611901878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612b07915050565b9050348160008151811061191157fe5b602002602001015111156119375760405162461bcd60e51b815260040161059c90614a3b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db08260008151811061197357fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156119a657600080fd5b505af11580156119ba573d6000803e3d6000fd5b50505050506119d261159c88886000818110610e7057fe5b611a1181888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b80600081518110611a1e57fe5b6020026020010151341115611a3e57611a3e3382600081518110610b8b57fe5b611a4a838387876134a4565b5050505b50505050565b6000611a698585600019810181811061091c57fe5b90506000611a788483856131b6565b90506000611aba878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612b07915050565b90508781600081518110611aca57fe5b60200260200101511115611af05760405162461bcd60e51b815260040161059c90614a3b565b611b3d33611b1889896000818110611b0457fe5b905060200201358a8a6001818110610ac557fe5b83600081518110611b2557fe5b6020026020010151610ea98b8b600081811061091c57fe5b611b7c81888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b611b88838387876134a4565b5050505050505050565b60608142811015611bdd576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016611c1a8787600019810181811061091c57fe5b6001600160a01b031614611c6d576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b611cab8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612b07915050565b91508682600081518110611cbb57fe5b60200260200101511115611d08576040805162461bcd60e51b815260206004820152600f60248201526e272937baba32b91d1026a0ac2fa4a760891b604482015290519081900360640190fd5b611d1c3361117388886000818110610e7057fe5b611d5b82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d83600185510381518110611d9a57fe5b60200260200101516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015611dd857600080fd5b505af1158015611dec573d6000803e3d6000fd5b505050506111bf8483600185510381518110611e0457fe5b60200260200101516128c4565b60608142811015611e5c576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016611e998787600019810181811061091c57fe5b6001600160a01b031614611eec576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b611f2a8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612e72915050565b91508682600184510381518110611f3d57fe5b60200260200101511015611d08576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e8813525397d3d55560821b604482015290519081900360640190fd5b60608142811015611fd6576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6120148686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612b07915050565b9150868260008151811061202457fe5b6020026020010151111561115f576040805162461bcd60e51b815260206004820152600f60248201526e272937baba32b91d1026a0ac2fa4a760891b604482015290519081900360640190fd5b600061207f8a333087612fa8565b60008a6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b1580156120ba57600080fd5b505afa1580156120ce573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120f691908101906146cd565b8051909150861480156121095750805188145b6121255760405162461bcd60e51b815260040161059c90614a64565b60405163b02f0b7360e01b81526001600160a01b038c169063b02f0b73906121559088908d908d90600401614b2b565b600060405180830381600087803b15801561216f57600080fd5b505af1158015612183573d6000803e3d6000fd5b5050604080516003808252608082019092526000935091506020820160608036833701905050905060005b82518110156122025760006121eb8483815181106121c857fe5b60200260200101518b8b858181106121dc57fe5b905060200201358f868a61353d565b90506121f7858261373f565b9450506001016121ae565b50848310156122235760405162461bcd60e51b815260040161059c90614ac1565b50509998505050505050505050565b60006122496001600160a01b038716333088612fa8565b60006122558585612b07565b9050866001600160a01b03166302c967486122838760008151811061227657fe5b6020026020010151612b01565b8360008151811061229057fe5b6020026020010151896040518463ffffffff1660e01b81526004016122b7939291906149a1565b602060405180830381600087803b1580156122d157600080fd5b505af11580156122e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612309919061487a565b915061236a61231e8660008151811061227657fe5b6123508760008151811061232e57fe5b60200260200101518860018151811061234357fe5b6020026020010151612c52565b8360008151811061235d57fe5b602002602001015161275b565b612375818685612cac565b610b9a33612383888561296f565b6001600160a01b038a16919061275b565b6000816123e8576040805162461bcd60e51b815260206004820152600c60248201527f4552525f4449565f5a45524f0000000000000000000000000000000000000000604482015290519081900360640190fd5b670de0b6b3a764000083028315806124105750670de0b6b3a764000084828161240d57fe5b04145b612454576040805162461bcd60e51b815260206004820152601060248201526f11549497d1125597d25395115493905360821b604482015290519081900360640190fd5b600283048101818110156124a2576040805162461bcd60e51b815260206004820152601060248201526f11549497d1125597d25395115493905360821b604482015290519081900360640190fd5b60008482816124ad57fe5b0493505050505b92915050565b60089190911b1790565b60008060006124d98660008151811061227657fe5b90506000896001600160a01b0316634aa4e0b5896040518263ffffffff1660e01b8152600401612509919061498d565b60206040518083038186803b15801561252157600080fd5b505afa158015612535573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612559919061487a565b90506125658682613797565b9350876001600160a01b0316826001600160a01b031614156125bb5760408051808201909152600f81526e272937baba32b91d1026a0ac2fa4a760891b60208201526125b49086908690613859565b925061273a565b74ffffffffffffffffffffffffffffffffffffffffff89169860a81c6125ea6001600160a01b038416826124ba565b886000815181106125f757fe5b60209081029190910101528961263d576002885261261f6001600160a01b038a1660006124ba565b8860018151811061262c57fe5b602002602001018181525050612676565b60038852604088018a905261265c6001600160a01b038a1660006124ba565b8860028151811061266957fe5b6020026020010181815250505b60006126828987612b07565b90506126d48160008151811061269457fe5b60200260200101516040518060400160405280600f81526020016e272937baba32b91d1026a0ac2fa4a760891b815250896138599092919063ffffffff16565b945061272c6126fe8a6000815181106126e957fe5b60200260200101518b60018151811061234357fe5b8260008151811061270b57fe5b6020026020010151866001600160a01b031661275b9092919063ffffffff16565b612737818a30612cac565b50505b61274e6001600160a01b0389168b866138f1565b5050965096945050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106127d75780518252601f1990920191602091820191016127b8565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612839576040519150601f19603f3d011682016040523d82523d6000602084013e61283e565b606091505b509150915081801561286c57508051158061286c575080806020019051602081101561286957600080fd5b50515b6128bd576040805162461bcd60e51b815260206004820152600560248201527f54483a5354000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b5050505050565b6040516000906001600160a01b0384169083908381818185875af1925050503d806000811461290f576040519150601f19603f3d011682016040523d82523d6000602084013e612914565b606091505b505090508061296a576040805162461bcd60e51b815260206004820152600660248201527f54483a5354450000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b505050565b808203828111156124b4576040805162461bcd60e51b815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b60006129d586333088612fa8565b6000866001600160a01b03166346ab38f16129f68760008151811061227657fe5b8860006040518463ffffffff1660e01b8152600401612a17939291906149a1565b602060405180830381600087803b158015612a3157600080fd5b505af1158015612a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a69919061487a565b90506000612a778683612e72565b905080600182510381518110612a8957fe5b6020026020010151925084831015612ab35760405162461bcd60e51b815260040161059c90614ac1565b612aeb612ac68760008151811061227657fe5b61235088600081518110612ad657fe5b60200260200101518960018151811061234357fe5b612af6818786612cac565b505095945050505050565b60081c90565b6060600283511015612b60576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f504154480000604482015290519081900360640190fd5b825167ffffffffffffffff81118015612b7857600080fd5b50604051908082528060200260200182016040528015612ba2578160200160208202803683370190505b5090508181600183510381518110612bb657fe5b60209081029190910101528251600019015b8015612c4b57600080612c04866001850381518110612be357fe5b6020026020010151878581518110612bf757fe5b6020026020010151613a53565b91509150612c26848481518110612c1757fe5b60200260200101518383613b03565b846001850381518110612c3557fe5b6020908102919091010152505060001901612bc8565b5092915050565b6000806000612c6085613bd1565b915091506000612c6f85612b01565b9050600080612c7e8584613bdf565b9150915083612c9657612c918282613cbd565b612ca0565b612ca08282613d9a565b98975050505050505050565b60005b6001835103811015611a4e57600080848381518110612cca57fe5b6020026020010151858460010181518110612ce157fe5b6020026020010151915091506000868460010181518110612cfe57fe5b60200260200101519050600080838510612d1a57826000612d1e565b6000835b91509150600060028951038710612d355787612d48565b612d48858a896002018151811061234357fe5b9050612d548686612c52565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015612d91576020820181803683370190505b506040518563ffffffff1660e01b815260040180858152602001848152602001836001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b83811015612df9578181015183820152602001612de1565b50505050905090810190601f168015612e265780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b158015612e4857600080fd5b505af1158015612e5c573d6000803e3d6000fd5b505060019098019750612caf9650505050505050565b6060600283511015612ecb576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f504154480000604482015290519081900360640190fd5b825167ffffffffffffffff81118015612ee357600080fd5b50604051908082528060200260200182016040528015612f0d578160200160208202803683370190505b5090508181600081518110612f1e57fe5b60200260200101818152505060005b6001845103811015612c4b57600080612f62868481518110612f4b57fe5b6020026020010151878560010181518110612bf757fe5b91509150612f84848481518110612f7557fe5b60200260200101518383613e77565b848460010181518110612f9357fe5b60209081029190910101525050600101612f2d565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b178152925182516000948594938a169392918291908083835b6020831061302c5780518252601f19909201916020918201910161300d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461308e576040519150601f19603f3d011682016040523d82523d6000602084013e613093565b606091505b50915091508180156130c15750805115806130c157508080602001905160208110156130be57600080fd5b50515b613112576040805162461bcd60e51b815260206004820152600660248201527f54483a5354460000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b505050505050565b60006131278584866138f1565b604051635db3427760e01b81526001600160a01b03841690635db3427790613157908890889087906004016149a1565b602060405180830381600087803b15801561317157600080fd5b505af1158015613185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a9919061487a565b905061084b83338361275b565b600080846001600160a01b03166364c7d661856040518263ffffffff1660e01b81526004016131e5919061498d565b60e06040518083038186803b1580156131fd57600080fd5b505afa158015613211573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323591906147d5565b90508060200151613321576040516391bfa2bf60e01b81526000906001600160a01b038716906391bfa2bf9061326f90889060040161498d565b60206040518083038186803b15801561328757600080fd5b505afa15801561329b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bf919061487a565b905060006132da6132d4838560c00151613f4f565b83612394565b905060006132ef6658d15e1762800083613797565b60c08501849052905061330a6703782dace9d9000082613fb5565b6bffffffffffffffffffffffff1660608501525050505b6000856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561335c57600080fd5b505afa158015613370573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613394919061487a565b90506000866001600160a01b031663936c34776040518163ffffffff1660e01b815260040160206040518083038186803b1580156133d157600080fd5b505afa1580156133e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613409919061487a565b90506000876001600160a01b031663d4cadf686040518163ffffffff1660e01b815260040160206040518083038186803b15801561344657600080fd5b505afa15801561345a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061347e919061487a565b9050612ca08460c0015185606001516bffffffffffffffffffffffff1685858a86614016565b6134af8483856138f1565b60405163036836fd60e51b81526001600160a01b03831690636d06dfa0906134df908790859088906004016149a1565b602060405180830381600087803b1580156134f957600080fd5b505af115801561350d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613531919061487a565b50611a4e82338361275b565b600080866001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161356c919061498d565b60206040518083038186803b15801561358457600080fd5b505afa158015613598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135bc919061487a565b9050846001600160a01b0316876001600160a01b03161415613604579050806001600160a01b03831630146135ff576135ff6001600160a01b038816848361275b565b610b9a565b74ffffffffffffffffffffffffffffffffffffffffff86169560a81c6136336001600160a01b038916826124ba565b8560008151811061364057fe5b60209081029190910101528661368657600285526136686001600160a01b03871660006124ba565b8560018151811061367557fe5b6020026020010181815250506136bf565b60038552604085018790526136a56001600160a01b03871660006124ba565b856002815181106136b257fe5b6020026020010181815250505b60006136cb8684612e72565b905061370e6136e08760008151811061232e57fe5b826000815181106136ed57fe5b60200260200101518b6001600160a01b031661275b9092919063ffffffff16565b613719818787612cac565b8060018251038151811061372957fe5b6020026020010151935050505095945050505050565b808201828110156124b4576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b60008282028315806137b15750828482816137ae57fe5b04145b6137f5576040805162461bcd60e51b815260206004820152601060248201526f4552525f4d554c5f4f564552464c4f5760801b604482015290519081900360640190fd5b6706f05b59d3b20000810181811015613848576040805162461bcd60e51b815260206004820152601060248201526f4552525f4d554c5f4f564552464c4f5760801b604482015290519081900360640190fd5b6000670de0b6b3a7640000826124ad565b81830381848211156138e95760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156138ae578181015183820152602001613896565b50505050905090810190601f1680156138db5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b509392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b1781529251825160009485949389169392918291908083835b6020831061396d5780518252601f19909201916020918201910161394e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146139cf576040519150601f19603f3d011682016040523d82523d6000602084013e6139d4565b606091505b5091509150818015613a02575080511580613a0257508080602001905160208110156139ff57600080fd5b50515b6128bd576040805162461bcd60e51b815260206004820152600560248201527f54483a5341000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600080600080613a638686612c52565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613a9b57600080fd5b505afa158015613aaf573d6000803e3d6000fd5b505050506040513d6060811015613ac557600080fd5b5080516020909101516dffffffffffffffffffffffffffff9182169350169050848610613af3578082613af6565b81815b9097909650945050505050565b6000808411613b435760405162461bcd60e51b815260040180806020018281038252602c815260200180614bdb602c913960400191505060405180910390fd5b600083118015613b535750600082115b613b8e5760405162461bcd60e51b8152600401808060200182810382526028815260200180614c2c6028913960400191505060405180910390fd5b6000613ba66103e8613ba086886140c9565b906140c9565b90506000613bba6103e5613ba0868961296f565b905061047e6001828481613bca57fe5b049061373f565b600881901c9160ff90911690565b600080826001600160a01b0316846001600160a01b03161415613c335760405162461bcd60e51b8152600401808060200182810382526025815260200180614c076025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b031610613c53578284613c56565b83835b90925090506001600160a01b038216613cb6576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401527f000000000000000000000000000000000000000000000000000000000000000090951b166069820152607d8101939093527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f609d808501919091528151808503909101815260bd9093019052815191012090565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401527f000000000000000000000000000000000000000000000000000000000000000090951b166069820152607d8101939093527fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303609d808501919091528151808503909101815260bd9093019052815191012090565b6000808411613eb75760405162461bcd60e51b815260040180806020018281038252602b815260200180614c54602b913960400191505060405180910390fd5b600083118015613ec75750600082115b613f025760405162461bcd60e51b8152600401808060200182810382526028815260200180614c2c6028913960400191505060405180910390fd5b6000613f10856103e56140c9565b90506000613f1e82856140c9565b90506000613f3883613f32886103e86140c9565b9061373f565b9050808281613f4357fe5b04979650505050505050565b6000806000613f5e8585614135565b9150915080156138e9576040805162461bcd60e51b815260206004820152601160248201527f4552525f5355425f554e444552464c4f57000000000000000000000000000000604482015290519081900360640190fd5b60008282018381101561400f576040805162461bcd60e51b815260206004820152601060248201527f4552525f4144445f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b9392505050565b6000806140238786612394565b905060006140318786613fb5565b9050600061403f8289612394565b90506000614055670de0b6b3a764000085612394565b905060006140638383614157565b90506000614071828e613797565b9050600061407f828f613f4f565b9050600061409e614098670de0b6b3a76400008a613f4f565b8b613797565b90506140b682610663670de0b6b3a764000084613f4f565b9f9e505050505050505050505050505050565b60008115806140e4575050808202828282816140e157fe5b04145b6124b4576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b60008082841061414b5750508082036000613cb6565b50508181036001613cb6565b600060018310156141af576040805162461bcd60e51b815260206004820152601560248201527f4552525f42504f575f424153455f544f4f5f4c4f570000000000000000000000604482015290519081900360640190fd5b671bc16d674ec7ffff83111561420c576040805162461bcd60e51b815260206004820152601660248201527f4552525f42504f575f424153455f544f4f5f4849474800000000000000000000604482015290519081900360640190fd5b600061421783614269565b905060006142258483613f4f565b9050600061423b8661423685614286565b614294565b90508161424c5792506124b4915050565b600061425d87846305f5e1006142eb565b905061161c8282613797565b6000670de0b6b3a764000061427d83614286565b0290505b919050565b670de0b6b3a7640000900490565b600080600283066142ad57670de0b6b3a76400006142af565b835b90506002830492505b821561400f576142c88485613797565b935060028306156142e0576142dd8185613797565b90505b6002830492506142b8565b600082818061430287670de0b6b3a7640000614135565b9092509050670de0b6b3a764000080600060015b8884106143bf576000670de0b6b3a76400008202905060008061434a8a61434585670de0b6b3a7640000613f4f565b614135565b915091506143618761435c848c613797565b613797565b965061436d8784612394565b96508661437c575050506143bf565b8715614386579315935b8015614390579315935b84156143a7576143a08688613f4f565b95506143b4565b6143b18688613fb5565b95505b505050600101614316565b50909998505050505050505050565b60008083601f8401126143df578182fd5b50813567ffffffffffffffff8111156143f6578182fd5b6020830191508360208083028501011115613cb657600080fd5b8051801515811461428157600080fd5b805164ffffffffff8116811461428157600080fd5b805160ff8116811461428157600080fd5b80516bffffffffffffffffffffffff8116811461428157600080fd5b60008060008060608587031215614477578384fd5b843561448281614bc2565b9350602085013567ffffffffffffffff81111561449d578384fd5b6144a9878288016143ce565b9598909750949560400135949350505050565b60008060008060008060a087890312156144d4578182fd5b86356144df81614bc2565b9550602087013567ffffffffffffffff8111156144fa578283fd5b61450689828a016143ce565b90965094505060408701359250606087013561452181614bc2565b80925050608087013590509295509295509295565b60008060008060008060008060c0898b031215614551578182fd5b883561455c81614bc2565b9750602089013567ffffffffffffffff80821115614578578384fd5b6145848c838d016143ce565b909950975060408b013591508082111561459c578384fd5b506145a98b828c016143ce565b9096509450506060890135925060808901356145c481614bc2565b8092505060a089013590509295985092959890939650565b600080600080600080600060a0888a0312156145f6578283fd5b873561460181614bc2565b9650602088013567ffffffffffffffff8082111561461d578485fd5b6146298b838c016143ce565b909850965060408a0135915080821115614641578485fd5b5061464e8a828b016143ce565b989b979a5095989597966060870135966080013595509350505050565b600080600080600060808688031215614682578081fd5b853561468d81614bc2565b945060208601359350604086013567ffffffffffffffff8111156146af578182fd5b6146bb888289016143ce565b96999598509660600135949350505050565b600060208083850312156146df578182fd5b825167ffffffffffffffff808211156146f6578384fd5b818501915085601f830112614709578384fd5b81518181111561471557fe5b8381029150614725848301614b9e565b8181528481019084860184860187018a101561473f578788fd5b8795505b8386101561476d578051945061475885614bc2565b84835260019590950194918601918601614743565b5098975050505050505050565b6000806000806060858703121561478f578182fd5b843567ffffffffffffffff8111156147a5578283fd5b6147b1878288016143ce565b90955093505060208501356147c581614bc2565b9396929550929360400135925050565b600060e082840312156147e6578081fd5b60405160e0810181811067ffffffffffffffff8211171561480357fe5b60405261480f83614410565b815261481d60208401614410565b602082015261482e60408401614420565b604082015261483f60608401614446565b606082015261485060808401614446565b608082015261486160a08401614435565b60a082015260c083015160c08201528091505092915050565b60006020828403121561488b578081fd5b5051919050565b6000806000806000608086880312156148a9578283fd5b85359450602086013567ffffffffffffffff8111156148c6578384fd5b6148d2888289016143ce565b90955093505060408601356148e681614bc2565b949793965091946060013592915050565b60008060008060008060a0878903121561490f578384fd5b8635955060208701359450604087013567ffffffffffffffff811115614933578485fd5b61493f89828a016143ce565b909550935050606087013561452181614bc2565b6000815180845260208085019450808401835b8381101561498257815187529582019590820190600101614966565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039390931683526020830191909152604082015260600190565b60006020825261400f6020830184614953565b6020808252600c908201527f494e56414c49445f504154480000000000000000000000000000000000000000604082015260600190565b60208082526015908201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604082015260600190565b6020808252600f908201526e272937baba32b91d1026a0ac2fa4a760891b604082015260600190565b6020808252602c908201527f496e6465786564556e6973776170526f757465724275726e65723a204241445f60408201527f41525241595f4c454e4754480000000000000000000000000000000000000000606082015260800190565b60208082526010908201526f13949bdd5d195c8e8813525397d3d55560821b604082015260600190565b60208082526010908201527f4e526f757465723a204152525f4c454e00000000000000000000000000000000604082015260600190565b90815260200190565b6000848252604060208301528260408301527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115614b69578081fd5b6020830280856060850137919091016060019081529392505050565b60008382526040602083015261084b6040830184614953565b60405181810167ffffffffffffffff81118282101715614bba57fe5b604052919050565b6001600160a01b0381168114614bd757600080fd5b5056fe556e697377617056324c6962726172793a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553556e697377617056324c6962726172793a20494e53554646494349454e545f4c4951554944495459556e697377617056324c6962726172793a20494e53554646494349454e545f494e5055545f414d4f554e54a164736f6c6343000706000a0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x60806040526004361061016e5760003560e01c8063501070be116100cb57806390ab67311161007f578063b96e9f3311610059578063b96e9f33146103ba578063beb586ae146103da578063e9040a97146103fa576101a7565b806390ab673114610367578063a86952b414610387578063b079cb8c1461039a576101a7565b806367e34f97116100b057806367e34f971461031f5780638bdb2afa146103325780638f32871e14610347576101a7565b8063501070be146102ea57806365526631146102ff576101a7565b80633ddb70281161012257806343405d511161010757806343405d511461028a5780634e4665e9146102aa5780634f553b26146102ca576101a7565b80633ddb7028146102555780633fc8cef314610268576101a7565b8063108a6d6b11610153578063108a6d6b146102025780632064bcbd146102155780633c3db8c114610235576101a7565b80630c05f882146101ac5780630c288791146101e2576101a7565b366101a757336001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216146101a557fe5b005b600080fd5b3480156101b857600080fd5b506101cc6101c7366004614536565b61041a565b6040516101d99190614b22565b60405180910390f35b3480156101ee57600080fd5b506101cc6101fd36600461466b565b61043a565b6101cc610210366004614462565b610488565b34801561022157600080fd5b506101cc61023036600461466b565b610853565b610248610243366004614892565b610897565b6040516101d991906149c2565b610248610263366004614892565b610ba4565b34801561027457600080fd5b5061027d610df5565b6040516101d9919061498d565b34801561029657600080fd5b506101cc6102a5366004614892565b610e19565b3480156102b657600080fd5b506101cc6102c536600461466b565b610f43565b3480156102d657600080fd5b506102486102e53660046148f7565b611075565b3480156102f657600080fd5b5061027d6111ca565b34801561030b57600080fd5b506101cc61031a3660046144bc565b6111ee565b6101cc61032d36600461477a565b61145d565b34801561033e57600080fd5b5061027d611627565b34801561035357600080fd5b506101cc6103623660046145dc565b61164b565b34801561037357600080fd5b506101cc61038236600461466b565b611709565b6101a561039536600461477a565b61183b565b3480156103a657600080fd5b506101a56103b5366004614892565b611a54565b3480156103c657600080fd5b506102486103d53660046148f7565b611b92565b3480156103e657600080fd5b506102486103f53660046148f7565b611e11565b34801561040657600080fd5b506102486104153660046148f7565b611f8b565b600061042d89848a8a8a8a8a8933612071565b9998505050505050505050565b600061047e86868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503391506122329050565b9695505050505050565b6000803490507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b1580156104e957600080fd5b505af11580156104fd573d6000803e3d6000fd5b50505050506000866001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561053d57600080fd5b505afa158015610551573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261057991908101906146cd565b805190915085146105a55760405162461bcd60e51b815260040161059c90614aeb565b60405180910390fd5b6000815167ffffffffffffffff811180156105bf57600080fd5b506040519080825280602002602001820160405280156105e9578160200160208202803683370190505b5090506000610668868a6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561062b57600080fd5b505afa15801561063f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610663919061487a565b612394565b6040805160038082526080820190925291925060009190602082016060803683370190505090506106c36001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21660006124ba565b816000815181106106d057fe5b60200260200101818152505060005b845181101561073f5761071b8b8b8b848181106106f857fe5b9050602002013587848151811061070b57fe5b602002602001015185878b6124c4565b85838151811061072757fe5b602090810291909101019190915295506001016106df565b506040516313da703560e21b81526001600160a01b038b1690634f69c0d49061076e908a908790600401614b85565b600060405180830381600087803b15801561078857600080fd5b505af115801561079c573d6000803e3d6000fd5b505050506107ab8a338961275b565b841561083957604051632e1a7d4d60e01b81526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d906107fd908890600401614b22565b600060405180830381600087803b15801561081757600080fd5b505af115801561082b573d6000803e3d6000fd5b5050505061083933866128c4565b610843348661296f565b955050505050505b949350505050565b600061047e86868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503391506129c79050565b606081428110156108e2576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03166109288787600081811061091c57fe5b90506020020135612b01565b6001600160a01b03161461097b576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b6109b98686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508b9250612b07915050565b915034826000815181106109c957fe5b60200260200101511115610a16576040805162461bcd60e51b815260206004820152600f60248201526e272937baba32b91d1026a0ac2fa4a760891b604482015290519081900360640190fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db083600081518110610a5257fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015610a8557600080fd5b505af1158015610a99573d6000803e3d6000fd5b5050505050610b1f610ad187876000818110610ab157fe5b9050602002013588886001818110610ac557fe5b90506020020135612c52565b83600081518110610ade57fe5b60200260200101517f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031661275b9092919063ffffffff16565b610b5e82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612cac915050565b81600081518110610b6b57fe5b6020026020010151341115610b9a57610b9a3383600081518110610b8b57fe5b602002602001015134036128c4565b5095945050505050565b60608142811015610bef576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316610c298787600081811061091c57fe5b6001600160a01b031614610c7c576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b610cba868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250349250612e72915050565b91508682600184510381518110610ccd57fe5b60200260200101511015610d1b576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e8813525397d3d55560821b604482015290519081900360640190fd5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db083600081518110610d5757fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b158015610d8a57600080fd5b505af1158015610d9e573d6000803e3d6000fd5b5050505050610db6610ad187876000818110610ab157fe5b610b9a82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612cac915050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b600080610e5a8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508b9250612e72915050565b9050610eba33610e8488886000818110610e7057fe5b9050602002013589896001818110610ac557fe5b83600081518110610e9157fe5b6020026020010151610ea98a8a600081811061091c57fe5b6001600160a01b0316929190612fa8565b610ef981878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b600081600183510381518110610f0b57fe5b60200260200101519050610f35610f2d888860018b8b90500381811061091c57fe5b82878761311a565b925050505b95945050505050565b60006001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216610f828585600019810181811061091c57fe5b6001600160a01b031614610fa85760405162461bcd60e51b815260040161059c90614a0c565b610fea86868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503091506122329050565b604051632e1a7d4d60e01b81529091506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90611039908590600401614b22565b600060405180830381600087803b15801561105357600080fd5b505af1158015611067573d6000803e3d6000fd5b50505050610f3a33836128c4565b606081428110156110c0576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6110fe8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612e72915050565b9150868260018451038151811061111157fe5b6020026020010151101561115f576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e8813525397d3d55560821b604482015290519081900360640190fd5b6111803361117388886000818110610e7057fe5b84600081518110610e9157fe5b6111bf82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612cac915050565b509695505050505050565b7f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac81565b6000808290506000886001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561122f57600080fd5b505afa158015611243573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261126b91908101906146cd565b8051909150871461128e5760405162461bcd60e51b815260040161059c90614aeb565b6112a36001600160a01b038616333087612fa8565b6000815167ffffffffffffffff811180156112bd57600080fd5b506040519080825280602002602001820160405280156112e7578160200160208202803683370190505b5090506000611329888c6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561062b57600080fd5b6040805160038082526080820190925291925060009190602082016060803683370190505090506113646001600160a01b03891660006124ba565b8160008151811061137157fe5b60200260200101818152505060005b84518110156113bd576113998d8d8d848181106106f857fe5b8583815181106113a557fe5b60209081029190910101919091529550600101611380565b506040516313da703560e21b81526001600160a01b038d1690634f69c0d4906113ec908c908790600401614b85565b600060405180830381600087803b15801561140657600080fd5b505af115801561141a573d6000803e3d6000fd5b505050506114298c338b61275b565b8415611443576114436001600160a01b038916338761275b565b61144d878661296f565b9c9b505050505050505050505050565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b03166114998686600081811061091c57fe5b6001600160a01b0316146114bf5760405162461bcd60e51b815260040161059c90614a0c565b60006114ff868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250349250612e72915050565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db08260008151811061153d57fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b15801561157057600080fd5b505af1158015611584573d6000803e3d6000fd5b50505050506115a961159c87876000818110610ab157fe5b82600081518110610ade57fe5b6115e881878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b6000816001835103815181106115fa57fe5b6020026020010151905061161c610f2d888860018b8b90500381811061091c57fe5b979650505050505050565b7f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f81565b600061167e887f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc289898989898930612071565b604051632e1a7d4d60e01b81529091506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d906116cd908490600401614b22565b600060405180830381600087803b1580156116e757600080fd5b505af11580156116fb573d6000803e3d6000fd5b5050505061161c33826128c4565b60006001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2166117488585600019810181811061091c57fe5b6001600160a01b03161461176e5760405162461bcd60e51b815260040161059c90614a0c565b6117b086868686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892503091506129c79050565b604051632e1a7d4d60e01b81529091506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d906117ff908490600401614b22565b600060405180830381600087803b15801561181957600080fd5b505af115801561182d573d6000803e3d6000fd5b50505050610f3a33826128c4565b60006118508585600019810181811061091c57fe5b9050600061185f8483856131b6565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031661189b8787600081811061091c57fe5b6001600160a01b0316146118c15760405162461bcd60e51b815260040161059c906149d5565b6000611901878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612b07915050565b9050348160008151811061191157fe5b602002602001015111156119375760405162461bcd60e51b815260040161059c90614a3b565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db08260008151811061197357fe5b60200260200101516040518263ffffffff1660e01b81526004016000604051808303818588803b1580156119a657600080fd5b505af11580156119ba573d6000803e3d6000fd5b50505050506119d261159c88886000818110610e7057fe5b611a1181888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b80600081518110611a1e57fe5b6020026020010151341115611a3e57611a3e3382600081518110610b8b57fe5b611a4a838387876134a4565b5050505b50505050565b6000611a698585600019810181811061091c57fe5b90506000611a788483856131b6565b90506000611aba878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612b07915050565b90508781600081518110611aca57fe5b60200260200101511115611af05760405162461bcd60e51b815260040161059c90614a3b565b611b3d33611b1889896000818110611b0457fe5b905060200201358a8a6001818110610ac557fe5b83600081518110611b2557fe5b6020026020010151610ea98b8b600081811061091c57fe5b611b7c81888880806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b611b88838387876134a4565b5050505050505050565b60608142811015611bdd576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216611c1a8787600019810181811061091c57fe5b6001600160a01b031614611c6d576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b611cab8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612b07915050565b91508682600081518110611cbb57fe5b60200260200101511115611d08576040805162461bcd60e51b815260206004820152600f60248201526e272937baba32b91d1026a0ac2fa4a760891b604482015290519081900360640190fd5b611d1c3361117388886000818110610e7057fe5b611d5b82878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250309250612cac915050565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316632e1a7d4d83600185510381518110611d9a57fe5b60200260200101516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015611dd857600080fd5b505af1158015611dec573d6000803e3d6000fd5b505050506111bf8483600185510381518110611e0457fe5b60200260200101516128c4565b60608142811015611e5c576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216611e998787600019810181811061091c57fe5b6001600160a01b031614611eec576040805162461bcd60e51b815260206004820152601560248201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604482015290519081900360640190fd5b611f2a8686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612e72915050565b91508682600184510381518110611f3d57fe5b60200260200101511015611d08576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e8813525397d3d55560821b604482015290519081900360640190fd5b60608142811015611fd6576040805162461bcd60e51b815260206004820152601060248201526f13949bdd5d195c8e881156141254915160821b604482015290519081900360640190fd5b6120148686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508c9250612b07915050565b9150868260008151811061202457fe5b6020026020010151111561115f576040805162461bcd60e51b815260206004820152600f60248201526e272937baba32b91d1026a0ac2fa4a760891b604482015290519081900360640190fd5b600061207f8a333087612fa8565b60008a6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b1580156120ba57600080fd5b505afa1580156120ce573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120f691908101906146cd565b8051909150861480156121095750805188145b6121255760405162461bcd60e51b815260040161059c90614a64565b60405163b02f0b7360e01b81526001600160a01b038c169063b02f0b73906121559088908d908d90600401614b2b565b600060405180830381600087803b15801561216f57600080fd5b505af1158015612183573d6000803e3d6000fd5b5050604080516003808252608082019092526000935091506020820160608036833701905050905060005b82518110156122025760006121eb8483815181106121c857fe5b60200260200101518b8b858181106121dc57fe5b905060200201358f868a61353d565b90506121f7858261373f565b9450506001016121ae565b50848310156122235760405162461bcd60e51b815260040161059c90614ac1565b50509998505050505050505050565b60006122496001600160a01b038716333088612fa8565b60006122558585612b07565b9050866001600160a01b03166302c967486122838760008151811061227657fe5b6020026020010151612b01565b8360008151811061229057fe5b6020026020010151896040518463ffffffff1660e01b81526004016122b7939291906149a1565b602060405180830381600087803b1580156122d157600080fd5b505af11580156122e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612309919061487a565b915061236a61231e8660008151811061227657fe5b6123508760008151811061232e57fe5b60200260200101518860018151811061234357fe5b6020026020010151612c52565b8360008151811061235d57fe5b602002602001015161275b565b612375818685612cac565b610b9a33612383888561296f565b6001600160a01b038a16919061275b565b6000816123e8576040805162461bcd60e51b815260206004820152600c60248201527f4552525f4449565f5a45524f0000000000000000000000000000000000000000604482015290519081900360640190fd5b670de0b6b3a764000083028315806124105750670de0b6b3a764000084828161240d57fe5b04145b612454576040805162461bcd60e51b815260206004820152601060248201526f11549497d1125597d25395115493905360821b604482015290519081900360640190fd5b600283048101818110156124a2576040805162461bcd60e51b815260206004820152601060248201526f11549497d1125597d25395115493905360821b604482015290519081900360640190fd5b60008482816124ad57fe5b0493505050505b92915050565b60089190911b1790565b60008060006124d98660008151811061227657fe5b90506000896001600160a01b0316634aa4e0b5896040518263ffffffff1660e01b8152600401612509919061498d565b60206040518083038186803b15801561252157600080fd5b505afa158015612535573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612559919061487a565b90506125658682613797565b9350876001600160a01b0316826001600160a01b031614156125bb5760408051808201909152600f81526e272937baba32b91d1026a0ac2fa4a760891b60208201526125b49086908690613859565b925061273a565b74ffffffffffffffffffffffffffffffffffffffffff89169860a81c6125ea6001600160a01b038416826124ba565b886000815181106125f757fe5b60209081029190910101528961263d576002885261261f6001600160a01b038a1660006124ba565b8860018151811061262c57fe5b602002602001018181525050612676565b60038852604088018a905261265c6001600160a01b038a1660006124ba565b8860028151811061266957fe5b6020026020010181815250505b60006126828987612b07565b90506126d48160008151811061269457fe5b60200260200101516040518060400160405280600f81526020016e272937baba32b91d1026a0ac2fa4a760891b815250896138599092919063ffffffff16565b945061272c6126fe8a6000815181106126e957fe5b60200260200101518b60018151811061234357fe5b8260008151811061270b57fe5b6020026020010151866001600160a01b031661275b9092919063ffffffff16565b612737818a30612cac565b50505b61274e6001600160a01b0389168b866138f1565b5050965096945050505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1781529251825160009485949389169392918291908083835b602083106127d75780518252601f1990920191602091820191016127b8565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612839576040519150601f19603f3d011682016040523d82523d6000602084013e61283e565b606091505b509150915081801561286c57508051158061286c575080806020019051602081101561286957600080fd5b50515b6128bd576040805162461bcd60e51b815260206004820152600560248201527f54483a5354000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b5050505050565b6040516000906001600160a01b0384169083908381818185875af1925050503d806000811461290f576040519150601f19603f3d011682016040523d82523d6000602084013e612914565b606091505b505090508061296a576040805162461bcd60e51b815260206004820152600660248201527f54483a5354450000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b505050565b808203828111156124b4576040805162461bcd60e51b815260206004820152601560248201527f64732d6d6174682d7375622d756e646572666c6f770000000000000000000000604482015290519081900360640190fd5b60006129d586333088612fa8565b6000866001600160a01b03166346ab38f16129f68760008151811061227657fe5b8860006040518463ffffffff1660e01b8152600401612a17939291906149a1565b602060405180830381600087803b158015612a3157600080fd5b505af1158015612a45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a69919061487a565b90506000612a778683612e72565b905080600182510381518110612a8957fe5b6020026020010151925084831015612ab35760405162461bcd60e51b815260040161059c90614ac1565b612aeb612ac68760008151811061227657fe5b61235088600081518110612ad657fe5b60200260200101518960018151811061234357fe5b612af6818786612cac565b505095945050505050565b60081c90565b6060600283511015612b60576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f504154480000604482015290519081900360640190fd5b825167ffffffffffffffff81118015612b7857600080fd5b50604051908082528060200260200182016040528015612ba2578160200160208202803683370190505b5090508181600183510381518110612bb657fe5b60209081029190910101528251600019015b8015612c4b57600080612c04866001850381518110612be357fe5b6020026020010151878581518110612bf757fe5b6020026020010151613a53565b91509150612c26848481518110612c1757fe5b60200260200101518383613b03565b846001850381518110612c3557fe5b6020908102919091010152505060001901612bc8565b5092915050565b6000806000612c6085613bd1565b915091506000612c6f85612b01565b9050600080612c7e8584613bdf565b9150915083612c9657612c918282613cbd565b612ca0565b612ca08282613d9a565b98975050505050505050565b60005b6001835103811015611a4e57600080848381518110612cca57fe5b6020026020010151858460010181518110612ce157fe5b6020026020010151915091506000868460010181518110612cfe57fe5b60200260200101519050600080838510612d1a57826000612d1e565b6000835b91509150600060028951038710612d355787612d48565b612d48858a896002018151811061234357fe5b9050612d548686612c52565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f191660200182016040528015612d91576020820181803683370190505b506040518563ffffffff1660e01b815260040180858152602001848152602001836001600160a01b0316815260200180602001828103825283818151815260200191508051906020019080838360005b83811015612df9578181015183820152602001612de1565b50505050905090810190601f168015612e265780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b158015612e4857600080fd5b505af1158015612e5c573d6000803e3d6000fd5b505060019098019750612caf9650505050505050565b6060600283511015612ecb576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a20494e56414c49445f504154480000604482015290519081900360640190fd5b825167ffffffffffffffff81118015612ee357600080fd5b50604051908082528060200260200182016040528015612f0d578160200160208202803683370190505b5090508181600081518110612f1e57fe5b60200260200101818152505060005b6001845103811015612c4b57600080612f62868481518110612f4b57fe5b6020026020010151878560010181518110612bf757fe5b91509150612f84848481518110612f7557fe5b60200260200101518383613e77565b848460010181518110612f9357fe5b60209081029190910101525050600101612f2d565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b178152925182516000948594938a169392918291908083835b6020831061302c5780518252601f19909201916020918201910161300d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461308e576040519150601f19603f3d011682016040523d82523d6000602084013e613093565b606091505b50915091508180156130c15750805115806130c157508080602001905160208110156130be57600080fd5b50515b613112576040805162461bcd60e51b815260206004820152600660248201527f54483a5354460000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b505050505050565b60006131278584866138f1565b604051635db3427760e01b81526001600160a01b03841690635db3427790613157908890889087906004016149a1565b602060405180830381600087803b15801561317157600080fd5b505af1158015613185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a9919061487a565b905061084b83338361275b565b600080846001600160a01b03166364c7d661856040518263ffffffff1660e01b81526004016131e5919061498d565b60e06040518083038186803b1580156131fd57600080fd5b505afa158015613211573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323591906147d5565b90508060200151613321576040516391bfa2bf60e01b81526000906001600160a01b038716906391bfa2bf9061326f90889060040161498d565b60206040518083038186803b15801561328757600080fd5b505afa15801561329b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132bf919061487a565b905060006132da6132d4838560c00151613f4f565b83612394565b905060006132ef6658d15e1762800083613797565b60c08501849052905061330a6703782dace9d9000082613fb5565b6bffffffffffffffffffffffff1660608501525050505b6000856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561335c57600080fd5b505afa158015613370573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613394919061487a565b90506000866001600160a01b031663936c34776040518163ffffffff1660e01b815260040160206040518083038186803b1580156133d157600080fd5b505afa1580156133e5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613409919061487a565b90506000876001600160a01b031663d4cadf686040518163ffffffff1660e01b815260040160206040518083038186803b15801561344657600080fd5b505afa15801561345a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061347e919061487a565b9050612ca08460c0015185606001516bffffffffffffffffffffffff1685858a86614016565b6134af8483856138f1565b60405163036836fd60e51b81526001600160a01b03831690636d06dfa0906134df908790859088906004016149a1565b602060405180830381600087803b1580156134f957600080fd5b505af115801561350d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613531919061487a565b50611a4e82338361275b565b600080866001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161356c919061498d565b60206040518083038186803b15801561358457600080fd5b505afa158015613598573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135bc919061487a565b9050846001600160a01b0316876001600160a01b03161415613604579050806001600160a01b03831630146135ff576135ff6001600160a01b038816848361275b565b610b9a565b74ffffffffffffffffffffffffffffffffffffffffff86169560a81c6136336001600160a01b038916826124ba565b8560008151811061364057fe5b60209081029190910101528661368657600285526136686001600160a01b03871660006124ba565b8560018151811061367557fe5b6020026020010181815250506136bf565b60038552604085018790526136a56001600160a01b03871660006124ba565b856002815181106136b257fe5b6020026020010181815250505b60006136cb8684612e72565b905061370e6136e08760008151811061232e57fe5b826000815181106136ed57fe5b60200260200101518b6001600160a01b031661275b9092919063ffffffff16565b613719818787612cac565b8060018251038151811061372957fe5b6020026020010151935050505095945050505050565b808201828110156124b4576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6164642d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b60008282028315806137b15750828482816137ae57fe5b04145b6137f5576040805162461bcd60e51b815260206004820152601060248201526f4552525f4d554c5f4f564552464c4f5760801b604482015290519081900360640190fd5b6706f05b59d3b20000810181811015613848576040805162461bcd60e51b815260206004820152601060248201526f4552525f4d554c5f4f564552464c4f5760801b604482015290519081900360640190fd5b6000670de0b6b3a7640000826124ad565b81830381848211156138e95760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156138ae578181015183820152602001613896565b50505050905090810190601f1680156138db5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b509392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b1781529251825160009485949389169392918291908083835b6020831061396d5780518252601f19909201916020918201910161394e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146139cf576040519150601f19603f3d011682016040523d82523d6000602084013e6139d4565b606091505b5091509150818015613a02575080511580613a0257508080602001905160208110156139ff57600080fd5b50515b6128bd576040805162461bcd60e51b815260206004820152600560248201527f54483a5341000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600080600080613a638686612c52565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015613a9b57600080fd5b505afa158015613aaf573d6000803e3d6000fd5b505050506040513d6060811015613ac557600080fd5b5080516020909101516dffffffffffffffffffffffffffff9182169350169050848610613af3578082613af6565b81815b9097909650945050505050565b6000808411613b435760405162461bcd60e51b815260040180806020018281038252602c815260200180614bdb602c913960400191505060405180910390fd5b600083118015613b535750600082115b613b8e5760405162461bcd60e51b8152600401808060200182810382526028815260200180614c2c6028913960400191505060405180910390fd5b6000613ba66103e8613ba086886140c9565b906140c9565b90506000613bba6103e5613ba0868961296f565b905061047e6001828481613bca57fe5b049061373f565b600881901c9160ff90911690565b600080826001600160a01b0316846001600160a01b03161415613c335760405162461bcd60e51b8152600401808060200182810382526025815260200180614c076025913960400191505060405180910390fd5b826001600160a01b0316846001600160a01b031610613c53578284613c56565b83835b90925090506001600160a01b038216613cb6576040805162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f414444524553530000604482015290519081900360640190fd5b9250929050565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401527f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f90951b166069820152607d8101939093527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f609d808501919091528151808503909101815260bd9093019052815191012090565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401527f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac90951b166069820152607d8101939093527fe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303609d808501919091528151808503909101815260bd9093019052815191012090565b6000808411613eb75760405162461bcd60e51b815260040180806020018281038252602b815260200180614c54602b913960400191505060405180910390fd5b600083118015613ec75750600082115b613f025760405162461bcd60e51b8152600401808060200182810382526028815260200180614c2c6028913960400191505060405180910390fd5b6000613f10856103e56140c9565b90506000613f1e82856140c9565b90506000613f3883613f32886103e86140c9565b9061373f565b9050808281613f4357fe5b04979650505050505050565b6000806000613f5e8585614135565b9150915080156138e9576040805162461bcd60e51b815260206004820152601160248201527f4552525f5355425f554e444552464c4f57000000000000000000000000000000604482015290519081900360640190fd5b60008282018381101561400f576040805162461bcd60e51b815260206004820152601060248201527f4552525f4144445f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b9392505050565b6000806140238786612394565b905060006140318786613fb5565b9050600061403f8289612394565b90506000614055670de0b6b3a764000085612394565b905060006140638383614157565b90506000614071828e613797565b9050600061407f828f613f4f565b9050600061409e614098670de0b6b3a76400008a613f4f565b8b613797565b90506140b682610663670de0b6b3a764000084613f4f565b9f9e505050505050505050505050505050565b60008115806140e4575050808202828282816140e157fe5b04145b6124b4576040805162461bcd60e51b815260206004820152601460248201527f64732d6d6174682d6d756c2d6f766572666c6f77000000000000000000000000604482015290519081900360640190fd5b60008082841061414b5750508082036000613cb6565b50508181036001613cb6565b600060018310156141af576040805162461bcd60e51b815260206004820152601560248201527f4552525f42504f575f424153455f544f4f5f4c4f570000000000000000000000604482015290519081900360640190fd5b671bc16d674ec7ffff83111561420c576040805162461bcd60e51b815260206004820152601660248201527f4552525f42504f575f424153455f544f4f5f4849474800000000000000000000604482015290519081900360640190fd5b600061421783614269565b905060006142258483613f4f565b9050600061423b8661423685614286565b614294565b90508161424c5792506124b4915050565b600061425d87846305f5e1006142eb565b905061161c8282613797565b6000670de0b6b3a764000061427d83614286565b0290505b919050565b670de0b6b3a7640000900490565b600080600283066142ad57670de0b6b3a76400006142af565b835b90506002830492505b821561400f576142c88485613797565b935060028306156142e0576142dd8185613797565b90505b6002830492506142b8565b600082818061430287670de0b6b3a7640000614135565b9092509050670de0b6b3a764000080600060015b8884106143bf576000670de0b6b3a76400008202905060008061434a8a61434585670de0b6b3a7640000613f4f565b614135565b915091506143618761435c848c613797565b613797565b965061436d8784612394565b96508661437c575050506143bf565b8715614386579315935b8015614390579315935b84156143a7576143a08688613f4f565b95506143b4565b6143b18688613fb5565b95505b505050600101614316565b50909998505050505050505050565b60008083601f8401126143df578182fd5b50813567ffffffffffffffff8111156143f6578182fd5b6020830191508360208083028501011115613cb657600080fd5b8051801515811461428157600080fd5b805164ffffffffff8116811461428157600080fd5b805160ff8116811461428157600080fd5b80516bffffffffffffffffffffffff8116811461428157600080fd5b60008060008060608587031215614477578384fd5b843561448281614bc2565b9350602085013567ffffffffffffffff81111561449d578384fd5b6144a9878288016143ce565b9598909750949560400135949350505050565b60008060008060008060a087890312156144d4578182fd5b86356144df81614bc2565b9550602087013567ffffffffffffffff8111156144fa578283fd5b61450689828a016143ce565b90965094505060408701359250606087013561452181614bc2565b80925050608087013590509295509295509295565b60008060008060008060008060c0898b031215614551578182fd5b883561455c81614bc2565b9750602089013567ffffffffffffffff80821115614578578384fd5b6145848c838d016143ce565b909950975060408b013591508082111561459c578384fd5b506145a98b828c016143ce565b9096509450506060890135925060808901356145c481614bc2565b8092505060a089013590509295985092959890939650565b600080600080600080600060a0888a0312156145f6578283fd5b873561460181614bc2565b9650602088013567ffffffffffffffff8082111561461d578485fd5b6146298b838c016143ce565b909850965060408a0135915080821115614641578485fd5b5061464e8a828b016143ce565b989b979a5095989597966060870135966080013595509350505050565b600080600080600060808688031215614682578081fd5b853561468d81614bc2565b945060208601359350604086013567ffffffffffffffff8111156146af578182fd5b6146bb888289016143ce565b96999598509660600135949350505050565b600060208083850312156146df578182fd5b825167ffffffffffffffff808211156146f6578384fd5b818501915085601f830112614709578384fd5b81518181111561471557fe5b8381029150614725848301614b9e565b8181528481019084860184860187018a101561473f578788fd5b8795505b8386101561476d578051945061475885614bc2565b84835260019590950194918601918601614743565b5098975050505050505050565b6000806000806060858703121561478f578182fd5b843567ffffffffffffffff8111156147a5578283fd5b6147b1878288016143ce565b90955093505060208501356147c581614bc2565b9396929550929360400135925050565b600060e082840312156147e6578081fd5b60405160e0810181811067ffffffffffffffff8211171561480357fe5b60405261480f83614410565b815261481d60208401614410565b602082015261482e60408401614420565b604082015261483f60608401614446565b606082015261485060808401614446565b608082015261486160a08401614435565b60a082015260c083015160c08201528091505092915050565b60006020828403121561488b578081fd5b5051919050565b6000806000806000608086880312156148a9578283fd5b85359450602086013567ffffffffffffffff8111156148c6578384fd5b6148d2888289016143ce565b90955093505060408601356148e681614bc2565b949793965091946060013592915050565b60008060008060008060a0878903121561490f578384fd5b8635955060208701359450604087013567ffffffffffffffff811115614933578485fd5b61493f89828a016143ce565b909550935050606087013561452181614bc2565b6000815180845260208085019450808401835b8381101561498257815187529582019590820190600101614966565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b039390931683526020830191909152604082015260600190565b60006020825261400f6020830184614953565b6020808252600c908201527f494e56414c49445f504154480000000000000000000000000000000000000000604082015260600190565b60208082526015908201527409ca4deeae8cae47440929cac82989288bea082a89605b1b604082015260600190565b6020808252600f908201526e272937baba32b91d1026a0ac2fa4a760891b604082015260600190565b6020808252602c908201527f496e6465786564556e6973776170526f757465724275726e65723a204241445f60408201527f41525241595f4c454e4754480000000000000000000000000000000000000000606082015260800190565b60208082526010908201526f13949bdd5d195c8e8813525397d3d55560821b604082015260600190565b60208082526010908201527f4e526f757465723a204152525f4c454e00000000000000000000000000000000604082015260600190565b90815260200190565b6000848252604060208301528260408301527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115614b69578081fd5b6020830280856060850137919091016060019081529392505050565b60008382526040602083015261084b6040830184614953565b60405181810167ffffffffffffffff81118282101715614bba57fe5b604052919050565b6001600160a01b0381168114614bd757600080fd5b5056fe556e697377617056324c6962726172793a20494e53554646494349454e545f4f55545055545f414d4f554e54556e697377617056324c6962726172793a204944454e544943414c5f414444524553534553556e697377617056324c6962726172793a20494e53554646494349454e545f4c4951554944495459556e697377617056324c6962726172793a20494e53554646494349454e545f494e5055545f414d4f554e54a164736f6c6343000706000a

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

0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : _uniswapFactory (address): 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
Arg [1] : _sushiswapFactory (address): 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f
Arg [1] : 000000000000000000000000c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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